atagen

malice

public
Star0
Watch0
294426c7f88fd4348a61b8a8590233237a1e52c0
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git
web/vite.config.tsTypescript109 lines3.5 KB
1
import { execFileSync } from 'node:child_process';
2
import { readdirSync, readFileSync } from 'node:fs';
3
import { join } from 'node:path';
4
import { fileURLToPath } from 'node:url';
5
import { defineConfig } from 'vite';
6
import { svelte } from '@sveltejs/vite-plugin-svelte';
7
8
const EMOJI_DATA_CHUNK = /^assets\/(compact|emojibase)-[^/]*\.js$/;
9
10
/**
11
 * The page isn't SW-controlled on first launch, so the worker chunk and the
12
 * wasm can bypass fetch interception entirely and never get cached. Instead
13
 * of relying on runtime fetches, prepend the full list of emitted files
14
 * (chunks, assets, wasm) to sw.js as a global so install() can precache all
15
 * of it directly, no separate request needed.
16
 */
17
function precacheManifestPlugin(): import('vite').Plugin {
18
  return {
19
    name: 'malice-sw-precache-manifest',
20
    generateBundle(_options, bundle) {
21
      const urls = Object.keys(bundle)
22
        .filter((fileName) => fileName !== 'sw.js' && !fileName.startsWith('twemoji/') && !EMOJI_DATA_CHUNK.test(fileName))
23
        .map((fileName) => `/${fileName}`);
24
      const swChunk = bundle['sw.js'];
25
      if (swChunk && swChunk.type === 'chunk') {
26
        swChunk.code = `self.__MALICE_PRECACHE__=${JSON.stringify(urls)};\n${swChunk.code}`;
27
      }
28
    }
29
  };
30
}
31
32
function twemojiPlugin(): import('vite').Plugin {
33
  const dir = fileURLToPath(new URL('./node_modules/@twemoji/svg', import.meta.url));
34
  const svgs = () => readdirSync(dir).filter((f) => f.endsWith('.svg'));
35
  return {
36
    name: 'malice-twemoji',
37
    configureServer(server) {
38
      server.middlewares.use('/twemoji', (req, res, next) => {
39
        const name = decodeURIComponent((req.url ?? '').split('?')[0]).replace(/^\//, '');
40
        if (!/^[0-9a-f-]+\.svg$/.test(name)) return next();
41
        try {
42
          res.setHeader('Content-Type', 'image/svg+xml');
43
          res.end(readFileSync(join(dir, name)));
44
        } catch {
45
          res.statusCode = 404;
46
          res.end();
47
        }
48
      });
49
    },
50
    generateBundle() {
51
      for (const file of svgs()) {
52
        this.emitFile({ type: 'asset', fileName: `twemoji/${file}`, source: readFileSync(join(dir, file)) });
53
      }
54
    }
55
  };
56
}
57
58
function buildId(): string {
59
  if (process.env.MALICE_BUILD) return process.env.MALICE_BUILD;
60
  let rev = 'dev';
61
  try {
62
    rev = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim() || rev;
63
  } catch {
64
  }
65
  return `${Math.floor(Date.now() / 1000)}-${rev}`;
66
}
67
68
export default defineConfig(({ mode }) => {
69
  const device = mode === 'device';
70
  const build = buildId();
71
  return {
72
    plugins: [svelte(), twemojiPlugin(), precacheManifestPlugin()],
73
    define: { __MALICE_DEVICE__: JSON.stringify(device), __MALICE_BUILD__: JSON.stringify(build) },
74
    assetsInclude: ['**/*.wasm'],
75
    worker: { format: 'es' },
76
    resolve: {
77
      alias: {
78
        'malice:device-transport': device ? '/src/lib/device-transport.ts' : '/src/lib/device-transport.stub.ts'
79
      }
80
    },
81
    server: {
82
      host: '127.0.0.1',
83
      port: 5173,
84
      proxy: {
85
        '/api': 'http://localhost:3000'
86
      }
87
    },
88
    preview: {
89
      host: '127.0.0.1',
90
      port: 4173,
91
      proxy: {
92
        '/api': 'http://localhost:3000'
93
      }
94
    },
95
    build: {
96
      outDir: device ? 'dist-device' : 'dist',
97
      emptyOutDir: true,
98
      rollupOptions: {
99
        input: {
100
          main: 'index.html',
101
          sw: 'src/sw.ts'
102
        },
103
        output: {
104
          entryFileNames: (chunk) => (chunk.name === 'sw' ? 'sw.js' : 'assets/[name]-[hash].js')
105
        }
106
      }
107
    }
108
  };
109
});