CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/next/lib/init.js
Views: 687
1
/*
2
This code makes it possible to start this nextjs server as a
3
Custmer Server as part of a running hub. We thus combine together
4
a node.js express server (the hub) with a nextjs server in a
5
single process.
6
7
IMPORTANT: to use this from packages/hub (say), it's critical that
8
packages/hub *also* have its own copy of next installed.
9
Otherwise, you'll see an error about
10
11
"Parsing error: Cannot find module 'next/babel'"
12
13
This is mentioned here, and it's maybe a bug in next?
14
https://www.gitmemory.com/issue/vercel/next.js/26127/862661818
15
*/
16
17
const { join } = require("path");
18
const getLogger = require("@cocalc/backend/logger").default;
19
const next = require("next");
20
const conf = require("../next.config");
21
const winston = getLogger("next:init");
22
23
async function init({ basePath }) {
24
// dev = Whether or not to run in dev mode. This features hot module reloading,
25
// but navigation between pages and serving pages is much slower.
26
const dev = process.env.NODE_ENV != "production";
27
28
winston.info(`basePath=${basePath}`);
29
// this is the next.js definition of "basePath";
30
// it differs from what we use in cocalc and internally here too.
31
conf.basePath = basePath == "/" ? "" : basePath;
32
conf.env.BASE_PATH = basePath;
33
34
winston.info(`creating next.js app with dev=${dev}`);
35
const app = next({ dev, dir: join(__dirname, ".."), conf });
36
37
const handle = app.getRequestHandler();
38
winston.info("preparing next.js app...");
39
40
// WARNING: This webpack init below is a workaround for a bug that was
41
// introduced in Nextjs 13. The custom server functionality described here
42
// https://nextjs.org/docs/advanced-features/custom-server
43
// which we are using to init this server from the hub for some
44
// reasons tries to import a build of webpack that needs to be init'd.
45
// I couldn't find a report of this bug anywhere, but trying to make
46
// a custom server with conf set to anything caused it, but without
47
// conf things worked fine. Somehow I tediously figured out the
48
// following workaround, which is just to explicitly init webpack
49
// before it gets used in prepare below:
50
require("next/dist/compiled/webpack/webpack").init(); // see comment above.
51
52
// app.prepare sets app.upgradeHandler, etc. --
53
// see https://github.com/vercel/next.js/blob/canary/packages/next/src/server/next.ts#L276
54
await app.prepare();
55
56
if (!dev) {
57
// The following is NOT a crazy a hack -- it's the result of me (ws)
58
// carefully reading nextjs source code for several hours.
59
// In production mode, we must completely disable the nextjs websocket upgrade
60
// handler, since it breaks allowing users to connect to the hub via a websocket,
61
// as it just kills all such connection immediately. That's done via some new
62
// code in nextjs v14 that IMHO the author does not understand, as you can see here:
63
// https://github.com/vercel/next.js/blob/23eba22d02290cff0021a53f449f1d7e32a35e56/packages/next/src/server/lib/router-server.ts#L667
64
// where there is a comment "// TODO: allow upgrade requests to pages/app paths?".
65
// In dev mode we leave this, since it suppots hot module loading, though
66
// we use a hack (see packages/hub/proxy/handle-upgrade.ts) that involves
67
// removing listeners. That hack could probably be redone better by using
68
// app.upgradeHandler directly.
69
// To see the importance of this you must:
70
// - build in prod mode (not dev, obviously)
71
// - load the cocalc next landing page /
72
// - then try to view /projects
73
// Without this fix, the websocket will disconnect. With this fix, the websocket works.
74
winston.info("patching upgrade handler");
75
app.upgradeHandler = () => {};
76
}
77
78
winston.info("ready to handle requests:");
79
return (req, res) => {
80
winston.http(`req.url=${req.url}`);
81
handle(req, res);
82
};
83
}
84
85
module.exports = init;
86
87