vite.config.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import path, { resolve } from "path"
  2. import fs from "fs"
  3. import { execSync } from "child_process"
  4. import { defineConfig, type PluginOption, type Plugin } from "vite"
  5. import react from "@vitejs/plugin-react"
  6. import tailwindcss from "@tailwindcss/vite"
  7. import { sourcemapPlugin } from "./src/vite-plugins/sourcemapPlugin"
  8. function getGitSha() {
  9. let gitSha: string | undefined = undefined
  10. try {
  11. gitSha = execSync("git rev-parse HEAD").toString().trim()
  12. } catch (_error) {
  13. // Do nothing.
  14. }
  15. return gitSha
  16. }
  17. const wasmPlugin = (): Plugin => ({
  18. name: "wasm",
  19. async load(id) {
  20. if (id.endsWith(".wasm")) {
  21. const wasmBinary = await import(id)
  22. return `
  23. const wasmModule = new WebAssembly.Module(${wasmBinary.default});
  24. export default wasmModule;
  25. `
  26. }
  27. },
  28. })
  29. const persistPortPlugin = (): Plugin => ({
  30. name: "write-port-to-file",
  31. configureServer(viteDevServer) {
  32. viteDevServer?.httpServer?.once("listening", () => {
  33. const address = viteDevServer?.httpServer?.address()
  34. const port = address && typeof address === "object" ? address.port : null
  35. if (port) {
  36. fs.writeFileSync(resolve(__dirname, "..", ".vite-port"), port.toString())
  37. console.log(`[Vite Plugin] Server started on port ${port}`)
  38. } else {
  39. console.warn("[Vite Plugin] Could not determine server port")
  40. }
  41. })
  42. },
  43. })
  44. // https://vitejs.dev/config/
  45. export default defineConfig(({ mode }) => {
  46. let outDir = "../src/webview-ui/build"
  47. const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "src", "package.json"), "utf8"))
  48. const gitSha = getGitSha()
  49. const define: Record<string, any> = {
  50. "process.platform": JSON.stringify(process.platform),
  51. "process.env.VSCODE_TEXTMATE_DEBUG": JSON.stringify(process.env.VSCODE_TEXTMATE_DEBUG),
  52. "process.env.PKG_NAME": JSON.stringify(pkg.name),
  53. "process.env.PKG_VERSION": JSON.stringify(pkg.version),
  54. "process.env.PKG_OUTPUT_CHANNEL": JSON.stringify("Roo-Code"),
  55. ...(gitSha ? { "process.env.PKG_SHA": JSON.stringify(gitSha) } : {}),
  56. }
  57. // TODO: We can use `@roo-code/build` to generate `define` once the
  58. // monorepo is deployed.
  59. if (mode === "nightly") {
  60. outDir = "../apps/vscode-nightly/build/webview-ui/build"
  61. const nightlyPkg = JSON.parse(
  62. fs.readFileSync(path.join(__dirname, "..", "apps", "vscode-nightly", "package.nightly.json"), "utf8"),
  63. )
  64. define["process.env.PKG_NAME"] = JSON.stringify(nightlyPkg.name)
  65. define["process.env.PKG_VERSION"] = JSON.stringify(nightlyPkg.version)
  66. define["process.env.PKG_OUTPUT_CHANNEL"] = JSON.stringify("Roo-Code-Nightly")
  67. }
  68. const plugins: PluginOption[] = [
  69. react({
  70. babel: {
  71. plugins: [["babel-plugin-react-compiler", { target: "18" }]],
  72. },
  73. }),
  74. tailwindcss(),
  75. persistPortPlugin(),
  76. wasmPlugin(),
  77. sourcemapPlugin(),
  78. ]
  79. return {
  80. plugins,
  81. resolve: {
  82. alias: {
  83. "@": resolve(__dirname, "./src"),
  84. "@src": resolve(__dirname, "./src"),
  85. "@roo": resolve(__dirname, "../src/shared"),
  86. },
  87. },
  88. build: {
  89. outDir,
  90. emptyOutDir: true,
  91. reportCompressedSize: false,
  92. // Generate complete source maps with original TypeScript sources
  93. sourcemap: true,
  94. // Ensure source maps are properly included in the build
  95. minify: mode === "production" ? "esbuild" : false,
  96. // Use a single combined CSS bundle so all webviews share styles
  97. cssCodeSplit: false,
  98. rollupOptions: {
  99. // Externalize vscode module - it's imported by file-search.ts which is
  100. // dynamically imported by roo-config/index.ts, but should never be bundled
  101. // in the webview since it's not available in the browser context
  102. external: ["vscode"],
  103. input: {
  104. index: resolve(__dirname, "index.html"),
  105. },
  106. output: {
  107. entryFileNames: `assets/[name].js`,
  108. chunkFileNames: (chunkInfo) => {
  109. if (chunkInfo.name === "mermaid-bundle") {
  110. return `assets/mermaid-bundle.js`
  111. }
  112. // Default naming for other chunks, ensuring uniqueness from entry
  113. return `assets/chunk-[hash].js`
  114. },
  115. assetFileNames: (assetInfo) => {
  116. const name = assetInfo.name || ""
  117. // Force all CSS into a single predictable file used by both webviews
  118. if (name.endsWith(".css")) {
  119. return "assets/index.css"
  120. }
  121. if (name.endsWith(".woff2") || name.endsWith(".woff") || name.endsWith(".ttf")) {
  122. return "assets/fonts/[name][extname]"
  123. }
  124. // Ensure source maps are included in the build
  125. if (name.endsWith(".map")) {
  126. return "assets/[name]"
  127. }
  128. return "assets/[name][extname]"
  129. },
  130. manualChunks: (id, { getModuleInfo }) => {
  131. // Consolidate all mermaid code and its direct large dependencies (like dagre)
  132. // into a single chunk. The 'channel.js' error often points to dagre.
  133. if (
  134. id.includes("node_modules/mermaid") ||
  135. id.includes("node_modules/dagre") || // dagre is a common dep for graph layout
  136. id.includes("node_modules/cytoscape") // another potential graph lib
  137. // Add other known large mermaid dependencies if identified
  138. ) {
  139. return "mermaid-bundle"
  140. }
  141. // Check if the module is part of any explicitly defined mermaid-related dynamic import
  142. // This is a more advanced check if simple path matching isn't enough.
  143. const moduleInfo = getModuleInfo(id)
  144. if (moduleInfo?.importers.some((importer) => importer.includes("node_modules/mermaid"))) {
  145. return "mermaid-bundle"
  146. }
  147. if (
  148. moduleInfo?.dynamicImporters.some((importer) => importer.includes("node_modules/mermaid"))
  149. ) {
  150. return "mermaid-bundle"
  151. }
  152. },
  153. },
  154. },
  155. },
  156. server: {
  157. hmr: {
  158. host: "localhost",
  159. protocol: "ws",
  160. },
  161. cors: {
  162. origin: "*",
  163. methods: "*",
  164. allowedHeaders: "*",
  165. },
  166. },
  167. define,
  168. optimizeDeps: {
  169. include: [
  170. "mermaid",
  171. "dagre", // Explicitly include dagre for pre-bundling
  172. // Add other known large mermaid dependencies if identified
  173. ],
  174. exclude: ["@vscode/codicons", "vscode-oniguruma", "shiki"],
  175. },
  176. assetsInclude: ["**/*.wasm", "**/*.wav"],
  177. }
  178. })