this repo has no description
1import { execSync } from 'child_process';
2import fs from 'fs';
3import { resolve } from 'path';
4
5import { lingui } from '@lingui/vite-plugin';
6import preact from '@preact/preset-vite';
7import Sonda from 'sonda/vite';
8import { uid } from 'uid/single';
9import { defineConfig, loadEnv } from 'vite';
10import generateFile from 'vite-plugin-generate-file';
11import htmlPlugin from 'vite-plugin-html-config';
12import { VitePWA } from 'vite-plugin-pwa';
13import removeConsole from 'vite-plugin-remove-console';
14import { run } from 'vite-plugin-run';
15
16import { ALL_LOCALES } from './src/locales';
17
18const allowedEnvPrefixes = ['VITE_', 'PHANPY_'];
19const { NODE_ENV } = process.env;
20const {
21 PHANPY_WEBSITE: WEBSITE,
22 PHANPY_CLIENT_NAME: CLIENT_NAME,
23 PHANPY_APP_ERROR_LOGGING: ERROR_LOGGING,
24 PHANPY_REFERRER_POLICY: REFERRER_POLICY,
25 PHANPY_DISALLOW_ROBOTS: DISALLOW_ROBOTS,
26 PHANPY_DEV,
27} = loadEnv('production', process.cwd(), allowedEnvPrefixes);
28
29const now = new Date();
30let commitHash;
31let fakeCommitHash = false;
32try {
33 commitHash = execSync('git rev-parse --short HEAD').toString().trim();
34} catch (error) {
35 // If error, means git is not installed or not a git repo (could be downloaded instead of git cloned)
36 // Fallback to random hash which should be different on every build run 🤞
37 commitHash = uid();
38 fakeCommitHash = true;
39}
40
41const rollbarCode = fs.readFileSync(
42 resolve(__dirname, './rollbar.js'),
43 'utf-8',
44);
45
46// https://vitejs.dev/config/
47export default defineConfig({
48 base: './',
49 envPrefix: allowedEnvPrefixes,
50 appType: 'mpa',
51 mode: NODE_ENV,
52 define: {
53 __BUILD_TIME__: JSON.stringify(now),
54 __COMMIT_HASH__: JSON.stringify(commitHash),
55 __FAKE_COMMIT_HASH__: fakeCommitHash,
56 },
57 server: {
58 host: true,
59 },
60 css: {
61 preprocessorMaxWorkers: 1,
62 },
63 plugins: [
64 preact({
65 // Force use Babel instead of ESBuild due to this change: https://github.com/preactjs/preset-vite/pull/114
66 // Else, a bug will happen with importing variables from import.meta.env
67 babel: {
68 plugins: ['@lingui/babel-plugin-lingui-macro'],
69 },
70 }),
71 lingui(),
72 run({
73 silent: false,
74 input: [
75 {
76 name: 'messages:extract:clean',
77 run: ['npm', 'run', 'messages:extract:clean'],
78 pattern: 'src/**/*.{js,jsx,ts,tsx}',
79 },
80 // {
81 // name: 'update-catalogs',
82 // run: ['node', 'scripts/catalogs.js'],
83 // pattern: 'src/locales/*.po',
84 // },
85 ],
86 }),
87 removeConsole({
88 includes: ['log', 'debug', 'info', 'warn', 'error'],
89 }),
90 htmlPlugin({
91 metas: [
92 // Learn more: https://web.dev/articles/referrer-best-practices
93 {
94 name: 'referrer',
95 content: REFERRER_POLICY || 'origin',
96 },
97 ],
98 headScripts: ERROR_LOGGING ? [rollbarCode] : [],
99 links: [
100 ...ALL_LOCALES.map((lang) => ({
101 rel: 'alternate',
102 hreflang: lang,
103 // *Fully-qualified* URLs
104 href: `${WEBSITE}/?lang=${lang}`,
105 })),
106 // https://developers.google.com/search/docs/specialty/international/localized-versions#xdefault
107 {
108 rel: 'alternate',
109 hreflang: 'x-default',
110 href: `${WEBSITE}`,
111 },
112 ],
113 }),
114 generateFile([
115 {
116 type: 'json',
117 output: './version.json',
118 data: {
119 buildTime: now,
120 commitHash,
121 },
122 },
123 ...(DISALLOW_ROBOTS
124 ? [
125 {
126 type: 'raw',
127 output: './robots.txt',
128 data: 'User-agent: *\nDisallow: /',
129 },
130 ]
131 : []),
132 ]),
133 VitePWA({
134 manifest: {
135 name: CLIENT_NAME,
136 short_name: CLIENT_NAME,
137 description: 'Minimalistic opinionated Mastodon web client',
138 // https://github.com/cheeaun/phanpy/issues/231
139 theme_color: undefined,
140 icons: [
141 {
142 src: 'logo-192.png',
143 sizes: '192x192',
144 type: 'image/png',
145 },
146 {
147 src: 'logo-512.png',
148 sizes: '512x512',
149 type: 'image/png',
150 },
151 {
152 src: 'logo-maskable-512.png',
153 sizes: '512x512',
154 type: 'image/png',
155 purpose: 'maskable',
156 },
157 ],
158 categories: ['social', 'news'],
159 },
160 strategies: 'injectManifest',
161 injectRegister: 'inline',
162 injectManifest: {
163 // Prevent "Unable to find a place to inject the manifest" error
164 injectionPoint: undefined,
165 },
166 devOptions: {
167 enabled: NODE_ENV === 'development',
168 type: 'module',
169 },
170 }),
171 Sonda({
172 deep: true,
173 brotli: true,
174 open: false,
175 }),
176 ],
177 build: {
178 sourcemap: true,
179 // Note: In Vite 6, if cssCodeSplit = false, it will show error "Cannot read properties of undefined (reading 'includes')"
180 // TODO: Revisit this when this issue is fixed
181 // cssCodeSplit: false,
182 rollupOptions: {
183 treeshake: false,
184 input: {
185 main: resolve(__dirname, 'index.html'),
186 compose: resolve(__dirname, 'compose/index.html'),
187 },
188 output: {
189 // NOTE: Comment this for now. This messes up async imports.
190 // Without SplitVendorChunkPlugin, pushing everything to vendor is not "smart" enough
191 // manualChunks: (id, { getModuleInfo }) => {
192 // // if (id.includes('@formatjs/intl-segmenter/polyfill')) return 'intl-segmenter-polyfill';
193 // if (/tiny.*light/.test(id)) return 'tinyld-light';
194
195 // // Implement logic similar to splitVendorChunkPlugin
196 // if (id.includes('node_modules')) {
197 // // Check if this module is dynamically imported
198 // const moduleInfo = getModuleInfo(id);
199 // if (moduleInfo) {
200 // // If it's imported dynamically, don't put in vendor
201 // const isDynamicOnly =
202 // moduleInfo.importers.length === 0 &&
203 // moduleInfo.dynamicImporters.length > 0;
204 // if (isDynamicOnly) return null;
205 // }
206 // return 'vendor';
207 // }
208 // },
209 chunkFileNames: (chunkInfo) => {
210 const { facadeModuleId } = chunkInfo;
211 if (facadeModuleId && facadeModuleId.includes('icon')) {
212 return 'assets/icons/[name]-[hash].js';
213 }
214 if (facadeModuleId && facadeModuleId.includes('locales')) {
215 return 'assets/locales/[name]-[hash].js';
216 }
217 return 'assets/[name]-[hash].js';
218 },
219 assetFileNames: (assetInfo) => {
220 const { originalFileNames } = assetInfo;
221 if (originalFileNames?.[0]?.includes('assets/sandbox')) {
222 return 'assets/sandbox/[name]-[hash].[ext]';
223 }
224 return 'assets/[name]-[hash].[ext]';
225 },
226 },
227 plugins: [
228 {
229 name: 'exclude-sandbox',
230 generateBundle(_, bundle) {
231 if (!PHANPY_DEV) {
232 Object.entries(bundle).forEach(([name, chunk]) => {
233 if (name.includes('sandbox')) {
234 delete bundle[name];
235 }
236 });
237 }
238 },
239 },
240 ],
241 },
242 },
243});