Repository navigation
Expand file tree
/
Copy pathvite.config.ts
More file actions
67 lines (62 loc) · 2.55 KB
/
Copy pathvite.config.ts
File metadata and controls
67 lines (62 loc) · 2.55 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
import { createLogger, defineConfig, type ProxyOptions } from 'vite';
import react from '@vitejs/plugin-react';
/**
* The API compiles for a few seconds while Vite is ready in under one, so polls from an
* already-open page are refused until it lands.
*
* Vite answers a refused proxy request by printing an ECONNREFUSED stack trace and hanging the
* request, which makes `make dev` read like it crashed on boot. Neither is true: the API is
* simply still starting. Say so in one line, and hand the page a 503 its poll can survive.
*/
const api = (): ProxyOptions => ({
target: 'http://localhost:8080',
configure: (proxy) => {
proxy.on('error', (error, _request, response) => {
const starting = (error as NodeJS.ErrnoException).code === 'ECONNREFUSED';
// A websocket upgrade hands back a raw socket, which cannot be answered with a status.
if (!('writeHead' in response)) return;
if (response.headersSent) return;
response.writeHead(starting ? 503 : 502, { 'content-type': 'application/json' });
response.end(JSON.stringify({ error: starting ? 'api_starting' : 'api_unreachable' }));
});
},
});
/**
* Vite's proxy stack trace is printed from its logger, not from the `error` handler above —
* which runs first and has already answered the request. Drop that one message and nothing
* else: a proxy error the page has recovered from is not news, but every other error is.
*/
const logger = createLogger();
const inherited = logger.error.bind(logger);
logger.error = (message, options) => {
if (message.includes('http proxy error')) {
if (message.includes('ECONNREFUSED')) inherited('[api] not up yet — the page will retry');
return;
}
inherited(message, options);
};
export default defineConfig({
root: 'src/client',
publicDir: '../../public',
plugins: [react()],
build: {
outDir: '../../dist/client',
emptyOutDir: true,
sourcemap: true,
},
customLogger: logger,
server: {
// Pinned and strict: the default range is a crowded neighbourhood, and a dev server
// that silently lands on another port takes the E2E suite's baseURL with it.
port: 5273,
strictPort: true,
proxy: {
// Anchored, because a bare '/api' prefix also matches the client's own `/api.ts`
// module request and proxies it to the backend — where it 404s, and the SPA never
// boots. Only real API routes go through.
// The card needs no rule: it is fetched straight from the public bucket, by its absolute
// URL, in dev exactly as in production.
'^/api/': api(),
},
},
});