-
Notifications
You must be signed in to change notification settings - Fork 21
/
vite.config.js
238 lines (215 loc) · 6.15 KB
/
vite.config.js
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
import replace from '@rollup/plugin-replace';
import { svelte } from '@sveltejs/vite-plugin-svelte'
import glslify from 'rollup-plugin-glslify';
import license from 'rollup-plugin-license';
import { promisify } from 'util';
import { exec as _exec } from 'child_process';
const exec = promisify(_exec);
import {
access,
appendFile,
copyFile,
mkdir,
readdir,
readFile,
stat,
symlink,
writeFile,
} from 'fs/promises';
import { platform } from 'os';
import { resolve } from 'path';
import dedent from 'dedent';
// print dev server address as `localhost` for Node.js versions < 17
import dns from 'dns'
dns.setDefaultResultOrder('verbatim');
// https://vitejs.dev/config/
export default async ({ mode }) => {
const production = mode === 'production';
const commit = await (async () => {
try {
return (await exec('git rev-parse --short HEAD')).stdout.trim();
} catch {
return '???????';
}
})();
const htmlComment = dedent`
<!--
Fluid Earth (fev2r)
=====================================================================
Commit: ${commit}
Generated: ${(new Date).toISOString()}
License: /legal/LICENSE
Licenses of third-party libraries: /legal/THIRD_PARTY
Modifications to third-party libraries: /legal/MODIFICATIONS
-->
`;
// If local data files don't exist, use the remote ones
const dataProxy = await (async () => {
try {
await access('./public/tera');
return false;
} catch {
return { '/tera': 'https://fluid-earth.byrd.osu.edu' };
}
})();
const plugins = [
replace({
// Hide developer-only tools in production
__production__: JSON.stringify(production),
// Set initial date based on forecast instead of current time
__using_local_data_files__: JSON.stringify(!dataProxy),
// Fix local file paths for dev environments on Windows
__windows__: JSON.stringify(platform() === 'win32'),
// Allow environments (e.g. Cloudflare Pages) to specify a custom prefix
// for fetcher.js requests
__fev2r_api__: JSON.stringify(process.env.FEV2R_API ?? ''),
// Plugin option to avoid assignment errors
preventAssignment: true,
}),
// For .svelte files
svelte({
preprocess: [customPreprocess()],
}),
// For GLSL .vert and .frag files
glslify({
compress: production,
}),
// Add build and license info to index.html
commentHtml(htmlComment),
];
const productionOnlyPlugins = [
license({
thirdParty: {
output: {
file: 'dist/legal/THIRD_PARTY'
},
},
}),
copyLicenseAndPatches(),
// symlink public/tera instead of copying it
customCopyPublicDir(),
];
// Optionally enable https for local dev, install
// https://github.com/FiloSottile/mkcert and run
//
// mkcert -install; mkcert localhost
//
// in the same directory as this config to generate the certificates below
const https = await (async () => {
try {
return {
key: await readFile('./localhost-key.pem'),
cert: await readFile('./localhost.pem'),
};
} catch {
return false;
}
})();
return {
css: {
devSourcemap: true,
},
build: {
rollupOptions: {
plugins: productionOnlyPlugins,
},
minify: 'terser',
terserOptions: {
format: { comments: false },
},
chunkSizeWarningLimit: 900,
sourcemap: true,
copyPublicDir: false,
},
plugins,
server: {
// some files only exist in prod build; proxy them as not to break links
proxy: {
'/legal': 'https://fluid-earth.byrd.osu.edu',
...dataProxy,
},
https,
},
};
}
function commentHtml(text) {
return {
transformIndexHtml(html) {
return html
.replace('<!-- insert build and license info -->', text)
// fix built-in transform spacing
.replace(' \n <script', ' <script')
}
};
}
function copyLicenseAndPatches() {
return {
async generateBundle() {
await copyFile('LICENSE', 'dist/legal/LICENSE');
const party = 'dist/legal/THIRD_PARTY';
await appendFile(party, '\n\n\n---\n\n');
await appendFile(party, await readFile('./VENDORED'));
let patchDir = 'patches/';
let modFile = 'dist/legal/MODIFICATIONS';
let patches = await readdir(patchDir);
if (patches.length === 0) {
await writeFile(modFile, 'none');
} else {
await writeFile(modFile, '');
for (const patch of patches) {
await appendFile(modFile, await readFile(patchDir + patch));
}
}
}
};
}
function customCopyPublicDir() {
return {
async generateBundle() {
await copyDir('public', 'dist', true);
}
}
}
async function copyDir(srcDir, destDir, symlinkTera=false) {
await mkdir(destDir, { recursive: true });
for (let path of await readdir(srcDir)) {
let src = resolve(srcDir, path);
let dest = resolve(destDir, path);
if (symlinkTera && path === 'tera') {
await symlink(src, dest);
} else if ((await stat(src)).isDirectory()) {
await copyDir(src, dest);
} else {
await copyFile(src, dest);
}
}
}
// A custom Svelte preprocess for Fluid Earth that does the following:
//
// - converts em/rem units of some third-party components to px
// - removes <svelte:options> on Map component (only used for webcomponent)
//
function customPreprocess() {
const files = [
'svelte-toggle/src/Toggle.svelte',
'svelte-range-slider-pips/src/RangeSlider.svelte',
];
return {
name: 'Fluid Earth custom Svelte preprocess',
style: ({ content, filename }) => {
if (files.find(file => filename.endsWith(file))) {
const code = content.replaceAll(
/([0-9.]+)r?em/g,
(_, value) => `${parseFloat(value) * 16}px`,
);
return { code };
}
},
markup: ({ content, filename }) => {
if (filename.endsWith('src/map/Map.svelte')) {
const code = content.replaceAll(/^.*customElement=".*$/gm, '');
return { code };
}
},
}
}