@digigov/cli-app 2.4.3-govgr-4752.27-07-26-12-24 → 2.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/CHANGELOG.md +13 -7
- package/next.common.cjs +76 -7
- package/nextrc.cjs +2 -1
- package/package.json +9 -13
- package/subcommands/build.js +10 -2
- package/subcommands/dev.js +5 -9
- package/subcommands/export.js +15 -5
- package/subcommands/start.js +56 -8
- package/utils/proxy-server.js +67 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
# Change Log - @digigov/cli-app
|
|
2
2
|
|
|
3
|
-
<!-- This log was last generated on Mon, 27 Jul 2026
|
|
3
|
+
<!-- This log was last generated on Mon, 27 Jul 2026 17:23:56 GMT and should not be manually modified. -->
|
|
4
4
|
|
|
5
5
|
<!-- Start content -->
|
|
6
6
|
|
|
7
|
-
## 2.
|
|
7
|
+
## 2.5.0
|
|
8
8
|
|
|
9
|
-
Mon, 27 Jul 2026
|
|
9
|
+
Mon, 27 Jul 2026 17:23:56 GMT
|
|
10
10
|
|
|
11
11
|
### Minor changes
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
- Bump @digigov/cli to v2.
|
|
15
|
-
- Bump @digigov/cli
|
|
16
|
-
- Bump @digigov/
|
|
13
|
+
- Next.js 13.1.1 -> 16.2.12: builds now use static export (output: 'export', next export was removed in Next 14) while next dev keeps the regular dev server, and digigov app start serves out/ statically (-p/--port, -H/--hostname). Drop next-images/url-loader/babel-loader in favor of built-in static image imports, remove the rimraf peer dependency, and modernize the dev proxy to http-proxy-middleware v3 running in-process, arming websocket upgrades for ws: true proxy entries at startup so sockets reconnecting right after a restart are not dropped. (sangelopoulos@admin.grnet.gr)
|
|
14
|
+
- Bump @digigov/cli-build to v2.5.0
|
|
15
|
+
- Bump @digigov/cli to v2.5.0
|
|
16
|
+
- Bump @digigov/cli-lint to v2.5.0
|
|
17
|
+
- Bump @digigov/css to v2.5.0
|
|
18
|
+
|
|
19
|
+
### Patches
|
|
20
|
+
|
|
21
|
+
- default images.unoptimized in dev as well as build so dev image rendering matches the exported artifact and @digigov/nextjs Image's basePath prepend behaves the same everywhere (sangelopoulos@admin.grnet.gr)
|
|
22
|
+
- route dev proxy and Next config output through the @digigov/cli logger instead of bare console calls (sangelopoulos@admin.grnet.gr)
|
|
17
23
|
|
|
18
24
|
## 2.4.2
|
|
19
25
|
|
package/next.common.cjs
CHANGED
|
@@ -1,8 +1,40 @@
|
|
|
1
1
|
const lib = require('@digigov/cli/lib');
|
|
2
|
+
const { logger } = require('@digigov/cli/lib/logger');
|
|
3
|
+
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants');
|
|
2
4
|
let aliases = lib.aliases(true);
|
|
3
|
-
const withImages = require('next-images');
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
// `next export` was removed in Next 14: outside the dev server the app must be
|
|
7
|
+
// built with `output: 'export'` for `next build` to emit the static site in
|
|
8
|
+
// `out/`. Export mode breaks `next dev` (and the dev proxy), so it can only be
|
|
9
|
+
// applied per phase. The built-in i18n routing is unsupported under static
|
|
10
|
+
// export.
|
|
11
|
+
// Images are unoptimized in every phase (not just export, where the default
|
|
12
|
+
// loader is unsupported): next/image never applies basePath to unoptimized
|
|
13
|
+
// srcs, so @digigov/nextjs's Image prepends it manually — with the optimizer
|
|
14
|
+
// active in dev that prepend would break, and dev would render differently
|
|
15
|
+
// from the deployed artifact.
|
|
16
|
+
function withStaticExport(config, phase) {
|
|
17
|
+
const common = {
|
|
18
|
+
...config,
|
|
19
|
+
images: { unoptimized: true, ...config.images },
|
|
20
|
+
};
|
|
21
|
+
if (phase === PHASE_DEVELOPMENT_SERVER) return common;
|
|
22
|
+
const exported = {
|
|
23
|
+
output: 'export',
|
|
24
|
+
...common,
|
|
25
|
+
};
|
|
26
|
+
if (exported.output === 'export' && exported.i18n) {
|
|
27
|
+
logger.warn(
|
|
28
|
+
'Built-in i18n routing is not supported with static export; ' +
|
|
29
|
+
'dropping the `i18n` config for this build. Locale routing must be ' +
|
|
30
|
+
'handled in userland (e.g. i18next without the Next.js `i18n` key).'
|
|
31
|
+
);
|
|
32
|
+
delete exported.i18n;
|
|
33
|
+
}
|
|
34
|
+
return exported;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function makeBaseConfig(dir) {
|
|
6
38
|
const project = lib.resolveProject(dir);
|
|
7
39
|
const digigov = project.digigov || {};
|
|
8
40
|
if (digigov.aliases) {
|
|
@@ -11,15 +43,52 @@ function makeNextConfig(dir) {
|
|
|
11
43
|
...digigov.aliases,
|
|
12
44
|
};
|
|
13
45
|
}
|
|
14
|
-
|
|
15
|
-
|
|
46
|
+
logger.info(`Creating aliases for ${Object.keys(aliases).join(', ')}`);
|
|
47
|
+
return {
|
|
16
48
|
poweredByHeader: false,
|
|
17
49
|
trailingSlash: true,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
50
|
+
// Published-package consumers need more than the workspace aliases here.
|
|
51
|
+
// Turbopack bundles app imports of node_modules on the server, but
|
|
52
|
+
// externalizes the same imports made from inside node_modules (e.g. from
|
|
53
|
+
// @digigov/nextjs) — so any package holding shared state (React contexts,
|
|
54
|
+
// singletons) must be listed to unify every import onto one bundled
|
|
55
|
+
// module instance:
|
|
56
|
+
// - @digigov/nextjs also must never load as a Node external: its ESM dist
|
|
57
|
+
// does `import Document from 'next/document'` (CJS), which only
|
|
58
|
+
// resolves to the class under bundler interop — externally the default
|
|
59
|
+
// import yields the whole exports object and SSR crashes.
|
|
60
|
+
// - @digigov/ui + react-core carry LinkProvider/I18NProvider contexts: a
|
|
61
|
+
// split copy makes SSR fall back to plain anchors (no next/link, no
|
|
62
|
+
// trailing-slash normalization → hydration mismatch).
|
|
63
|
+
// - i18next/react-i18next are singletons initialized once by initI18n():
|
|
64
|
+
// a split copy renders raw translation keys during SSR.
|
|
65
|
+
// Inside the SDK workspace the aliases already cover the workspace libs;
|
|
66
|
+
// the Set just dedupes.
|
|
67
|
+
transpilePackages: [
|
|
68
|
+
...new Set([
|
|
69
|
+
...Object.keys(aliases),
|
|
70
|
+
'@digigov/nextjs',
|
|
71
|
+
'@digigov/ui',
|
|
72
|
+
'@digigov/react-core',
|
|
73
|
+
'@digigov/react-icons',
|
|
74
|
+
'@digigov/form',
|
|
75
|
+
'i18next',
|
|
76
|
+
'react-i18next',
|
|
77
|
+
]),
|
|
78
|
+
],
|
|
79
|
+
};
|
|
21
80
|
}
|
|
81
|
+
|
|
82
|
+
const baseConfig = makeBaseConfig();
|
|
83
|
+
|
|
84
|
+
function makeNextConfig(dir) {
|
|
85
|
+
const base = dir ? makeBaseConfig(dir) : baseConfig;
|
|
86
|
+
return (phase) => withStaticExport(base, phase);
|
|
87
|
+
}
|
|
88
|
+
|
|
22
89
|
module.exports = {
|
|
90
|
+
baseConfig,
|
|
23
91
|
makeNextConfig,
|
|
92
|
+
withStaticExport,
|
|
24
93
|
config: makeNextConfig(),
|
|
25
94
|
};
|
package/nextrc.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { makeConfig } = require('@digigov/cli/lib');
|
|
2
|
+
const { baseConfig, withStaticExport } = require('./next.common.cjs');
|
|
2
3
|
|
|
3
4
|
module.exports = (overrides) => {
|
|
4
|
-
return makeConfig(overrides,
|
|
5
|
+
return (phase) => withStaticExport(makeConfig(overrides, baseConfig), phase);
|
|
5
6
|
};
|
package/package.json
CHANGED
|
@@ -1,33 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@digigov/cli-app",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "@digigov developer cli tool",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": "GRNET Developers <devs@lists.grnet.gr>",
|
|
8
8
|
"license": "BSD-2-Clause",
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"next-images": "1.3.0",
|
|
14
|
-
"url-loader": "4.1.1",
|
|
15
|
-
"next": "13.1.1",
|
|
10
|
+
"express": "4.22.2",
|
|
11
|
+
"http-proxy-middleware": "3.0.5",
|
|
12
|
+
"next": "16.2.12",
|
|
16
13
|
"commander": "12.1.0",
|
|
17
14
|
"globby": "11.0.0",
|
|
18
15
|
"publint": "0.1.8",
|
|
19
|
-
"@digigov/cli-build": "2.
|
|
16
|
+
"@digigov/cli-build": "2.5.0"
|
|
20
17
|
},
|
|
21
18
|
"peerDependencies": {
|
|
22
|
-
"rimraf": "3.0.2",
|
|
23
19
|
"typescript": "5.6.2",
|
|
24
20
|
"@types/node": "20.17.24",
|
|
25
21
|
"@types/react": "19.1.3",
|
|
26
22
|
"autoprefixer": "10.4.16",
|
|
27
23
|
"postcss": "8.4.4",
|
|
28
24
|
"tailwindcss": "3.4.13",
|
|
29
|
-
"@digigov/cli": "2.
|
|
30
|
-
"@digigov/css": "2.
|
|
25
|
+
"@digigov/cli": "2.5.0",
|
|
26
|
+
"@digigov/css": "2.5.0"
|
|
31
27
|
},
|
|
32
28
|
"exports": {
|
|
33
29
|
".": "./index.js",
|
|
@@ -45,8 +41,8 @@
|
|
|
45
41
|
"typescript": "5.6.2",
|
|
46
42
|
"eslint": "9.16.0",
|
|
47
43
|
"prettier": "3.4.2",
|
|
48
|
-
"@digigov/cli": "2.
|
|
49
|
-
"@digigov/cli-lint": "2.
|
|
44
|
+
"@digigov/cli": "2.5.0",
|
|
45
|
+
"@digigov/cli-lint": "2.5.0"
|
|
50
46
|
},
|
|
51
47
|
"scripts": {
|
|
52
48
|
"publint": "publint",
|
package/subcommands/build.js
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
import { DigigovCommand, resolveProject } from '@digigov/cli/lib';
|
|
2
5
|
import { EXEC_CONFIG } from '../common.js';
|
|
3
6
|
|
|
4
7
|
const buildCommand = new DigigovCommand('build');
|
|
5
8
|
|
|
9
|
+
const project = resolveProject();
|
|
10
|
+
|
|
6
11
|
buildCommand
|
|
7
12
|
.argument('[...args]', 'Arguments to pass to the `next build` command')
|
|
8
13
|
.helpOption(false)
|
|
9
14
|
.allowUnknownOption()
|
|
10
15
|
.action(async (args = [], _, ctx) => {
|
|
11
|
-
await
|
|
16
|
+
await fs.rm(path.join(project.root, 'out'), {
|
|
17
|
+
recursive: true,
|
|
18
|
+
force: true,
|
|
19
|
+
});
|
|
12
20
|
await ctx.exec('next', ['build', ...args], EXEC_CONFIG);
|
|
13
21
|
});
|
|
14
22
|
|
package/subcommands/dev.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { DigigovCommand, resolveProject, logger } from '@digigov/cli/lib';
|
|
2
2
|
import { EXEC_CONFIG } from '../common.js';
|
|
3
3
|
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { fileURLToPath } from 'url';
|
|
6
|
-
|
|
7
4
|
const { digigov } = resolveProject();
|
|
8
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
5
|
|
|
10
6
|
const devCommand = new DigigovCommand('dev');
|
|
11
7
|
|
|
@@ -16,11 +12,11 @@ devCommand
|
|
|
16
12
|
.action(async (args = [], _, ctx) => {
|
|
17
13
|
logger.info('Starting development server...');
|
|
18
14
|
if (digigov && digigov.proxy) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
// The proxy server wraps its own Next dev instance and replaces
|
|
16
|
+
// `next dev` entirely; running both would bind two servers. It starts
|
|
17
|
+
// listening on import.
|
|
18
|
+
await import('../utils/proxy-server.js');
|
|
19
|
+
return;
|
|
24
20
|
}
|
|
25
21
|
await ctx.exec('next', ['dev', ...(args ?? [])], EXEC_CONFIG);
|
|
26
22
|
});
|
package/subcommands/export.js
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
import { DigigovCommand, resolveProject } from '@digigov/cli/lib';
|
|
2
5
|
import { EXEC_CONFIG } from '../common.js';
|
|
3
6
|
|
|
4
7
|
const exportCommand = new DigigovCommand('export');
|
|
5
8
|
|
|
9
|
+
const project = resolveProject();
|
|
10
|
+
|
|
11
|
+
// `next export` was removed in Next 14; `next build` itself emits `out/`
|
|
12
|
+
// because the shared config sets `output: 'export'` outside the dev phase.
|
|
13
|
+
// Kept as an alias of `build` so existing `digigov app export` setups work.
|
|
6
14
|
exportCommand
|
|
7
|
-
.argument('[...args]', 'Arguments to pass to the `next
|
|
15
|
+
.argument('[...args]', 'Arguments to pass to the `next build` command')
|
|
8
16
|
.helpOption(false)
|
|
9
17
|
.allowUnknownOption()
|
|
10
18
|
.action(async (args = [], _, ctx) => {
|
|
11
|
-
await
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
await fs.rm(path.join(project.root, 'out'), {
|
|
20
|
+
recursive: true,
|
|
21
|
+
force: true,
|
|
22
|
+
});
|
|
23
|
+
await ctx.exec('next', ['build', ...args], EXEC_CONFIG);
|
|
14
24
|
});
|
|
15
25
|
|
|
16
26
|
export default exportCommand;
|
package/subcommands/start.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
+
import { createRequire } from 'module';
|
|
2
3
|
|
|
3
|
-
import
|
|
4
|
+
import express from 'express';
|
|
5
|
+
import { PHASE_PRODUCTION_SERVER } from 'next/constants.js';
|
|
6
|
+
import { DigigovCommand, resolveProject, logger } from '@digigov/cli/lib';
|
|
4
7
|
import { EXEC_CONFIG } from '../common.js';
|
|
5
8
|
import path from 'path';
|
|
6
9
|
|
|
@@ -8,15 +11,60 @@ const startCommand = new DigigovCommand('start');
|
|
|
8
11
|
|
|
9
12
|
const project = resolveProject();
|
|
10
13
|
|
|
14
|
+
// `next start` does not support `output: 'export'`, so serve the exported
|
|
15
|
+
// site the same way production does (static files behind a web server).
|
|
11
16
|
startCommand
|
|
12
|
-
.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
.option(
|
|
18
|
+
'-p, --port <port>',
|
|
19
|
+
'Port to listen on',
|
|
20
|
+
process.env['PORT'] ?? '3000'
|
|
21
|
+
)
|
|
22
|
+
.option('-H, --hostname <hostname>', 'Hostname to bind to', '0.0.0.0')
|
|
23
|
+
.action(async (options, ctx) => {
|
|
24
|
+
const port = parseInt(options.port, 10);
|
|
25
|
+
if (Number.isNaN(port)) {
|
|
26
|
+
logger.error(`Invalid port: ${options.port}`);
|
|
27
|
+
process.exit(1);
|
|
18
28
|
}
|
|
19
|
-
|
|
29
|
+
const outDir = path.join(project.root, 'out');
|
|
30
|
+
if (!fs.existsSync(outDir)) {
|
|
31
|
+
await ctx.exec('next', ['build'], EXEC_CONFIG);
|
|
32
|
+
}
|
|
33
|
+
// The exported HTML references pages and assets under `basePath`, so the
|
|
34
|
+
// static site must be mounted there — as `next start` used to do.
|
|
35
|
+
const configPath = path.join(project.root, 'next.config.js');
|
|
36
|
+
let conf = {};
|
|
37
|
+
if (fs.existsSync(configPath)) {
|
|
38
|
+
const confModule = createRequire(configPath)(configPath);
|
|
39
|
+
conf =
|
|
40
|
+
typeof confModule === 'function'
|
|
41
|
+
? await confModule(PHASE_PRODUCTION_SERVER, { defaultConfig: {} })
|
|
42
|
+
: confModule;
|
|
43
|
+
}
|
|
44
|
+
const basePath = conf.basePath ?? '';
|
|
45
|
+
const server = express();
|
|
46
|
+
server.use(
|
|
47
|
+
basePath || '/',
|
|
48
|
+
express.static(outDir, { extensions: ['html'] })
|
|
49
|
+
);
|
|
50
|
+
server.use((_req, res) => {
|
|
51
|
+
const notFoundPage = path.join(outDir, '404.html');
|
|
52
|
+
if (fs.existsSync(notFoundPage)) {
|
|
53
|
+
res.status(404).sendFile(notFoundPage);
|
|
54
|
+
} else {
|
|
55
|
+
res.status(404).send('Not Found');
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
server
|
|
59
|
+
.listen(port, options.hostname, () => {
|
|
60
|
+
logger.info(
|
|
61
|
+
`Serving ${outDir} on http://${options.hostname}:${port}${basePath}`
|
|
62
|
+
);
|
|
63
|
+
})
|
|
64
|
+
.on('error', (err) => {
|
|
65
|
+
logger.error(`Unable to start server: ${err.message}`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
});
|
|
20
68
|
});
|
|
21
69
|
|
|
22
70
|
export default startCommand;
|
package/utils/proxy-server.js
CHANGED
|
@@ -2,8 +2,15 @@ import express from 'express';
|
|
|
2
2
|
import next from 'next';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { createRequire } from 'module';
|
|
5
|
+
import { createProxyMiddleware } from 'http-proxy-middleware';
|
|
6
|
+
// `next` publishes no `exports` map, so the subpath needs the explicit
|
|
7
|
+
// extension to resolve under ESM.
|
|
8
|
+
import {
|
|
9
|
+
PHASE_DEVELOPMENT_SERVER,
|
|
10
|
+
PHASE_PRODUCTION_SERVER,
|
|
11
|
+
} from 'next/constants.js';
|
|
5
12
|
|
|
6
|
-
import { resolveProject } from '@digigov/cli/lib';
|
|
13
|
+
import { resolveProject, logger } from '@digigov/cli/lib';
|
|
7
14
|
|
|
8
15
|
const port = parseInt(process.env['PORT'] ?? '3000', 10);
|
|
9
16
|
const isDev = process.env.NODE_ENV !== 'production';
|
|
@@ -12,37 +19,73 @@ const project = resolveProject();
|
|
|
12
19
|
const devProxy = project.digigov.proxy;
|
|
13
20
|
|
|
14
21
|
const require = createRequire(import.meta.url);
|
|
15
|
-
const
|
|
22
|
+
const confModule = require(path.join(project.root, 'next.config.js'));
|
|
16
23
|
|
|
17
|
-
|
|
24
|
+
// nextrc exports a phase → config function; `next({ conf })` spreads the
|
|
25
|
+
// value without invoking it, so the phase must be resolved here.
|
|
26
|
+
const conf =
|
|
27
|
+
typeof confModule === 'function'
|
|
28
|
+
? await confModule(
|
|
29
|
+
isDev ? PHASE_DEVELOPMENT_SERVER : PHASE_PRODUCTION_SERVER,
|
|
30
|
+
{ defaultConfig: {} }
|
|
31
|
+
)
|
|
32
|
+
: confModule;
|
|
33
|
+
|
|
34
|
+
// Next 16 removed the CJS `.default` interop export: `module.exports` itself is
|
|
35
|
+
// the server factory (on 13 too), but the d.ts still types the module as a
|
|
36
|
+
// non-callable namespace — hence the cast.
|
|
37
|
+
const createNextServer = /** @type {import('next')['default']} */ (
|
|
38
|
+
/** @type {unknown} */ (next)
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const app = createNextServer({
|
|
18
42
|
dir: project.root, // base directory where everything is, could move to src later
|
|
19
43
|
dev: isDev,
|
|
20
44
|
conf,
|
|
21
45
|
});
|
|
22
46
|
const handle = app.getRequestHandler();
|
|
23
47
|
|
|
24
|
-
|
|
25
|
-
.prepare()
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
try {
|
|
49
|
+
await app.prepare();
|
|
50
|
+
const server = express();
|
|
51
|
+
|
|
52
|
+
// Set up the proxy.
|
|
53
|
+
const wsProxies = [];
|
|
54
|
+
if (isDev && devProxy) {
|
|
55
|
+
for (const [pathFilter, options] of Object.entries(devProxy)) {
|
|
56
|
+
const proxy = createProxyMiddleware({ pathFilter, ...options });
|
|
57
|
+
server.use(proxy);
|
|
58
|
+
if (options.ws) {
|
|
59
|
+
wsProxies.push(proxy);
|
|
60
|
+
}
|
|
35
61
|
}
|
|
62
|
+
}
|
|
36
63
|
|
|
37
|
-
|
|
38
|
-
|
|
64
|
+
// Default catch-all handler to allow Next.js to handle all other routes
|
|
65
|
+
server.use((req, res) => handle(req, res));
|
|
39
66
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
43
|
-
return;
|
|
44
|
-
})
|
|
45
|
-
.catch((err) => {
|
|
46
|
-
console.log('An error occurred, unable to start the server');
|
|
47
|
-
console.log(err);
|
|
67
|
+
const httpServer = server.listen(port, () => {
|
|
68
|
+
logger.info(`Ready on port ${port}`);
|
|
48
69
|
});
|
|
70
|
+
|
|
71
|
+
// Next and http-proxy-middleware each register their own `upgrade` listener,
|
|
72
|
+
// but only lazily — on the first HTTP request they see. A websocket that
|
|
73
|
+
// connects right after a restart, before any HTTP request (e.g. an open tab
|
|
74
|
+
// reconnecting through a `ws: true` proxy), finds no listener and hangs.
|
|
75
|
+
// Arm the ws proxies up front: `upgrade` checks its own pathFilter and
|
|
76
|
+
// no-ops once the proxy's internal listener exists, so nothing is handled
|
|
77
|
+
// twice. Next needs no arming — its HMR socket always follows a page load.
|
|
78
|
+
if (wsProxies.length > 0) {
|
|
79
|
+
httpServer.on('upgrade', (req, socket, head) => {
|
|
80
|
+
for (const proxy of wsProxies) {
|
|
81
|
+
proxy.upgrade(req, socket, head);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
} catch (err) {
|
|
86
|
+
logger.error(
|
|
87
|
+
'Unable to start the server:',
|
|
88
|
+
err instanceof Error ? err.stack : err
|
|
89
|
+
);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|