aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/frontend
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-12-09 23:49:07 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-12-11 12:18:43 +0100
commit280a3fab74348697429b7bab56b3436968113d79 (patch)
treee1603153db18f7f35c1bcceb03462409f75002db /subprojects/frontend
parentrefactor(frontend): lazy load XtextClient (diff)
downloadrefinery-280a3fab74348697429b7bab56b3436968113d79.tar.gz
refinery-280a3fab74348697429b7bab56b3436968113d79.tar.zst
refinery-280a3fab74348697429b7bab56b3436968113d79.zip
refactor(frontend): split vite config
Also introduces tsconfig.shared.json to keep track of source files used both and build time and in the browser.
Diffstat (limited to 'subprojects/frontend')
-rw-r--r--subprojects/frontend/.eslintrc.cjs8
-rw-r--r--subprojects/frontend/config/backendConfigVitePlugin.ts27
-rw-r--r--subprojects/frontend/config/detectDevModeOptions.ts94
-rw-r--r--subprojects/frontend/config/fetchPackageMetadata.ts20
-rw-r--r--subprojects/frontend/config/manifest.ts40
-rw-r--r--subprojects/frontend/config/minifyHTMLVitePlugin.ts24
-rw-r--r--subprojects/frontend/config/preloadFontsVitePlugin.ts24
-rw-r--r--subprojects/frontend/package.json5
-rw-r--r--subprojects/frontend/src/xtext/BackendConfig.ts13
-rw-r--r--subprojects/frontend/src/xtext/fetchBackendConfig.ts12
-rw-r--r--subprojects/frontend/tsconfig.json8
-rw-r--r--subprojects/frontend/tsconfig.node.json4
-rw-r--r--subprojects/frontend/tsconfig.shared.json13
-rw-r--r--subprojects/frontend/vite.config.ts183
14 files changed, 310 insertions, 165 deletions
diff --git a/subprojects/frontend/.eslintrc.cjs b/subprojects/frontend/.eslintrc.cjs
index eadd3fb4..8a7b474a 100644
--- a/subprojects/frontend/.eslintrc.cjs
+++ b/subprojects/frontend/.eslintrc.cjs
@@ -4,6 +4,7 @@ const path = require('node:path');
4const project = [ 4const project = [
5 path.join(__dirname, 'tsconfig.json'), 5 path.join(__dirname, 'tsconfig.json'),
6 path.join(__dirname, 'tsconfig.node.json'), 6 path.join(__dirname, 'tsconfig.node.json'),
7 path.join(__dirname, 'tsconfig.shared.json'),
7]; 8];
8 9
9/** @type {import('eslint').Linter.Config} */ 10/** @type {import('eslint').Linter.Config} */
@@ -86,7 +87,12 @@ module.exports = {
86 }, 87 },
87 }, 88 },
88 { 89 {
89 files: ['.eslintrc.cjs', 'prettier.config.cjs', 'vite.config.ts'], 90 files: [
91 '.eslintrc.cjs',
92 'config/*.ts',
93 'prettier.config.cjs',
94 'vite.config.ts',
95 ],
90 env: { 96 env: {
91 browser: false, 97 browser: false,
92 node: true, 98 node: true,
diff --git a/subprojects/frontend/config/backendConfigVitePlugin.ts b/subprojects/frontend/config/backendConfigVitePlugin.ts
new file mode 100644
index 00000000..7a6bc3db
--- /dev/null
+++ b/subprojects/frontend/config/backendConfigVitePlugin.ts
@@ -0,0 +1,27 @@
1import type { PluginOption } from 'vite';
2
3import type BackendConfig from '../src/xtext/BackendConfig';
4import { ENDPOINT } from '../src/xtext/BackendConfig';
5
6export default function backendConfigVitePlugin(
7 backendConfig: BackendConfig,
8): PluginOption {
9 return {
10 name: 'backend-config',
11 apply: 'serve',
12 configureServer(server) {
13 const config = JSON.stringify(backendConfig);
14 server.middlewares.use((req, res, next) => {
15 if (req.url === `/${ENDPOINT}`) {
16 res.setHeader('Content-Type', 'application/json');
17 res.end(config);
18 } else {
19 next();
20 }
21 });
22 },
23 };
24}
25
26export type { default as BackendConfig } from '../src/xtext/BackendConfig';
27export { ENDPOINT as CONFIG_ENDPOINT } from '../src/xtext/BackendConfig';
diff --git a/subprojects/frontend/config/detectDevModeOptions.ts b/subprojects/frontend/config/detectDevModeOptions.ts
new file mode 100644
index 00000000..b3696241
--- /dev/null
+++ b/subprojects/frontend/config/detectDevModeOptions.ts
@@ -0,0 +1,94 @@
1import type { PluginOption, ServerOptions } from 'vite';
2
3import backendConfigVitePlugin, {
4 type BackendConfig,
5} from './backendConfigVitePlugin';
6
7export const API_ENDPOINT = 'xtext-service';
8
9export interface DevModeOptions {
10 mode: string;
11 isDevelopment: boolean;
12 devModePlugins: PluginOption[];
13 serverOptions: ServerOptions;
14}
15
16interface ListenOptions {
17 host: string;
18 port: number;
19 secure: boolean;
20}
21
22function detectListenOptions(
23 name: string,
24 fallbackHost: string,
25 fallbackPort: number,
26): ListenOptions {
27 const host = process.env[`${name}_HOST`] ?? fallbackHost;
28 const rawPort = process.env[`${name}_PORT`];
29 const port = rawPort === undefined ? fallbackPort : parseInt(rawPort, 10);
30 const secure = port === 443;
31 return { host, port, secure };
32}
33
34function listenURL(
35 { host, port, secure }: ListenOptions,
36 protocol = 'http',
37): string {
38 return `${secure ? `${protocol}s` : protocol}://${host}:${port}`;
39}
40
41export default function detectDevModeOptions(): DevModeOptions {
42 const mode = process.env['MODE'] || 'development';
43 const isDevelopment = mode === 'development';
44
45 if (!isDevelopment) {
46 return {
47 mode,
48 isDevelopment,
49 devModePlugins: [],
50 serverOptions: {},
51 };
52 }
53
54 const listen = detectListenOptions('LISTEN', 'localhost', 1313);
55 // Make sure we always use IPv4 to connect to the backend,
56 // because it doesn't listen on IPv6.
57 const api = detectListenOptions('API', '127.0.0.1', 1312);
58 const publicAddress = detectListenOptions('PUBLIC', listen.host, listen.port);
59
60 const backendConfig: BackendConfig = {
61 webSocketURL: `${listenURL(publicAddress, 'ws')}/${API_ENDPOINT}`,
62 };
63
64 return {
65 mode,
66 isDevelopment,
67 devModePlugins: [backendConfigVitePlugin(backendConfig)],
68 serverOptions: {
69 host: listen.host,
70 port: listen.port,
71 strictPort: true,
72 https: listen.secure,
73 headers: {
74 // Enable strict origin isolation, see e.g.,
75 // https://github.com/vitejs/vite/issues/3909#issuecomment-1065893956
76 'Cross-Origin-Opener-Policy': 'same-origin',
77 'Cross-Origin-Embedder-Policy': 'require-corp',
78 'Cross-Origin-Resource-Policy': 'cross-origin',
79 },
80 proxy: {
81 [`/${API_ENDPOINT}`]: {
82 target: listenURL(api),
83 ws: true,
84 secure: api.secure,
85 },
86 },
87 hmr: {
88 host: publicAddress.host,
89 clientPort: publicAddress.port,
90 path: '/vite',
91 },
92 },
93 };
94}
diff --git a/subprojects/frontend/config/fetchPackageMetadata.ts b/subprojects/frontend/config/fetchPackageMetadata.ts
new file mode 100644
index 00000000..50807b03
--- /dev/null
+++ b/subprojects/frontend/config/fetchPackageMetadata.ts
@@ -0,0 +1,20 @@
1import { readFile } from 'node:fs/promises';
2import path from 'node:path';
3
4import z from 'zod';
5
6const PackageInfo = z.object({
7 name: z.string().min(1),
8 version: z.string().min(1),
9});
10
11export default async function fetchPackageMetadata(
12 thisDir: string,
13): Promise<void> {
14 const contents = await readFile(path.join(thisDir, 'package.json'), 'utf8');
15 const { name: packageName, version: packageVersion } = PackageInfo.parse(
16 JSON.parse(contents),
17 );
18 process.env['VITE_PACKAGE_NAME'] ??= packageName;
19 process.env['VITE_PACKAGE_VERSION'] ??= packageVersion;
20}
diff --git a/subprojects/frontend/config/manifest.ts b/subprojects/frontend/config/manifest.ts
new file mode 100644
index 00000000..3cec777c
--- /dev/null
+++ b/subprojects/frontend/config/manifest.ts
@@ -0,0 +1,40 @@
1import type { ManifestOptions } from 'vite-plugin-pwa';
2
3const manifest: Partial<ManifestOptions> = {
4 lang: 'en-US',
5 name: 'Refinery',
6 short_name: 'Refinery',
7 description: 'An efficient graph sovler for generating well-formed models',
8 theme_color: '#f5f5f5',
9 display_override: ['window-controls-overlay'],
10 display: 'standalone',
11 background_color: '#21252b',
12 icons: [
13 {
14 src: 'icon-192x192.png',
15 sizes: '192x192',
16 type: 'image/png',
17 purpose: 'any maskable',
18 },
19 {
20 src: 'icon-512x512.png',
21 sizes: '512x512',
22 type: 'image/png',
23 purpose: 'any maskable',
24 },
25 {
26 src: 'icon-any.svg',
27 sizes: 'any',
28 type: 'image/svg+xml',
29 purpose: 'any maskable',
30 },
31 {
32 src: 'mask-icon.svg',
33 sizes: 'any',
34 type: 'image/svg+xml',
35 purpose: 'monochrome',
36 },
37 ],
38};
39
40export default manifest;
diff --git a/subprojects/frontend/config/minifyHTMLVitePlugin.ts b/subprojects/frontend/config/minifyHTMLVitePlugin.ts
new file mode 100644
index 00000000..18336d4d
--- /dev/null
+++ b/subprojects/frontend/config/minifyHTMLVitePlugin.ts
@@ -0,0 +1,24 @@
1import { minify, type Options as TerserOptions } from 'html-minifier-terser';
2import type { PluginOption } from 'vite';
3
4export default function minifyHTMLVitePlugin(
5 options?: TerserOptions | undefined,
6): PluginOption {
7 return {
8 name: 'minify-html',
9 apply: 'build',
10 enforce: 'post',
11 transformIndexHtml(html) {
12 return minify(html, {
13 collapseWhitespace: true,
14 collapseBooleanAttributes: true,
15 minifyCSS: true,
16 removeComments: true,
17 removeAttributeQuotes: true,
18 removeRedundantAttributes: true,
19 sortAttributes: true,
20 ...(options ?? {}),
21 });
22 },
23 };
24}
diff --git a/subprojects/frontend/config/preloadFontsVitePlugin.ts b/subprojects/frontend/config/preloadFontsVitePlugin.ts
new file mode 100644
index 00000000..bc6f8eaf
--- /dev/null
+++ b/subprojects/frontend/config/preloadFontsVitePlugin.ts
@@ -0,0 +1,24 @@
1import micromatch from 'micromatch';
2import type { PluginOption } from 'vite';
3
4export default function preloadFontsVitePlugin(
5 fontsGlob: string | string[],
6): PluginOption {
7 return {
8 name: 'refinery-preload-fonts',
9 apply: 'build',
10 enforce: 'post',
11 transformIndexHtml(_html, { bundle }) {
12 return micromatch(Object.keys(bundle ?? {}), fontsGlob).map((href) => ({
13 tag: 'link',
14 attrs: {
15 href,
16 rel: 'preload',
17 type: 'font/woff2',
18 as: 'font',
19 crossorigin: 'anonymous',
20 },
21 }));
22 },
23 };
24}
diff --git a/subprojects/frontend/package.json b/subprojects/frontend/package.json
index db5d3f68..a826755d 100644
--- a/subprojects/frontend/package.json
+++ b/subprojects/frontend/package.json
@@ -7,7 +7,7 @@
7 "build": "cross-env MODE=production vite build", 7 "build": "cross-env MODE=production vite build",
8 "serve": "cross-env MODE=development vite serve", 8 "serve": "cross-env MODE=development vite serve",
9 "typegen": "xstate typegen \"src/**/*.ts?(x)\"", 9 "typegen": "xstate typegen \"src/**/*.ts?(x)\"",
10 "typecheck": "tsc -p tsconfig.node.json && tsc -p tsconfig.json", 10 "typecheck": "tsc -p tsconfig.shared.json && tsc -p tsconfig.node.json && tsc -p tsconfig.json",
11 "lint": "eslint .", 11 "lint": "eslint .",
12 "lint:ci": "eslint -f json -o build/eslint.json .", 12 "lint:ci": "eslint -f json -o build/eslint.json .",
13 "lint:fix": "yarn run lint --fix" 13 "lint:fix": "yarn run lint --fix"
@@ -62,6 +62,7 @@
62 "@types/eslint": "^8.4.10", 62 "@types/eslint": "^8.4.10",
63 "@types/html-minifier-terser": "^7.0.0", 63 "@types/html-minifier-terser": "^7.0.0",
64 "@types/lodash-es": "^4.17.6", 64 "@types/lodash-es": "^4.17.6",
65 "@types/micromatch": "^4.0.2",
65 "@types/ms": "^0.7.31", 66 "@types/ms": "^0.7.31",
66 "@types/node": "^18.11.13", 67 "@types/node": "^18.11.13",
67 "@types/prettier": "^2.7.1", 68 "@types/prettier": "^2.7.1",
@@ -83,10 +84,10 @@
83 "eslint-plugin-react": "^7.31.11", 84 "eslint-plugin-react": "^7.31.11",
84 "eslint-plugin-react-hooks": "^4.6.0", 85 "eslint-plugin-react-hooks": "^4.6.0",
85 "html-minifier-terser": "^7.1.0", 86 "html-minifier-terser": "^7.1.0",
87 "micromatch": "^4.0.5",
86 "prettier": "^2.8.1", 88 "prettier": "^2.8.1",
87 "typescript": "4.9.3", 89 "typescript": "4.9.3",
88 "vite": "^4.0.0", 90 "vite": "^4.0.0",
89 "vite-plugin-inject-preload": "^1.1.1",
90 "vite-plugin-pwa": "^0.13.3", 91 "vite-plugin-pwa": "^0.13.3",
91 "workbox-window": "^6.5.4" 92 "workbox-window": "^6.5.4"
92 } 93 }
diff --git a/subprojects/frontend/src/xtext/BackendConfig.ts b/subprojects/frontend/src/xtext/BackendConfig.ts
new file mode 100644
index 00000000..41737c0b
--- /dev/null
+++ b/subprojects/frontend/src/xtext/BackendConfig.ts
@@ -0,0 +1,13 @@
1/* eslint-disable @typescript-eslint/no-redeclare -- Declare types with their companion objects */
2
3import { z } from 'zod';
4
5export const ENDPOINT = 'config.json';
6
7const BackendConfig = z.object({
8 webSocketURL: z.string().url(),
9});
10
11type BackendConfig = z.infer<typeof BackendConfig>;
12
13export default BackendConfig;
diff --git a/subprojects/frontend/src/xtext/fetchBackendConfig.ts b/subprojects/frontend/src/xtext/fetchBackendConfig.ts
index f8087a70..15e976d8 100644
--- a/subprojects/frontend/src/xtext/fetchBackendConfig.ts
+++ b/subprojects/frontend/src/xtext/fetchBackendConfig.ts
@@ -1,15 +1,7 @@
1/* eslint-disable @typescript-eslint/no-redeclare -- Declare types with their companion objects */ 1import BackendConfig, { ENDPOINT } from './BackendConfig';
2
3import { z } from 'zod';
4
5export const BackendConfig = z.object({
6 webSocketURL: z.string().url(),
7});
8
9export type BackendConfig = z.infer<typeof BackendConfig>;
10 2
11export default async function fetchBackendConfig(): Promise<BackendConfig> { 3export default async function fetchBackendConfig(): Promise<BackendConfig> {
12 const configURL = `${import.meta.env.BASE_URL}config.json`; 4 const configURL = `${import.meta.env.BASE_URL}${ENDPOINT}`;
13 const response = await fetch(configURL); 5 const response = await fetch(configURL);
14 const rawConfig = (await response.json()) as unknown; 6 const rawConfig = (await response.json()) as unknown;
15 return BackendConfig.parse(rawConfig); 7 return BackendConfig.parse(rawConfig);
diff --git a/subprojects/frontend/tsconfig.json b/subprojects/frontend/tsconfig.json
index 0dccec40..35abd789 100644
--- a/subprojects/frontend/tsconfig.json
+++ b/subprojects/frontend/tsconfig.json
@@ -10,8 +10,12 @@
10 "src", 10 "src",
11 "types" 11 "types"
12 ], 12 ],
13 "exclude": ["types/node"], 13 "exclude": [
14 "src/xtext/BackendConfig.ts",
15 "types/node"
16 ],
14 "references": [ 17 "references": [
15 { "path": "./tsconfig.node.json" } 18 { "path": "./tsconfig.node.json" },
19 { "path": "./tsconfig.shared.json" }
16 ] 20 ]
17} 21}
diff --git a/subprojects/frontend/tsconfig.node.json b/subprojects/frontend/tsconfig.node.json
index c4539dbc..f4908bcb 100644
--- a/subprojects/frontend/tsconfig.node.json
+++ b/subprojects/frontend/tsconfig.node.json
@@ -9,8 +9,12 @@
9 }, 9 },
10 "include": [ 10 "include": [
11 ".eslintrc.cjs", 11 ".eslintrc.cjs",
12 "config/*.ts",
12 "prettier.config.cjs", 13 "prettier.config.cjs",
13 "types/node", 14 "types/node",
14 "vite.config.ts" 15 "vite.config.ts"
16 ],
17 "references": [
18 { "path": "./tsconfig.shared.json" }
15 ] 19 ]
16} 20}
diff --git a/subprojects/frontend/tsconfig.shared.json b/subprojects/frontend/tsconfig.shared.json
new file mode 100644
index 00000000..b7e1de55
--- /dev/null
+++ b/subprojects/frontend/tsconfig.shared.json
@@ -0,0 +1,13 @@
1{
2 "extends": "./tsconfig.base.json",
3 "compilerOptions": {
4 "composite": true,
5 "lib": ["ES2022"],
6 "types": [],
7 "emitDeclarationOnly": true,
8 "outDir": "build/typescript"
9 },
10 "include": [
11 "src/xtext/BackendConfig.ts",
12 ]
13}
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});