forked from morethanwords/tweb
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvite.config.mts
More file actions
264 lines (247 loc) · 10.6 KB
/
Copy pathvite.config.mts
File metadata and controls
264 lines (247 loc) · 10.6 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
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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
/// <reference types="vitest/config" />
import {defineConfig, loadEnv} from 'vite';
import solidPlugin from 'vite-plugin-solid';
// @ts-ignore no type declarations
import handlebars from 'vite-plugin-handlebars';
import basicSsl from '@vitejs/plugin-basic-ssl';
// import devtools from 'solid-devtools/vite'
import autoprefixer from 'autoprefixer';
import {resolve} from 'path';
import {existsSync, copyFileSync, readFileSync} from 'fs';
// `import type`, not a value import: Vite's `configLoader: 'native'` hands this
// file to Node's type stripper, which erases annotations but does NOT elide
// unused value imports — a plain `import {ServerOptions}` would become a real
// runtime import of an export that only exists in the type world.
import type {ServerOptions} from 'vite';
import {watchLangFile} from './watch-lang.js';
import devChecks from './scripts/dev-checks.mjs';
import path from 'path';
// `import.meta.dirname`, not `__dirname`: this file is ESM (.mts), so the CJS
// global does not exist under `configLoader: 'native'`. Vite's current bundle
// loader rewrites it to an injected constant, so both loaders work.
const rootDir = resolve(import.meta.dirname);
const certsDir = path.join(rootDir, 'certs');
const ENV_LOCAL_FILE_PATH = path.join(rootDir, '.env.local');
const LANG_PACK_LOCAL_FILE_PATH = path.join(rootDir, 'src', 'langPackLocalVersion.ts');
const isDEV = process.env.NODE_ENV === 'development';
if(!existsSync(LANG_PACK_LOCAL_FILE_PATH)) {
copyFileSync(path.join(rootDir, 'src', 'langPackLocalVersion.example.ts'), LANG_PACK_LOCAL_FILE_PATH);
}
if(isDEV) {
if(!existsSync(ENV_LOCAL_FILE_PATH)) {
copyFileSync(path.join(rootDir, '.env.local.example'), ENV_LOCAL_FILE_PATH);
}
watchLangFile();
}
const mode = process.env.NODE_ENV || 'development';
const currentEnv = loadEnv(mode, process.cwd(), '');
const primaryHost = (currentEnv.VITE_ALLOWED_HOSTS || 'web.telegram.org')
.split(',')[0]
.replace(/^https?:\/\//, '')
.replace(/\/$/, '')
.trim();
const APP_ORIGIN = `https://${primaryHost}/`;
const APP_URL = `${APP_ORIGIN}k/`;
const appName = process.env.VITE_APP_NAME || 'Telegram';
const handlebarsPlugin = handlebars({
context: {
title: `${appName} Web`,
description: `A private, self-hosted messaging app for ${appName}.`,
url: APP_URL,
origin: APP_ORIGIN
}
});
const USE_SSL = false;
const USE_SIGNED_CERTS = USE_SSL && true;
const USE_SELF_SIGNED_CERTS = USE_SSL && false;
// * mkdir certs; cd certs
// * mkcert web.telegram.org
// * chmod 644 web.telegram.org-key.pem
// * nano /etc/hosts
// * 127.0.0.1 web.telegram.org
const host = USE_SSL ? primaryHost : 'localhost';
// HTTP/2 for `pnpm start`. Vite serves dev modules unbundled — one request per module —
// and over http/1.1 the browser's ~6-connections-per-origin cap serialises the hundreds
// of module requests into a slow waterfall (lots of "pending"). Enabling https flips the
// dev server to HTTP/2, which multiplexes them all over one connection and kills the
// waterfall. Use mkcert, NOT a self-signed cert: tweb's ServiceWorker refuses to register
// on an untrusted cert. One-time setup: mkcert -install && (cd certs && mkcert localhost)
// Auto-enabled once the cert exists; off under TWEB_PREVIEW (the merged preview config
// must stay on http for its tooling) and off until the cert is present (no cert → today's
// plain-http dev, unchanged).
const DEV_HTTP2_KEY = path.join(certsDir, 'localhost-key.pem');
const DEV_HTTP2_CERT = path.join(certsDir, 'localhost.pem');
const USE_DEV_HTTP2 = !USE_SSL && !process.env.TWEB_PREVIEW && !process.env.VITEST &&
existsSync(DEV_HTTP2_KEY) && existsSync(DEV_HTTP2_CERT);
const serverOptions: ServerOptions = {
host,
port: USE_SSL ? 443 : 8080,
// Configure dynamically assigned security hosts straight from the root
allowedHosts: (currentEnv.VITE_ALLOWED_HOSTS || '')
.split(',')
.map(host => host.replace(/^https?:\/\//, '').replace(/\/$/, '').trim())
.filter(Boolean),
watch: {
// NB: anchor on rootDir. A worktree checkout's own path contains
// ".claude/worktrees/<name>/", so a bare '**/.claude/**' glob would also match
// the worktree's OWN src and silently disable all HMR there. Anchoring ignores
// only this checkout's .claude (and, from the main repo, the worktrees inside it).
ignored: [resolve(rootDir, '.claude') + '/**']
},
sourcemapIgnoreList(sourcePath, sourcemapPath) {
return sourcePath.includes('node_modules') ||
sourcePath.includes('logger') ||
sourcePath.includes('eventListenerBase');
},
https: USE_SIGNED_CERTS ? {
key: path.join(certsDir, host + '-key.pem'),
cert: path.join(certsDir, host + '.pem')
} : USE_DEV_HTTP2 ? {
key: readFileSync(DEV_HTTP2_KEY),
cert: readFileSync(DEV_HTTP2_CERT)
} : undefined
};
const SOLID_SRC_PATH = 'src/solid/packages/solid';
const SOLID_BUILT_PATH = 'src/vendor/solid';
const USE_SOLID_SRC = false;
const SOLID_PATH = USE_SOLID_SRC ? SOLID_SRC_PATH : SOLID_BUILT_PATH;
const USE_OWN_SOLID = existsSync(resolve(rootDir, SOLID_PATH));
const NO_MINIFY = false;
const BASIC_SSL_CONFIG: Parameters<typeof basicSsl>[0] = USE_SELF_SIGNED_CERTS ? {
name: host,
certDir: certsDir
} : undefined;
const ADDITIONAL_ALIASES = {
'solid-transition-group': resolve(rootDir, 'src/vendor/solid-transition-group'),
'@components': resolve(rootDir, 'src/components'),
'@helpers': resolve(rootDir, 'src/helpers'),
'@hooks': resolve(rootDir, 'src/hooks'),
'@stores': resolve(rootDir, 'src/stores'),
'@lib': resolve(rootDir, 'src/lib'),
'@appManagers': resolve(rootDir, 'src/lib/appManagers'),
'@richTextProcessor': resolve(rootDir, 'src/lib/richTextProcessor'),
'@environment': resolve(rootDir, 'src/environment'),
'@customEmoji': resolve(rootDir, 'src/lib/customEmoji'),
'@config': resolve(rootDir, 'src/config'),
'@vendor': resolve(rootDir, 'src/vendor'),
'@layer': resolve(rootDir, 'src/layer'),
'@types': resolve(rootDir, 'src/types'),
'@': resolve(rootDir, 'src')
};
if(USE_OWN_SOLID) {
console.log('using own solid', SOLID_PATH, 'built', !USE_SOLID_SRC);
} else {
console.log('using original solid');
}
export default defineConfig({
plugins: [
// devtools({
// /* features options - all disabled by default */
// autoname: true // e.g. enable autoname
// }),
process.env.VITEST || process.env.TWEB_PREVIEW ? undefined : devChecks(rootDir),
solidPlugin(),
handlebarsPlugin as any,
USE_SELF_SIGNED_CERTS ? basicSsl(BASIC_SSL_CONFIG) : undefined,
// Only emit the bundle treemap (stats.html) when explicitly analyzing (ANALYZE=1):
// it adds build time and writes a ~1.3MB file that otherwise gets globbed into the
// dep scan. Run `ANALYZE=1 pnpm build` to generate it.
process.env.ANALYZE ? import('rollup-plugin-visualizer').then(({visualizer}) => visualizer({
gzipSize: true,
template: 'treemap'
})) : undefined
].filter(Boolean),
test: {
// include: ['**/*.{test,spec}.?(c|m)[jt]s?(x)'],
exclude: [
'**/node_modules/**',
'**/dist/**',
// git worktrees live here with their own copies of every test file —
// without this, `pnpm test <pattern>` runs each match N+1 times at once
'**/.claude/**',
'**/cypress/**',
'**/.{idea,git,cache,output,temp}/**',
'**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*',
'**/solid/**',
// Playwright browser specs live here and must not be run by vitest (jsdom)
'**/e2e/**'
],
// coverage: {
// provider: 'v8',
// reporter: ['text', 'lcov'],
// include: ['src/**/*.ts', 'store/src/**/*.ts', 'web/src/**/*.ts'],
// exclude: ['**/*.d.ts', 'src/server/*.ts', 'store/src/**/server.ts']
// },
environment: 'jsdom',
// otherwise, solid would be loaded twice:
// deps: {registerNodeLoader: true},
pool: 'forks',
globals: true,
setupFiles: ['./src/tests/setup.ts']
},
server: serverOptions,
define: {
'import.meta.env.VITE_APP_ORIGIN': JSON.stringify(APP_ORIGIN)
},
base: '',
// Pin the dep-optimizer's scan to the real entry (index.html → src/index.ts).
// Otherwise Vite auto-globs every *.html (stats.html, public/*.html, the icomoon
// demo.html) as scan entries, and a parse error in any of them (e.g. the stale
// public/*.js build artifacts with merge-conflict markers) aborts the whole scan
// and disables dependency pre-bundling — making cold dev loads slow and reload-prone.
optimizeDeps: {
entries: ['index.html']
},
build: {
target: 'es2020',
// `es2020` says nothing about browsers, so lightningcss falls back to a target old enough to
// lower every logical property into a pair of `:lang()` rules — 13680 of them, emitted after
// the block they came from and with a higher specificity, so a physical `left`/`margin-left`
// authored alongside `inset-inline-*` silently loses (that is what pinned the emoji panel to
// the window edge in a built bundle only). These are the versions that support logical
// properties natively, so lightningcss leaves them alone; RTL runs off `documentElement.dir`,
// which index.ts sets on every language, not off `:lang()`.
cssTarget: ['chrome87', 'edge87', 'firefox78', 'safari14.1'],
sourcemap: true,
assetsDir: '',
copyPublicDir: false,
emptyOutDir: true,
minify: NO_MINIFY ? false : undefined,
rolldownOptions: {
output: {
sourcemapIgnoreList: serverOptions.sourcemapIgnoreList
}
// input: {
// main: './index.html',
// sw: './src/index.service.ts'
// }
}
// cssCodeSplit: true
},
worker: {
format: 'es'
},
css: {
devSourcemap: true,
postcss: {
plugins: [
autoprefixer({}) // add options if needed
]
}
},
resolve: {
// conditions: ['development', 'browser'],
alias: USE_OWN_SOLID ? {
'rxcore': resolve(rootDir, SOLID_PATH, 'web/core'),
// Vite 8 no longer sniffs aliased package formats. Point directly at the
// browser builds so Solid's `module` field cannot select server.js.
'solid-js/jsx-runtime': resolve(rootDir, SOLID_PATH, 'dist', isDEV ? 'dev.js' : 'solid.js'),
'solid-js/html': resolve(rootDir, SOLID_PATH, 'html/dist/html.js'),
'solid-js/h': resolve(rootDir, SOLID_PATH, 'h/dist/h.js'),
'solid-js/web': resolve(rootDir, SOLID_PATH, 'web/dist', isDEV ? 'dev.js' : 'web.js'),
'solid-js/store': resolve(rootDir, SOLID_PATH, 'store/dist', isDEV ? 'dev.js' : 'store.js'),
'solid-js': resolve(rootDir, SOLID_PATH, 'dist', isDEV ? 'dev.js' : 'solid.js'),
...ADDITIONAL_ALIASES
} : ADDITIONAL_ALIASES
}
});