Repository navigation
Expand file tree
/
Copy pathnext.config.mjs
More file actions
91 lines (83 loc) · 3.53 KB
/
Copy pathnext.config.mjs
File metadata and controls
91 lines (83 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import { createMDX } from "fumadocs-mdx/next";
import { createHash } from "node:crypto";
import { readdirSync, readFileSync, statSync } from "node:fs";
import path from "node:path";
import redirects from './redirects.mjs';
const withMDX = createMDX();
/**
* Files and directories whose contents determine the client bundle. Editing
* MDX under `content/` does not belong here: it changes individual pages, not
* the bundle those pages load.
*/
const BUILD_INPUTS = ["yarn.lock", "next.config.mjs", "source.config.ts"];
const BUILD_INPUT_DIRS = ["src"];
function hashDirectory(hash, dir) {
for (const entry of readdirSync(dir).sort()) {
const full = path.join(dir, entry);
if (statSync(full).isDirectory()) hashDirectory(hash, full);
else hash.update(entry).update(readFileSync(full));
}
}
/**
* Deterministic build id derived from the inputs above.
*
* Next defaults to a random id per build and embeds it in every exported HTML
* file and RSC payload. Two builds of identical source therefore shared almost
* no bytes — 2,638 of 3,172 files differed — so every deploy re-uploaded the
* whole site to Arweave instead of only what changed.
*
* Hard-coding a constant would also fix that, but the id is what Next uses to
* detect deployment skew: when an RSC response carries a different build id
* than the running bundle, the client forces a full navigation so the two
* cannot drift apart. A constant disables that permanently.
*
* Hashing the bundle inputs keeps both properties. A docs edit leaves the id
* (and every unrelated page) untouched, so the upload deduplicates; a change
* to `src/`, the config, or a dependency moves it and skew detection behaves
* exactly as upstream intends.
*
* Length matches Next's own ids (21 chars) to avoid surprising anything that
* assumes that shape.
*/
function bundleInputsHash() {
const hash = createHash("sha256");
for (const file of BUILD_INPUTS) {
try {
hash.update(readFileSync(file));
} catch {
// A missing optional input simply does not contribute to the hash.
}
}
for (const dir of BUILD_INPUT_DIRS) hashDirectory(hash, dir);
return hash.digest("base64url").slice(0, 21);
}
/** @type {import('next').NextConfig} */
const config = {
// Enable static export only for production builds
output: process.env.NODE_ENV === "production" ? "export" : "standalone",
trailingSlash: process.env.NODE_ENV === "production" ? true : false,
basePath: process.env.BASE_PATH || "",
reactStrictMode: true,
generateBuildId: async () => bundleInputsHash(),
experimental: {
// Turbopack's default production ids ('deterministic') are module-path
// hashes truncated to a small number. Two of our modules collide, and
// which one gets bumped varies between CI runs: the Shiki `tsv` loader
// flipped between 825913 and 841727 on consecutive deploys of the same
// bundle inputs. The new id renames its chunk, the chunks that reference
// it rename in turn, and every page that loads them changes -- about
// 2,000 files re-uploaded to Arweave for a one-line docs edit.
//
// Named ids are the module paths themselves, so nothing can collide.
// Cost: ~3% larger output, paid once, not per deploy.
turbopackModuleIds: "named",
},
eslint: {
// Warning: This allows production builds to successfully complete even if
// your project has ESLint errors.
ignoreDuringBuilds: true,
},
images: { unoptimized: true }, // required for static export + <Image>
redirects: () => redirects,
};
export default withMDX(config);