next-bun-compile 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +105 -37
- package/dist/compile.js +2 -2
- package/dist/generate.js +101 -33
- package/dist/index.js +103 -35
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -32,63 +32,129 @@ function toVarName(filePath) {
|
|
|
32
32
|
const safe = filePath.replace(/[^a-zA-Z0-9]/g, "_").slice(0, 40);
|
|
33
33
|
return `asset_${safe}_${hash}`;
|
|
34
34
|
}
|
|
35
|
+
function findPackageDirs(nodeModulesDir, pkg) {
|
|
36
|
+
const dirs = [];
|
|
37
|
+
const direct = join(nodeModulesDir, pkg);
|
|
38
|
+
if (existsSync(direct))
|
|
39
|
+
dirs.push(direct);
|
|
40
|
+
const bunDir = join(nodeModulesDir, ".bun");
|
|
41
|
+
if (existsSync(bunDir)) {
|
|
42
|
+
const scope = pkg.startsWith("@") ? pkg.split("/")[0] + "+" + pkg.split("/")[1] : pkg;
|
|
43
|
+
for (const entry of readdirSync(bunDir)) {
|
|
44
|
+
if (!entry.startsWith(scope + "@"))
|
|
45
|
+
continue;
|
|
46
|
+
const hoisted = join(bunDir, entry, "node_modules", pkg);
|
|
47
|
+
if (existsSync(hoisted))
|
|
48
|
+
dirs.push(hoisted);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return dirs;
|
|
52
|
+
}
|
|
35
53
|
function generateStubs(standaloneDir) {
|
|
36
54
|
const stubs = [
|
|
37
55
|
{
|
|
38
|
-
|
|
56
|
+
pkg: "next",
|
|
57
|
+
subpath: "dist/server/dev/next-dev-server.js",
|
|
39
58
|
content: "module.exports = { default: null };"
|
|
40
59
|
},
|
|
41
60
|
{
|
|
42
|
-
|
|
61
|
+
pkg: "next",
|
|
62
|
+
subpath: "dist/server/lib/router-utils/setup-dev-bundler.js",
|
|
43
63
|
content: "module.exports = {};"
|
|
44
64
|
},
|
|
45
65
|
{
|
|
46
|
-
|
|
66
|
+
pkg: "@opentelemetry/api",
|
|
67
|
+
subpath: "index.js",
|
|
47
68
|
content: "throw new Error('not installed');"
|
|
48
69
|
},
|
|
49
70
|
{
|
|
50
|
-
|
|
71
|
+
pkg: "critters",
|
|
72
|
+
subpath: "index.js",
|
|
51
73
|
content: "module.exports = {};"
|
|
52
74
|
}
|
|
53
75
|
];
|
|
76
|
+
const nodeModulesDir = join(standaloneDir, "node_modules");
|
|
54
77
|
let count = 0;
|
|
55
78
|
for (const stub of stubs) {
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
79
|
+
const pkgDirs = findPackageDirs(nodeModulesDir, stub.pkg);
|
|
80
|
+
if (pkgDirs.length === 0)
|
|
81
|
+
pkgDirs.push(join(nodeModulesDir, stub.pkg));
|
|
82
|
+
for (const pkgDir of pkgDirs) {
|
|
83
|
+
const fullPath = join(pkgDir, stub.subpath);
|
|
84
|
+
if (!existsSync(fullPath)) {
|
|
85
|
+
const dir = join(fullPath, "..");
|
|
86
|
+
if (!existsSync(dir)) {
|
|
87
|
+
mkdirSync(dir, { recursive: true });
|
|
88
|
+
}
|
|
89
|
+
writeFileSync(fullPath, stub.content);
|
|
90
|
+
count++;
|
|
61
91
|
}
|
|
62
|
-
writeFileSync(fullPath, stub.content);
|
|
63
|
-
count++;
|
|
64
92
|
}
|
|
65
93
|
}
|
|
66
94
|
if (count > 0) {
|
|
67
95
|
console.log(`next-bun-compile: Created ${count} module stubs`);
|
|
68
96
|
}
|
|
69
97
|
}
|
|
98
|
+
function findServerDir(standaloneDir) {
|
|
99
|
+
if (existsSync(join(standaloneDir, "server.js"))) {
|
|
100
|
+
return standaloneDir;
|
|
101
|
+
}
|
|
102
|
+
function search(dir) {
|
|
103
|
+
if (!existsSync(dir))
|
|
104
|
+
return null;
|
|
105
|
+
for (const entry of readdirSync(dir)) {
|
|
106
|
+
if (entry === "node_modules")
|
|
107
|
+
continue;
|
|
108
|
+
const full = join(dir, entry);
|
|
109
|
+
if (!statSync(full).isDirectory())
|
|
110
|
+
continue;
|
|
111
|
+
if (existsSync(join(full, "server.js")))
|
|
112
|
+
return full;
|
|
113
|
+
const found2 = search(full);
|
|
114
|
+
if (found2)
|
|
115
|
+
return found2;
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
const found = search(standaloneDir);
|
|
120
|
+
if (!found) {
|
|
121
|
+
throw new Error("next-bun-compile: Could not find server.js in standalone output");
|
|
122
|
+
}
|
|
123
|
+
const rel = relative(standaloneDir, found);
|
|
124
|
+
console.log(`next-bun-compile: Monorepo layout detected — server.js found at ${rel}/`);
|
|
125
|
+
return found;
|
|
126
|
+
}
|
|
70
127
|
function patchRequireHook(standaloneDir) {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
return;
|
|
74
|
-
let content = readFileSync(hookPath, "utf-8");
|
|
128
|
+
const nodeModulesDir = join(standaloneDir, "node_modules");
|
|
129
|
+
const nextDirs = findPackageDirs(nodeModulesDir, "next");
|
|
75
130
|
const target = "let resolve = process.env.NEXT_MINIMAL ? __non_webpack_require__.resolve : require.resolve;";
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
131
|
+
const replacement = `let _resolve = process.env.NEXT_MINIMAL ? __non_webpack_require__.resolve : require.resolve;
|
|
132
|
+
let resolve = (id) => { try { return _resolve(id); } catch { return ''; } };`;
|
|
133
|
+
let patched = 0;
|
|
134
|
+
for (const nextDir of nextDirs) {
|
|
135
|
+
const hookPath = join(nextDir, "dist/server/require-hook.js");
|
|
136
|
+
if (!existsSync(hookPath))
|
|
137
|
+
continue;
|
|
138
|
+
let content = readFileSync(hookPath, "utf-8");
|
|
139
|
+
if (!content.includes(target))
|
|
140
|
+
continue;
|
|
141
|
+
content = content.replace(target, replacement);
|
|
142
|
+
writeFileSync(hookPath, content);
|
|
143
|
+
patched++;
|
|
144
|
+
}
|
|
145
|
+
if (patched > 0) {
|
|
146
|
+
console.log("next-bun-compile: Patched require-hook.js for compiled binary compatibility");
|
|
147
|
+
}
|
|
82
148
|
}
|
|
83
|
-
function collectExternalModules(standaloneDir) {
|
|
84
|
-
const chunksDir = join(
|
|
149
|
+
function collectExternalModules(standaloneDir, serverDir) {
|
|
150
|
+
const chunksDir = join(serverDir, ".next/server/chunks");
|
|
85
151
|
if (!existsSync(chunksDir))
|
|
86
152
|
return [];
|
|
87
153
|
const seeds = new Set;
|
|
88
|
-
for (const
|
|
89
|
-
if (!
|
|
154
|
+
for (const { absolutePath } of walkDir(chunksDir)) {
|
|
155
|
+
if (!absolutePath.endsWith(".js"))
|
|
90
156
|
continue;
|
|
91
|
-
const content = readFileSync(
|
|
157
|
+
const content = readFileSync(absolutePath, "utf-8");
|
|
92
158
|
for (const match of content.matchAll(/require\("(next\/dist\/[^"]+)"\)/g)) {
|
|
93
159
|
seeds.add(match[1]);
|
|
94
160
|
}
|
|
@@ -132,6 +198,7 @@ function collectExternalModules(standaloneDir) {
|
|
|
132
198
|
}
|
|
133
199
|
function generateEntryPoint(options) {
|
|
134
200
|
const { standaloneDir, distDir, projectDir } = options;
|
|
201
|
+
const serverDir = findServerDir(standaloneDir);
|
|
135
202
|
generateStubs(standaloneDir);
|
|
136
203
|
patchRequireHook(standaloneDir);
|
|
137
204
|
const staticDir = join(distDir, "static");
|
|
@@ -144,14 +211,14 @@ function generateEntryPoint(options) {
|
|
|
144
211
|
...f,
|
|
145
212
|
urlPath: `/${f.relativePath.replace(/\\/g, "/")}`
|
|
146
213
|
}));
|
|
147
|
-
const standaloneNextDir = join(
|
|
214
|
+
const standaloneNextDir = join(serverDir, ".next");
|
|
148
215
|
const runtimeFiles = walkDir(standaloneNextDir).map((f) => ({
|
|
149
216
|
...f,
|
|
150
217
|
urlPath: `__runtime/.next/${f.relativePath.replace(/\\/g, "/")}`
|
|
151
218
|
}));
|
|
152
|
-
const externalModules = collectExternalModules(standaloneDir);
|
|
219
|
+
const externalModules = collectExternalModules(standaloneDir, serverDir);
|
|
153
220
|
const externalPaths = ["next/package.json", ...externalModules];
|
|
154
|
-
const externalDir = join(
|
|
221
|
+
const externalDir = join(serverDir, ".next/__external");
|
|
155
222
|
for (const mod of externalPaths) {
|
|
156
223
|
const src = join(standaloneDir, "node_modules", mod);
|
|
157
224
|
if (!existsSync(src))
|
|
@@ -179,18 +246,18 @@ function generateEntryPoint(options) {
|
|
|
179
246
|
const mapEntries = [];
|
|
180
247
|
for (const asset of assetsToEmbed) {
|
|
181
248
|
const varName = toVarName(asset.urlPath);
|
|
182
|
-
const importPath = relative(
|
|
249
|
+
const importPath = relative(serverDir, asset.absolutePath).replace(/\\/g, "/");
|
|
183
250
|
imports.push(`import ${varName} from "./${importPath}" with { type: "file" };`);
|
|
184
251
|
mapEntries.push(` ["${asset.urlPath}", ${varName}],`);
|
|
185
252
|
}
|
|
186
|
-
writeFileSync(join(
|
|
253
|
+
writeFileSync(join(serverDir, "assets.generated.js"), `${imports.join(`
|
|
187
254
|
`)}
|
|
188
255
|
export const assetMap = new Map([
|
|
189
256
|
${mapEntries.join(`
|
|
190
257
|
`)}
|
|
191
258
|
]);
|
|
192
259
|
`);
|
|
193
|
-
const standaloneServerSrc = readFileSync(join(
|
|
260
|
+
const standaloneServerSrc = readFileSync(join(serverDir, "server.js"), "utf-8");
|
|
194
261
|
const configMatch = standaloneServerSrc.match(/const nextConfig = ({[\s\S]*?})\n/);
|
|
195
262
|
if (!configMatch) {
|
|
196
263
|
throw new Error("next-bun-compile: Could not extract nextConfig from standalone server.js");
|
|
@@ -246,15 +313,16 @@ extractAssets().then(() => {
|
|
|
246
313
|
});
|
|
247
314
|
}).catch((err) => { console.error(err); process.exit(1); });
|
|
248
315
|
`;
|
|
249
|
-
writeFileSync(join(
|
|
316
|
+
writeFileSync(join(serverDir, "server-entry.js"), serverEntry);
|
|
317
|
+
return serverDir;
|
|
250
318
|
}
|
|
251
319
|
|
|
252
320
|
// src/compile.ts
|
|
253
321
|
import { execFileSync } from "node:child_process";
|
|
254
322
|
import { join as join2 } from "node:path";
|
|
255
323
|
function compile(options) {
|
|
256
|
-
const {
|
|
257
|
-
const entryPoint = join2(
|
|
324
|
+
const { serverDir, outfile, extraArgs = [] } = options;
|
|
325
|
+
const entryPoint = join2(serverDir, "server-entry.js");
|
|
258
326
|
const args = [
|
|
259
327
|
"build",
|
|
260
328
|
entryPoint,
|
|
@@ -293,5 +361,5 @@ var ctxPath = join3(distDir, "bun-compile-ctx.json");
|
|
|
293
361
|
if (existsSync2(ctxPath)) {
|
|
294
362
|
console.log("next-bun-compile: Using build context from adapter");
|
|
295
363
|
}
|
|
296
|
-
generateEntryPoint({ standaloneDir, distDir, projectDir });
|
|
297
|
-
compile({
|
|
364
|
+
var serverDir = generateEntryPoint({ standaloneDir, distDir, projectDir });
|
|
365
|
+
compile({ serverDir, outfile: join3(projectDir, "server"), extraArgs });
|
package/dist/compile.js
CHANGED
|
@@ -5,8 +5,8 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
function compile(options) {
|
|
8
|
-
const {
|
|
9
|
-
const entryPoint = join(
|
|
8
|
+
const { serverDir, outfile, extraArgs = [] } = options;
|
|
9
|
+
const entryPoint = join(serverDir, "server-entry.js");
|
|
10
10
|
const args = [
|
|
11
11
|
"build",
|
|
12
12
|
entryPoint,
|
package/dist/generate.js
CHANGED
|
@@ -31,63 +31,129 @@ function toVarName(filePath) {
|
|
|
31
31
|
const safe = filePath.replace(/[^a-zA-Z0-9]/g, "_").slice(0, 40);
|
|
32
32
|
return `asset_${safe}_${hash}`;
|
|
33
33
|
}
|
|
34
|
+
function findPackageDirs(nodeModulesDir, pkg) {
|
|
35
|
+
const dirs = [];
|
|
36
|
+
const direct = join(nodeModulesDir, pkg);
|
|
37
|
+
if (existsSync(direct))
|
|
38
|
+
dirs.push(direct);
|
|
39
|
+
const bunDir = join(nodeModulesDir, ".bun");
|
|
40
|
+
if (existsSync(bunDir)) {
|
|
41
|
+
const scope = pkg.startsWith("@") ? pkg.split("/")[0] + "+" + pkg.split("/")[1] : pkg;
|
|
42
|
+
for (const entry of readdirSync(bunDir)) {
|
|
43
|
+
if (!entry.startsWith(scope + "@"))
|
|
44
|
+
continue;
|
|
45
|
+
const hoisted = join(bunDir, entry, "node_modules", pkg);
|
|
46
|
+
if (existsSync(hoisted))
|
|
47
|
+
dirs.push(hoisted);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return dirs;
|
|
51
|
+
}
|
|
34
52
|
function generateStubs(standaloneDir) {
|
|
35
53
|
const stubs = [
|
|
36
54
|
{
|
|
37
|
-
|
|
55
|
+
pkg: "next",
|
|
56
|
+
subpath: "dist/server/dev/next-dev-server.js",
|
|
38
57
|
content: "module.exports = { default: null };"
|
|
39
58
|
},
|
|
40
59
|
{
|
|
41
|
-
|
|
60
|
+
pkg: "next",
|
|
61
|
+
subpath: "dist/server/lib/router-utils/setup-dev-bundler.js",
|
|
42
62
|
content: "module.exports = {};"
|
|
43
63
|
},
|
|
44
64
|
{
|
|
45
|
-
|
|
65
|
+
pkg: "@opentelemetry/api",
|
|
66
|
+
subpath: "index.js",
|
|
46
67
|
content: "throw new Error('not installed');"
|
|
47
68
|
},
|
|
48
69
|
{
|
|
49
|
-
|
|
70
|
+
pkg: "critters",
|
|
71
|
+
subpath: "index.js",
|
|
50
72
|
content: "module.exports = {};"
|
|
51
73
|
}
|
|
52
74
|
];
|
|
75
|
+
const nodeModulesDir = join(standaloneDir, "node_modules");
|
|
53
76
|
let count = 0;
|
|
54
77
|
for (const stub of stubs) {
|
|
55
|
-
const
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
78
|
+
const pkgDirs = findPackageDirs(nodeModulesDir, stub.pkg);
|
|
79
|
+
if (pkgDirs.length === 0)
|
|
80
|
+
pkgDirs.push(join(nodeModulesDir, stub.pkg));
|
|
81
|
+
for (const pkgDir of pkgDirs) {
|
|
82
|
+
const fullPath = join(pkgDir, stub.subpath);
|
|
83
|
+
if (!existsSync(fullPath)) {
|
|
84
|
+
const dir = join(fullPath, "..");
|
|
85
|
+
if (!existsSync(dir)) {
|
|
86
|
+
mkdirSync(dir, { recursive: true });
|
|
87
|
+
}
|
|
88
|
+
writeFileSync(fullPath, stub.content);
|
|
89
|
+
count++;
|
|
60
90
|
}
|
|
61
|
-
writeFileSync(fullPath, stub.content);
|
|
62
|
-
count++;
|
|
63
91
|
}
|
|
64
92
|
}
|
|
65
93
|
if (count > 0) {
|
|
66
94
|
console.log(`next-bun-compile: Created ${count} module stubs`);
|
|
67
95
|
}
|
|
68
96
|
}
|
|
97
|
+
function findServerDir(standaloneDir) {
|
|
98
|
+
if (existsSync(join(standaloneDir, "server.js"))) {
|
|
99
|
+
return standaloneDir;
|
|
100
|
+
}
|
|
101
|
+
function search(dir) {
|
|
102
|
+
if (!existsSync(dir))
|
|
103
|
+
return null;
|
|
104
|
+
for (const entry of readdirSync(dir)) {
|
|
105
|
+
if (entry === "node_modules")
|
|
106
|
+
continue;
|
|
107
|
+
const full = join(dir, entry);
|
|
108
|
+
if (!statSync(full).isDirectory())
|
|
109
|
+
continue;
|
|
110
|
+
if (existsSync(join(full, "server.js")))
|
|
111
|
+
return full;
|
|
112
|
+
const found2 = search(full);
|
|
113
|
+
if (found2)
|
|
114
|
+
return found2;
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const found = search(standaloneDir);
|
|
119
|
+
if (!found) {
|
|
120
|
+
throw new Error("next-bun-compile: Could not find server.js in standalone output");
|
|
121
|
+
}
|
|
122
|
+
const rel = relative(standaloneDir, found);
|
|
123
|
+
console.log(`next-bun-compile: Monorepo layout detected — server.js found at ${rel}/`);
|
|
124
|
+
return found;
|
|
125
|
+
}
|
|
69
126
|
function patchRequireHook(standaloneDir) {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
return;
|
|
73
|
-
let content = readFileSync(hookPath, "utf-8");
|
|
127
|
+
const nodeModulesDir = join(standaloneDir, "node_modules");
|
|
128
|
+
const nextDirs = findPackageDirs(nodeModulesDir, "next");
|
|
74
129
|
const target = "let resolve = process.env.NEXT_MINIMAL ? __non_webpack_require__.resolve : require.resolve;";
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
130
|
+
const replacement = `let _resolve = process.env.NEXT_MINIMAL ? __non_webpack_require__.resolve : require.resolve;
|
|
131
|
+
let resolve = (id) => { try { return _resolve(id); } catch { return ''; } };`;
|
|
132
|
+
let patched = 0;
|
|
133
|
+
for (const nextDir of nextDirs) {
|
|
134
|
+
const hookPath = join(nextDir, "dist/server/require-hook.js");
|
|
135
|
+
if (!existsSync(hookPath))
|
|
136
|
+
continue;
|
|
137
|
+
let content = readFileSync(hookPath, "utf-8");
|
|
138
|
+
if (!content.includes(target))
|
|
139
|
+
continue;
|
|
140
|
+
content = content.replace(target, replacement);
|
|
141
|
+
writeFileSync(hookPath, content);
|
|
142
|
+
patched++;
|
|
143
|
+
}
|
|
144
|
+
if (patched > 0) {
|
|
145
|
+
console.log("next-bun-compile: Patched require-hook.js for compiled binary compatibility");
|
|
146
|
+
}
|
|
81
147
|
}
|
|
82
|
-
function collectExternalModules(standaloneDir) {
|
|
83
|
-
const chunksDir = join(
|
|
148
|
+
function collectExternalModules(standaloneDir, serverDir) {
|
|
149
|
+
const chunksDir = join(serverDir, ".next/server/chunks");
|
|
84
150
|
if (!existsSync(chunksDir))
|
|
85
151
|
return [];
|
|
86
152
|
const seeds = new Set;
|
|
87
|
-
for (const
|
|
88
|
-
if (!
|
|
153
|
+
for (const { absolutePath } of walkDir(chunksDir)) {
|
|
154
|
+
if (!absolutePath.endsWith(".js"))
|
|
89
155
|
continue;
|
|
90
|
-
const content = readFileSync(
|
|
156
|
+
const content = readFileSync(absolutePath, "utf-8");
|
|
91
157
|
for (const match of content.matchAll(/require\("(next\/dist\/[^"]+)"\)/g)) {
|
|
92
158
|
seeds.add(match[1]);
|
|
93
159
|
}
|
|
@@ -131,6 +197,7 @@ function collectExternalModules(standaloneDir) {
|
|
|
131
197
|
}
|
|
132
198
|
function generateEntryPoint(options) {
|
|
133
199
|
const { standaloneDir, distDir, projectDir } = options;
|
|
200
|
+
const serverDir = findServerDir(standaloneDir);
|
|
134
201
|
generateStubs(standaloneDir);
|
|
135
202
|
patchRequireHook(standaloneDir);
|
|
136
203
|
const staticDir = join(distDir, "static");
|
|
@@ -143,14 +210,14 @@ function generateEntryPoint(options) {
|
|
|
143
210
|
...f,
|
|
144
211
|
urlPath: `/${f.relativePath.replace(/\\/g, "/")}`
|
|
145
212
|
}));
|
|
146
|
-
const standaloneNextDir = join(
|
|
213
|
+
const standaloneNextDir = join(serverDir, ".next");
|
|
147
214
|
const runtimeFiles = walkDir(standaloneNextDir).map((f) => ({
|
|
148
215
|
...f,
|
|
149
216
|
urlPath: `__runtime/.next/${f.relativePath.replace(/\\/g, "/")}`
|
|
150
217
|
}));
|
|
151
|
-
const externalModules = collectExternalModules(standaloneDir);
|
|
218
|
+
const externalModules = collectExternalModules(standaloneDir, serverDir);
|
|
152
219
|
const externalPaths = ["next/package.json", ...externalModules];
|
|
153
|
-
const externalDir = join(
|
|
220
|
+
const externalDir = join(serverDir, ".next/__external");
|
|
154
221
|
for (const mod of externalPaths) {
|
|
155
222
|
const src = join(standaloneDir, "node_modules", mod);
|
|
156
223
|
if (!existsSync(src))
|
|
@@ -178,18 +245,18 @@ function generateEntryPoint(options) {
|
|
|
178
245
|
const mapEntries = [];
|
|
179
246
|
for (const asset of assetsToEmbed) {
|
|
180
247
|
const varName = toVarName(asset.urlPath);
|
|
181
|
-
const importPath = relative(
|
|
248
|
+
const importPath = relative(serverDir, asset.absolutePath).replace(/\\/g, "/");
|
|
182
249
|
imports.push(`import ${varName} from "./${importPath}" with { type: "file" };`);
|
|
183
250
|
mapEntries.push(` ["${asset.urlPath}", ${varName}],`);
|
|
184
251
|
}
|
|
185
|
-
writeFileSync(join(
|
|
252
|
+
writeFileSync(join(serverDir, "assets.generated.js"), `${imports.join(`
|
|
186
253
|
`)}
|
|
187
254
|
export const assetMap = new Map([
|
|
188
255
|
${mapEntries.join(`
|
|
189
256
|
`)}
|
|
190
257
|
]);
|
|
191
258
|
`);
|
|
192
|
-
const standaloneServerSrc = readFileSync(join(
|
|
259
|
+
const standaloneServerSrc = readFileSync(join(serverDir, "server.js"), "utf-8");
|
|
193
260
|
const configMatch = standaloneServerSrc.match(/const nextConfig = ({[\s\S]*?})\n/);
|
|
194
261
|
if (!configMatch) {
|
|
195
262
|
throw new Error("next-bun-compile: Could not extract nextConfig from standalone server.js");
|
|
@@ -245,7 +312,8 @@ extractAssets().then(() => {
|
|
|
245
312
|
});
|
|
246
313
|
}).catch((err) => { console.error(err); process.exit(1); });
|
|
247
314
|
`;
|
|
248
|
-
writeFileSync(join(
|
|
315
|
+
writeFileSync(join(serverDir, "server-entry.js"), serverEntry);
|
|
316
|
+
return serverDir;
|
|
249
317
|
}
|
|
250
318
|
export {
|
|
251
319
|
generateEntryPoint
|
package/dist/index.js
CHANGED
|
@@ -31,63 +31,129 @@ function toVarName(filePath) {
|
|
|
31
31
|
const safe = filePath.replace(/[^a-zA-Z0-9]/g, "_").slice(0, 40);
|
|
32
32
|
return `asset_${safe}_${hash}`;
|
|
33
33
|
}
|
|
34
|
+
function findPackageDirs(nodeModulesDir, pkg) {
|
|
35
|
+
const dirs = [];
|
|
36
|
+
const direct = join(nodeModulesDir, pkg);
|
|
37
|
+
if (existsSync(direct))
|
|
38
|
+
dirs.push(direct);
|
|
39
|
+
const bunDir = join(nodeModulesDir, ".bun");
|
|
40
|
+
if (existsSync(bunDir)) {
|
|
41
|
+
const scope = pkg.startsWith("@") ? pkg.split("/")[0] + "+" + pkg.split("/")[1] : pkg;
|
|
42
|
+
for (const entry of readdirSync(bunDir)) {
|
|
43
|
+
if (!entry.startsWith(scope + "@"))
|
|
44
|
+
continue;
|
|
45
|
+
const hoisted = join(bunDir, entry, "node_modules", pkg);
|
|
46
|
+
if (existsSync(hoisted))
|
|
47
|
+
dirs.push(hoisted);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return dirs;
|
|
51
|
+
}
|
|
34
52
|
function generateStubs(standaloneDir) {
|
|
35
53
|
const stubs = [
|
|
36
54
|
{
|
|
37
|
-
|
|
55
|
+
pkg: "next",
|
|
56
|
+
subpath: "dist/server/dev/next-dev-server.js",
|
|
38
57
|
content: "module.exports = { default: null };"
|
|
39
58
|
},
|
|
40
59
|
{
|
|
41
|
-
|
|
60
|
+
pkg: "next",
|
|
61
|
+
subpath: "dist/server/lib/router-utils/setup-dev-bundler.js",
|
|
42
62
|
content: "module.exports = {};"
|
|
43
63
|
},
|
|
44
64
|
{
|
|
45
|
-
|
|
65
|
+
pkg: "@opentelemetry/api",
|
|
66
|
+
subpath: "index.js",
|
|
46
67
|
content: "throw new Error('not installed');"
|
|
47
68
|
},
|
|
48
69
|
{
|
|
49
|
-
|
|
70
|
+
pkg: "critters",
|
|
71
|
+
subpath: "index.js",
|
|
50
72
|
content: "module.exports = {};"
|
|
51
73
|
}
|
|
52
74
|
];
|
|
75
|
+
const nodeModulesDir = join(standaloneDir, "node_modules");
|
|
53
76
|
let count = 0;
|
|
54
77
|
for (const stub of stubs) {
|
|
55
|
-
const
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
78
|
+
const pkgDirs = findPackageDirs(nodeModulesDir, stub.pkg);
|
|
79
|
+
if (pkgDirs.length === 0)
|
|
80
|
+
pkgDirs.push(join(nodeModulesDir, stub.pkg));
|
|
81
|
+
for (const pkgDir of pkgDirs) {
|
|
82
|
+
const fullPath = join(pkgDir, stub.subpath);
|
|
83
|
+
if (!existsSync(fullPath)) {
|
|
84
|
+
const dir = join(fullPath, "..");
|
|
85
|
+
if (!existsSync(dir)) {
|
|
86
|
+
mkdirSync(dir, { recursive: true });
|
|
87
|
+
}
|
|
88
|
+
writeFileSync(fullPath, stub.content);
|
|
89
|
+
count++;
|
|
60
90
|
}
|
|
61
|
-
writeFileSync(fullPath, stub.content);
|
|
62
|
-
count++;
|
|
63
91
|
}
|
|
64
92
|
}
|
|
65
93
|
if (count > 0) {
|
|
66
94
|
console.log(`next-bun-compile: Created ${count} module stubs`);
|
|
67
95
|
}
|
|
68
96
|
}
|
|
97
|
+
function findServerDir(standaloneDir) {
|
|
98
|
+
if (existsSync(join(standaloneDir, "server.js"))) {
|
|
99
|
+
return standaloneDir;
|
|
100
|
+
}
|
|
101
|
+
function search(dir) {
|
|
102
|
+
if (!existsSync(dir))
|
|
103
|
+
return null;
|
|
104
|
+
for (const entry of readdirSync(dir)) {
|
|
105
|
+
if (entry === "node_modules")
|
|
106
|
+
continue;
|
|
107
|
+
const full = join(dir, entry);
|
|
108
|
+
if (!statSync(full).isDirectory())
|
|
109
|
+
continue;
|
|
110
|
+
if (existsSync(join(full, "server.js")))
|
|
111
|
+
return full;
|
|
112
|
+
const found2 = search(full);
|
|
113
|
+
if (found2)
|
|
114
|
+
return found2;
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const found = search(standaloneDir);
|
|
119
|
+
if (!found) {
|
|
120
|
+
throw new Error("next-bun-compile: Could not find server.js in standalone output");
|
|
121
|
+
}
|
|
122
|
+
const rel = relative(standaloneDir, found);
|
|
123
|
+
console.log(`next-bun-compile: Monorepo layout detected — server.js found at ${rel}/`);
|
|
124
|
+
return found;
|
|
125
|
+
}
|
|
69
126
|
function patchRequireHook(standaloneDir) {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
return;
|
|
73
|
-
let content = readFileSync(hookPath, "utf-8");
|
|
127
|
+
const nodeModulesDir = join(standaloneDir, "node_modules");
|
|
128
|
+
const nextDirs = findPackageDirs(nodeModulesDir, "next");
|
|
74
129
|
const target = "let resolve = process.env.NEXT_MINIMAL ? __non_webpack_require__.resolve : require.resolve;";
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
130
|
+
const replacement = `let _resolve = process.env.NEXT_MINIMAL ? __non_webpack_require__.resolve : require.resolve;
|
|
131
|
+
let resolve = (id) => { try { return _resolve(id); } catch { return ''; } };`;
|
|
132
|
+
let patched = 0;
|
|
133
|
+
for (const nextDir of nextDirs) {
|
|
134
|
+
const hookPath = join(nextDir, "dist/server/require-hook.js");
|
|
135
|
+
if (!existsSync(hookPath))
|
|
136
|
+
continue;
|
|
137
|
+
let content = readFileSync(hookPath, "utf-8");
|
|
138
|
+
if (!content.includes(target))
|
|
139
|
+
continue;
|
|
140
|
+
content = content.replace(target, replacement);
|
|
141
|
+
writeFileSync(hookPath, content);
|
|
142
|
+
patched++;
|
|
143
|
+
}
|
|
144
|
+
if (patched > 0) {
|
|
145
|
+
console.log("next-bun-compile: Patched require-hook.js for compiled binary compatibility");
|
|
146
|
+
}
|
|
81
147
|
}
|
|
82
|
-
function collectExternalModules(standaloneDir) {
|
|
83
|
-
const chunksDir = join(
|
|
148
|
+
function collectExternalModules(standaloneDir, serverDir) {
|
|
149
|
+
const chunksDir = join(serverDir, ".next/server/chunks");
|
|
84
150
|
if (!existsSync(chunksDir))
|
|
85
151
|
return [];
|
|
86
152
|
const seeds = new Set;
|
|
87
|
-
for (const
|
|
88
|
-
if (!
|
|
153
|
+
for (const { absolutePath } of walkDir(chunksDir)) {
|
|
154
|
+
if (!absolutePath.endsWith(".js"))
|
|
89
155
|
continue;
|
|
90
|
-
const content = readFileSync(
|
|
156
|
+
const content = readFileSync(absolutePath, "utf-8");
|
|
91
157
|
for (const match of content.matchAll(/require\("(next\/dist\/[^"]+)"\)/g)) {
|
|
92
158
|
seeds.add(match[1]);
|
|
93
159
|
}
|
|
@@ -131,6 +197,7 @@ function collectExternalModules(standaloneDir) {
|
|
|
131
197
|
}
|
|
132
198
|
function generateEntryPoint(options) {
|
|
133
199
|
const { standaloneDir, distDir, projectDir } = options;
|
|
200
|
+
const serverDir = findServerDir(standaloneDir);
|
|
134
201
|
generateStubs(standaloneDir);
|
|
135
202
|
patchRequireHook(standaloneDir);
|
|
136
203
|
const staticDir = join(distDir, "static");
|
|
@@ -143,14 +210,14 @@ function generateEntryPoint(options) {
|
|
|
143
210
|
...f,
|
|
144
211
|
urlPath: `/${f.relativePath.replace(/\\/g, "/")}`
|
|
145
212
|
}));
|
|
146
|
-
const standaloneNextDir = join(
|
|
213
|
+
const standaloneNextDir = join(serverDir, ".next");
|
|
147
214
|
const runtimeFiles = walkDir(standaloneNextDir).map((f) => ({
|
|
148
215
|
...f,
|
|
149
216
|
urlPath: `__runtime/.next/${f.relativePath.replace(/\\/g, "/")}`
|
|
150
217
|
}));
|
|
151
|
-
const externalModules = collectExternalModules(standaloneDir);
|
|
218
|
+
const externalModules = collectExternalModules(standaloneDir, serverDir);
|
|
152
219
|
const externalPaths = ["next/package.json", ...externalModules];
|
|
153
|
-
const externalDir = join(
|
|
220
|
+
const externalDir = join(serverDir, ".next/__external");
|
|
154
221
|
for (const mod of externalPaths) {
|
|
155
222
|
const src = join(standaloneDir, "node_modules", mod);
|
|
156
223
|
if (!existsSync(src))
|
|
@@ -178,18 +245,18 @@ function generateEntryPoint(options) {
|
|
|
178
245
|
const mapEntries = [];
|
|
179
246
|
for (const asset of assetsToEmbed) {
|
|
180
247
|
const varName = toVarName(asset.urlPath);
|
|
181
|
-
const importPath = relative(
|
|
248
|
+
const importPath = relative(serverDir, asset.absolutePath).replace(/\\/g, "/");
|
|
182
249
|
imports.push(`import ${varName} from "./${importPath}" with { type: "file" };`);
|
|
183
250
|
mapEntries.push(` ["${asset.urlPath}", ${varName}],`);
|
|
184
251
|
}
|
|
185
|
-
writeFileSync(join(
|
|
252
|
+
writeFileSync(join(serverDir, "assets.generated.js"), `${imports.join(`
|
|
186
253
|
`)}
|
|
187
254
|
export const assetMap = new Map([
|
|
188
255
|
${mapEntries.join(`
|
|
189
256
|
`)}
|
|
190
257
|
]);
|
|
191
258
|
`);
|
|
192
|
-
const standaloneServerSrc = readFileSync(join(
|
|
259
|
+
const standaloneServerSrc = readFileSync(join(serverDir, "server.js"), "utf-8");
|
|
193
260
|
const configMatch = standaloneServerSrc.match(/const nextConfig = ({[\s\S]*?})\n/);
|
|
194
261
|
if (!configMatch) {
|
|
195
262
|
throw new Error("next-bun-compile: Could not extract nextConfig from standalone server.js");
|
|
@@ -245,15 +312,16 @@ extractAssets().then(() => {
|
|
|
245
312
|
});
|
|
246
313
|
}).catch((err) => { console.error(err); process.exit(1); });
|
|
247
314
|
`;
|
|
248
|
-
writeFileSync(join(
|
|
315
|
+
writeFileSync(join(serverDir, "server-entry.js"), serverEntry);
|
|
316
|
+
return serverDir;
|
|
249
317
|
}
|
|
250
318
|
|
|
251
319
|
// src/compile.ts
|
|
252
320
|
import { execFileSync } from "node:child_process";
|
|
253
321
|
import { join as join2 } from "node:path";
|
|
254
322
|
function compile(options) {
|
|
255
|
-
const {
|
|
256
|
-
const entryPoint = join2(
|
|
323
|
+
const { serverDir, outfile, extraArgs = [] } = options;
|
|
324
|
+
const entryPoint = join2(serverDir, "server-entry.js");
|
|
257
325
|
const args = [
|
|
258
326
|
"build",
|
|
259
327
|
entryPoint,
|