vite.config.mts 8.8 KB

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