aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/frontend/vite.config.ts
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/frontend/vite.config.ts')
-rw-r--r--subprojects/frontend/vite.config.ts183
1 files changed, 33 insertions, 150 deletions
diff --git a/subprojects/frontend/vite.config.ts b/subprojects/frontend/vite.config.ts
index 08b3db0c..cd9993cc 100644
--- a/subprojects/frontend/vite.config.ts
+++ b/subprojects/frontend/vite.config.ts
@@ -1,160 +1,61 @@
1import { setDefaultResultOrder } from 'node:dns';
2import { readFileSync } from 'node:fs';
3import path from 'node:path'; 1import path from 'node:path';
4import { fileURLToPath } from 'node:url'; 2import { fileURLToPath } from 'node:url';
5 3
6import { lezer } from '@lezer/generator/rollup'; 4import { lezer } from '@lezer/generator/rollup';
7import react from '@vitejs/plugin-react-swc'; 5import react from '@vitejs/plugin-react-swc';
8import { minify } from 'html-minifier-terser'; 6import { defineConfig, type UserConfig as ViteConfig } from 'vite';
9import { defineConfig, type PluginOption } from 'vite';
10import injectPreload from 'vite-plugin-inject-preload';
11import { VitePWA } from 'vite-plugin-pwa'; 7import { VitePWA } from 'vite-plugin-pwa';
12 8
13setDefaultResultOrder('verbatim'); 9import { CONFIG_ENDPOINT } from './config/backendConfigVitePlugin';
10import detectDevModeOptions, {
11 API_ENDPOINT,
12} from './config/detectDevModeOptions';
13import fetchPackageMetadata from './config/fetchPackageMetadata';
14import manifest from './config/manifest';
15import minifyHTMLVitePlugin from './config/minifyHTMLVitePlugin';
16import preloadFontsVitePlugin from './config/preloadFontsVitePlugin';
14 17
15const thisDir = path.dirname(fileURLToPath(import.meta.url)); 18const thisDir = path.dirname(fileURLToPath(import.meta.url));
16 19
17const mode = process.env['MODE'] || 'development'; 20const { mode, isDevelopment, devModePlugins, serverOptions } =
18const isDevelopment = mode === 'development'; 21 detectDevModeOptions();
19process.env['NODE_ENV'] ??= mode;
20
21function portNumberOrElse(envName: string, fallback: number): number {
22 const value = process.env[envName];
23 return value ? parseInt(value, 10) : fallback;
24}
25 22
26const listenHost = process.env['LISTEN_HOST'] || 'localhost'; 23process.env['NODE_ENV'] ??= mode;
27const listenPort = portNumberOrElse('LISTEN_PORT', 1313);
28const apiHost = process.env['API_HOST'] || '127.0.0.1';
29const apiPort = portNumberOrElse('API_PORT', 1312);
30const apiSecure = apiPort === 443;
31const publicHost = process.env['PUBLIC_HOST'] || listenHost;
32const publicPort = portNumberOrElse('PUBLIC_PORT', listenPort);
33const publicSecure = publicPort === 443;
34 24
35const { name: packageName, version: packageVersion } = JSON.parse( 25const fontsGlob = [
36 readFileSync(path.join(thisDir, 'package.json'), 'utf8'), 26 'inter-latin-variable-wghtOnly-normal-*.woff2',
37) as { name: string; version: string }; 27 'jetbrains-mono-latin-variable-wghtOnly-{normal,italic}-*.woff2',
38process.env['VITE_PACKAGE_NAME'] ??= packageName; 28];
39process.env['VITE_PACKAGE_VERSION'] ??= packageVersion;
40 29
41const minifyPlugin: PluginOption = { 30const viteConfig: ViteConfig = {
42 name: 'minify-html',
43 enforce: 'post',
44 async transformIndexHtml(html) {
45 if (isDevelopment) {
46 return html;
47 }
48 return minify(html, {
49 collapseWhitespace: true,
50 collapseBooleanAttributes: true,
51 minifyCSS: true,
52 removeComments: true,
53 removeAttributeQuotes: true,
54 removeRedundantAttributes: true,
55 sortAttributes: true,
56 });
57 },
58};
59
60const backendConfigPlugin: PluginOption = {
61 name: 'backend-config',
62 configureServer(server) {
63 const protocol = publicSecure ? 'wss' : 'ws';
64 const webSocketURL = `${protocol}://${publicHost}:${publicPort}/xtext-service`;
65 const config = JSON.stringify({ webSocketURL });
66 server.middlewares.use((req, res, next) => {
67 if (req.url === '/config.json') {
68 res.setHeader('Content-Type', 'application/json');
69 res.end(config);
70 } else {
71 next();
72 }
73 });
74 },
75};
76
77export default defineConfig({
78 logLevel: 'info', 31 logLevel: 'info',
79 mode, 32 mode,
80 root: thisDir, 33 root: thisDir,
81 cacheDir: path.join(thisDir, 'build/vite/cache'), 34 cacheDir: path.join(thisDir, 'build/vite/cache'),
82 plugins: [ 35 plugins: [
83 minifyPlugin,
84 backendConfigPlugin,
85 react(), 36 react(),
86 injectPreload({
87 files: [
88 {
89 match:
90 /(?:inter-latin-variable-wghtOnly-normal|jetbrains-mono-latin-variable-wghtOnly-(?:italic|normal)).+\.woff2$/,
91 attributes: {
92 type: 'font/woff2',
93 as: 'font',
94 crossorigin: 'anonymous',
95 },
96 },
97 ],
98 }),
99 lezer(), 37 lezer(),
38 preloadFontsVitePlugin(fontsGlob),
39 minifyHTMLVitePlugin(),
100 VitePWA({ 40 VitePWA({
101 strategies: 'generateSW', 41 strategies: 'generateSW',
102 registerType: 'prompt', 42 registerType: 'prompt',
103 injectRegister: null, 43 injectRegister: null,
104 workbox: { 44 workbox: {
105 globPatterns: [ 45 globPatterns: ['**/*.{css,html,js}', ...fontsGlob],
106 '**/*.{css,html,js}',
107 'inter-latin-variable-wghtOnly-normal-*.woff2',
108 'jetbrains-mono-latin-variable-wghtOnly-{normal,italic}-*.woff2',
109 ],
110 dontCacheBustURLsMatching: /\.(?:css|js|woff2?)$/, 46 dontCacheBustURLsMatching: /\.(?:css|js|woff2?)$/,
111 navigateFallbackDenylist: [/^\/xtext-service/], 47 navigateFallbackDenylist: [new RegExp(`^\\/${API_ENDPOINT}$`)],
112 runtimeCaching: [ 48 runtimeCaching: [
113 { 49 {
114 urlPattern: 'config.json', 50 urlPattern: CONFIG_ENDPOINT,
115 handler: 'StaleWhileRevalidate', 51 handler: 'StaleWhileRevalidate',
116 }, 52 },
117 ], 53 ],
118 }, 54 },
119 includeAssets: ['apple-touch-icon.png', 'favicon.svg', 'mask-icon.svg'], 55 includeAssets: ['apple-touch-icon.png', 'favicon.svg'],
120 manifest: { 56 manifest,
121 lang: 'en-US',
122 name: 'Refinery',
123 short_name: 'Refinery',
124 description:
125 'An efficient graph sovler for generating well-formed models',
126 theme_color: '#f5f5f5',
127 display_override: ['window-controls-overlay'],
128 display: 'standalone',
129 background_color: '#21252b',
130 icons: [
131 {
132 src: 'icon-192x192.png',
133 sizes: '192x192',
134 type: 'image/png',
135 purpose: 'any maskable',
136 },
137 {
138 src: 'icon-512x512.png',
139 sizes: '512x512',
140 type: 'image/png',
141 purpose: 'any maskable',
142 },
143 {
144 src: 'icon-any.svg',
145 sizes: 'any',
146 type: 'image/svg+xml',
147 purpose: 'any maskable',
148 },
149 {
150 src: 'mask-icon.svg',
151 sizes: 'any',
152 type: 'image/svg+xml',
153 purpose: 'monochrome',
154 },
155 ],
156 },
157 }), 57 }),
58 ...devModePlugins,
158 ], 59 ],
159 base: '', 60 base: '',
160 define: { 61 define: {
@@ -162,36 +63,18 @@ export default defineConfig({
162 }, 63 },
163 build: { 64 build: {
164 assetsDir: '.', 65 assetsDir: '.',
165 // If we don't control inlining manually, 66 // If we don't control inlining manually, web fonts will be randomly inlined
166 // web fonts will randomly get inlined into the CSS, degrading performance. 67 // into the CSS, which degrades performance.
167 assetsInlineLimit: 0, 68 assetsInlineLimit: 0,
168 outDir: path.join('build/vite', mode), 69 outDir: path.join('build/vite', mode),
169 emptyOutDir: true, 70 emptyOutDir: true,
170 sourcemap: isDevelopment, 71 sourcemap: isDevelopment,
171 minify: !isDevelopment, 72 minify: !isDevelopment,
172 }, 73 },
173 server: { 74 server: serverOptions,
174 host: listenHost, 75};
175 port: listenPort, 76
176 strictPort: true, 77export default defineConfig(async () => {
177 headers: { 78 await fetchPackageMetadata(thisDir);
178 // Enable strict origin isolation, see e.g., 79 return viteConfig;
179 // https://github.com/vitejs/vite/issues/3909#issuecomment-1065893956
180 'Cross-Origin-Opener-Policy': 'same-origin',
181 'Cross-Origin-Embedder-Policy': 'require-corp',
182 'Cross-Origin-Resource-Policy': 'cross-origin',
183 },
184 proxy: {
185 '/xtext-service': {
186 target: `${apiSecure ? 'https' : 'http'}://${apiHost}:${apiPort}`,
187 ws: true,
188 secure: apiSecure,
189 },
190 },
191 hmr: {
192 host: publicHost,
193 clientPort: publicPort,
194 path: '/vite',
195 },
196 },
197}); 80});