vite.config.mts 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. import path from "path";
  2. import { defineConfig, loadEnv } from "vite";
  3. import react from "@vitejs/plugin-react";
  4. import svgrPlugin from "vite-plugin-svgr";
  5. import { ViteEjsPlugin } from "vite-plugin-ejs";
  6. import { VitePWA } from "vite-plugin-pwa";
  7. import checker from "vite-plugin-checker";
  8. import { createHtmlPlugin } from "vite-plugin-html";
  9. import Sitemap from "vite-plugin-sitemap";
  10. import { woff2BrowserPlugin } from "../scripts/woff2/woff2-vite-plugins";
  11. export default defineConfig(({ mode }) => {
  12. // To load .env variables
  13. const envVars = loadEnv(mode, `../`);
  14. // https://vitejs.dev/config/
  15. return {
  16. server: {
  17. port: Number(envVars.VITE_APP_PORT || 3000),
  18. // open the browser
  19. open: true,
  20. },
  21. // We need to specify the envDir since now there are no
  22. //more located in parallel with the vite.config.ts file but in parent dir
  23. envDir: "../",
  24. resolve: {
  25. alias: [
  26. {
  27. find: /^@excalidraw\/common$/,
  28. replacement: path.resolve(
  29. __dirname,
  30. "../packages/common/src/index.ts",
  31. ),
  32. },
  33. {
  34. find: /^@excalidraw\/common\/(.*?)/,
  35. replacement: path.resolve(__dirname, "../packages/common/src/$1"),
  36. },
  37. {
  38. find: /^@excalidraw\/element$/,
  39. replacement: path.resolve(
  40. __dirname,
  41. "../packages/element/src/index.ts",
  42. ),
  43. },
  44. {
  45. find: /^@excalidraw\/element\/(.*?)/,
  46. replacement: path.resolve(__dirname, "../packages/element/src/$1"),
  47. },
  48. {
  49. find: /^@excalidraw\/excalidraw$/,
  50. replacement: path.resolve(
  51. __dirname,
  52. "../packages/excalidraw/index.tsx",
  53. ),
  54. },
  55. {
  56. find: /^@excalidraw\/excalidraw\/(.*?)/,
  57. replacement: path.resolve(__dirname, "../packages/excalidraw/$1"),
  58. },
  59. {
  60. find: /^@excalidraw\/math$/,
  61. replacement: path.resolve(__dirname, "../packages/math/src/index.ts"),
  62. },
  63. {
  64. find: /^@excalidraw\/math\/(.*?)/,
  65. replacement: path.resolve(__dirname, "../packages/math/src/$1"),
  66. },
  67. {
  68. find: /^@excalidraw\/utils$/,
  69. replacement: path.resolve(
  70. __dirname,
  71. "../packages/utils/src/index.ts",
  72. ),
  73. },
  74. {
  75. find: /^@excalidraw\/utils\/(.*?)/,
  76. replacement: path.resolve(__dirname, "../packages/utils/src/$1"),
  77. },
  78. ],
  79. },
  80. build: {
  81. outDir: "build",
  82. rollupOptions: {
  83. output: {
  84. assetFileNames(chunkInfo) {
  85. if (chunkInfo?.name?.endsWith(".woff2")) {
  86. const family = chunkInfo.name.split("-")[0];
  87. return `fonts/${family}/[name][extname]`;
  88. }
  89. return "assets/[name]-[hash][extname]";
  90. },
  91. // Creating separate chunk for locales except for en and percentages.json so they
  92. // can be cached at runtime and not merged with
  93. // app precache. en.json and percentages.json are needed for first load
  94. // or fallback hence not clubbing with locales so first load followed by offline mode works fine. This is how CRA used to work too.
  95. manualChunks(id) {
  96. if (
  97. id.includes("packages/excalidraw/locales") &&
  98. id.match(/en.json|percentages.json/) === null
  99. ) {
  100. const index = id.indexOf("locales/");
  101. // Taking the substring after "locales/"
  102. return `locales/${id.substring(index + 8)}`;
  103. }
  104. },
  105. },
  106. },
  107. sourcemap: true,
  108. // don't auto-inline small assets (i.e. fonts hosted on CDN)
  109. assetsInlineLimit: 0,
  110. },
  111. plugins: [
  112. Sitemap({
  113. hostname: "https://excalidraw.com",
  114. outDir: "build",
  115. changefreq: "monthly",
  116. // its static in public folder
  117. generateRobotsTxt: false,
  118. }),
  119. woff2BrowserPlugin(),
  120. react(),
  121. checker({
  122. typescript: true,
  123. eslint:
  124. envVars.VITE_APP_ENABLE_ESLINT === "false"
  125. ? undefined
  126. : { lintCommand: 'eslint "./**/*.{js,ts,tsx}"' },
  127. overlay: {
  128. initialIsOpen: envVars.VITE_APP_COLLAPSE_OVERLAY === "false",
  129. badgeStyle: "margin-bottom: 4rem; margin-left: 1rem",
  130. },
  131. }),
  132. svgrPlugin(),
  133. ViteEjsPlugin(),
  134. VitePWA({
  135. registerType: "autoUpdate",
  136. devOptions: {
  137. /* set this flag to true to enable in Development mode */
  138. enabled: envVars.VITE_APP_ENABLE_PWA === "true",
  139. },
  140. workbox: {
  141. // don't precache fonts, locales and separate chunks
  142. globIgnores: [
  143. "fonts.css",
  144. "**/locales/**",
  145. "service-worker.js",
  146. "**/*.chunk-*.js",
  147. ],
  148. runtimeCaching: [
  149. {
  150. urlPattern: new RegExp(".+.woff2"),
  151. handler: "CacheFirst",
  152. options: {
  153. cacheName: "fonts",
  154. expiration: {
  155. maxEntries: 1000,
  156. maxAgeSeconds: 60 * 60 * 24 * 90, // 90 days
  157. },
  158. cacheableResponse: {
  159. // 0 to cache "opaque" responses from cross-origin requests (i.e. CDN)
  160. statuses: [0, 200],
  161. },
  162. },
  163. },
  164. {
  165. urlPattern: new RegExp("fonts.css"),
  166. handler: "StaleWhileRevalidate",
  167. options: {
  168. cacheName: "fonts",
  169. expiration: {
  170. maxEntries: 50,
  171. },
  172. },
  173. },
  174. {
  175. urlPattern: new RegExp("locales/[^/]+.js"),
  176. handler: "CacheFirst",
  177. options: {
  178. cacheName: "locales",
  179. expiration: {
  180. maxEntries: 50,
  181. maxAgeSeconds: 60 * 60 * 24 * 30, // <== 30 days
  182. },
  183. },
  184. },
  185. {
  186. urlPattern: new RegExp(".chunk-.+.js"),
  187. handler: "CacheFirst",
  188. options: {
  189. cacheName: "chunk",
  190. expiration: {
  191. maxEntries: 50,
  192. maxAgeSeconds: 60 * 60 * 24 * 90, // <== 90 days
  193. },
  194. },
  195. },
  196. ],
  197. },
  198. manifest: {
  199. short_name: "Excalidraw",
  200. name: "Excalidraw",
  201. description:
  202. "Excalidraw is a whiteboard tool that lets you easily sketch diagrams that have a hand-drawn feel to them.",
  203. icons: [
  204. {
  205. src: "android-chrome-192x192.png",
  206. sizes: "192x192",
  207. type: "image/png",
  208. },
  209. {
  210. src: "apple-touch-icon.png",
  211. type: "image/png",
  212. sizes: "180x180",
  213. },
  214. {
  215. src: "favicon-32x32.png",
  216. sizes: "32x32",
  217. type: "image/png",
  218. },
  219. {
  220. src: "favicon-16x16.png",
  221. sizes: "16x16",
  222. type: "image/png",
  223. },
  224. ],
  225. start_url: "/",
  226. id: "excalidraw",
  227. display: "standalone",
  228. theme_color: "#121212",
  229. background_color: "#ffffff",
  230. file_handlers: [
  231. {
  232. action: "/",
  233. accept: {
  234. "application/vnd.excalidraw+json": [".excalidraw"],
  235. },
  236. },
  237. ],
  238. share_target: {
  239. action: "/web-share-target",
  240. method: "POST",
  241. enctype: "multipart/form-data",
  242. params: {
  243. files: [
  244. {
  245. name: "file",
  246. accept: [
  247. "application/vnd.excalidraw+json",
  248. "application/json",
  249. ".excalidraw",
  250. ],
  251. },
  252. ],
  253. },
  254. },
  255. screenshots: [
  256. {
  257. src: "/screenshots/virtual-whiteboard.png",
  258. type: "image/png",
  259. sizes: "462x945",
  260. },
  261. {
  262. src: "/screenshots/wireframe.png",
  263. type: "image/png",
  264. sizes: "462x945",
  265. },
  266. {
  267. src: "/screenshots/illustration.png",
  268. type: "image/png",
  269. sizes: "462x945",
  270. },
  271. {
  272. src: "/screenshots/shapes.png",
  273. type: "image/png",
  274. sizes: "462x945",
  275. },
  276. {
  277. src: "/screenshots/collaboration.png",
  278. type: "image/png",
  279. sizes: "462x945",
  280. },
  281. {
  282. src: "/screenshots/export.png",
  283. type: "image/png",
  284. sizes: "462x945",
  285. },
  286. ],
  287. },
  288. }),
  289. createHtmlPlugin({
  290. minify: true,
  291. }),
  292. ],
  293. publicDir: "../public",
  294. };
  295. });