import fs from 'node:fs'; import { createRequire } from 'node:module'; import { cpus } from 'node:os'; import path, { resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import type { GetManualChunk } from 'rollup'; import type { Plugin } from 'vite'; import { defineConfig, loadEnv } from 'vite'; import istanbul from 'vite-plugin-istanbul'; import wasm from 'vite-plugin-wasm'; import { hmrPlugin } from './scripts/hmr-plugin'; const require = createRequire(import.meta.url); const enableIstanbul = !!process.env.COVERAGE; const chunkSizeReport = !!process.env.CHUNK_SIZE_REPORT; const cache = new Map(); export function sourcemapExclude(): Plugin { return { name: 'sourcemap-exclude', transform(code: string, id: string) { if (id.includes('node_modules') && !id.includes('@blocksuite')) { return { code, // https://github.com/rollup/rollup/blob/master/docs/plugin-development/index.md#source-code-transformations map: { mappings: '' }, }; } }, }; } type GetModuleInfo = Parameters[1]['getModuleInfo']; function isDepInclude( id: string, depPaths: string[], importChain: string[], getModuleInfo: GetModuleInfo ): boolean | undefined { const key = `${id}-${depPaths.join('|')}`; if (importChain.includes(id)) { cache.set(key, false); return false; } if (cache.has(key)) { return cache.get(key); } for (const depPath of depPaths) { if (id.includes(depPath)) { importChain.forEach(item => cache.set(`${item}-${depPaths.join('|')}`, true) ); return true; } } const moduleInfo = getModuleInfo(id); if (!moduleInfo || !moduleInfo.importers) { cache.set(key, false); return false; } const isInclude = moduleInfo.importers.some(importer => isDepInclude(importer, depPaths, importChain.concat(id), getModuleInfo) ); cache.set(key, isInclude); return isInclude; } const chunkGroups = { framework: [ require.resolve('@blocksuite/block-std'), require.resolve('@blocksuite/block-std/gfx'), require.resolve('@blocksuite/global'), require.resolve('@blocksuite/global/utils'), require.resolve('@blocksuite/global/env'), require.resolve('@blocksuite/global/exceptions'), require.resolve('@blocksuite/global/di'), require.resolve('@blocksuite/inline'), require.resolve('@blocksuite/store'), require.resolve('@blocksuite/sync'), ], components: [ require.resolve('@blocksuite/affine-components/icons'), require.resolve('@blocksuite/affine-components/peek'), require.resolve('@blocksuite/affine-components/portal'), require.resolve('@blocksuite/affine-components/hover'), require.resolve('@blocksuite/affine-components/toolbar'), require.resolve('@blocksuite/affine-components/toast'), require.resolve('@blocksuite/affine-components/rich-text'), require.resolve('@blocksuite/affine-components/caption'), require.resolve('@blocksuite/affine-components/context-menu'), require.resolve('@blocksuite/affine-components/date-picker'), require.resolve('@blocksuite/affine-components/drag-indicator'), ], affine: [ require.resolve('@blocksuite/affine-shared'), require.resolve('@blocksuite/affine-model'), require.resolve('@blocksuite/affine-block-list'), require.resolve('@blocksuite/affine-block-paragraph'), require.resolve('@blocksuite/affine-block-surface'), require.resolve('@blocksuite/data-view'), ], datefns: [path.dirname(require.resolve('date-fns'))], dompurify: [path.dirname(require.resolve('dompurify'))], shiki: [path.dirname(require.resolve('@shikijs/core'))], dotLottie: [path.dirname(require.resolve('@lottiefiles/dotlottie-wc'))], unified: [ path.dirname(require.resolve('unified')), path.dirname(require.resolve('rehype-parse')), path.dirname(require.resolve('rehype-stringify')), path.dirname(require.resolve('remark-parse')), path.dirname(require.resolve('remark-stringify')), path.dirname(require.resolve('mdast-util-gfm-autolink-literal')), path.dirname(require.resolve('mdast-util-gfm-strikethrough')), path.dirname(require.resolve('mdast-util-gfm-table')), path.dirname(require.resolve('mdast-util-gfm-task-list-item')), path.dirname(require.resolve('micromark-extension-gfm-autolink-literal')), path.dirname(require.resolve('micromark-extension-gfm-strikethrough')), path.dirname(require.resolve('micromark-extension-gfm-table')), path.dirname(require.resolve('micromark-extension-gfm-task-list-item')), path.dirname(require.resolve('micromark-util-combine-extensions')), ], blocks: [ require.resolve('@blocksuite/blocks'), require.resolve('@blocksuite/blocks/schemas'), ], presets: [require.resolve('@blocksuite/presets')], common: [ require.resolve('@blocksuite/icons/lit'), require.resolve('@toeverything/theme'), require.resolve('@toeverything/y-indexeddb'), require.resolve('@preact/signals-core'), require.resolve('@lit/context'), require.resolve('lit'), require.resolve('zod'), require.resolve('minimatch'), require.resolve('nanoid'), require.resolve('yjs'), ], }; const clearSiteDataPlugin = () => ({ name: 'clear-site-data', configureServer(server) { server.middlewares.use((req, res, next) => { if (req.url === '/Clear-Site-Data') { res.statusCode = 200; res.setHeader('Clear-Site-Data', '"*"'); } next(); }); }, }) as Plugin; // https://vitejs.dev/config/ export default ({ mode }) => { process.env = { ...process.env, ...loadEnv(mode, __dirname, '') }; return defineConfig({ envDir: __dirname, define: { 'import.meta.env.PLAYGROUND_SERVER': JSON.stringify( process.env.PLAYGROUND_SERVER ?? 'http://localhost:8787' ), 'import.meta.env.PLAYGROUND_WS': JSON.stringify( process.env.PLAYGROUND_WS ?? 'ws://localhost:8787' ), }, plugins: [ hmrPlugin, sourcemapExclude(), enableIstanbul && istanbul({ cwd: fileURLToPath(new URL('../..', import.meta.url)), include: ['packages/**/src/*'], exclude: [ 'node_modules', 'tests', fileURLToPath(new URL('.', import.meta.url)), ], forceBuildInstrument: true, }), wasm(), clearSiteDataPlugin(), ], esbuild: { target: 'es2018', }, resolve: { extensions: ['.ts', '.js'], }, build: { target: 'es2022', sourcemap: true, rollupOptions: { cache: false, maxParallelFileOps: Math.max(1, cpus().length - 1), onwarn(warning, defaultHandler) { if (['EVAL', 'SOURCEMAP_ERROR'].includes(warning.code)) { return; } defaultHandler(warning); }, input: { main: resolve(__dirname, 'index.html'), 'starter/': resolve(__dirname, 'starter/index.html'), 'examples/basic/page': resolve( __dirname, 'examples/basic/page/index.html' ), 'examples/basic/edgeless': resolve( __dirname, 'examples/basic/edgeless/index.html' ), 'examples/multiple-editors/page-page': resolve( __dirname, 'examples/multiple-editors/page-page/index.html' ), 'examples/multiple-editors/page-edgeless': resolve( __dirname, 'examples/multiple-editors/page-edgeless/index.html' ), 'examples/multiple-editors/edgeless-edgeless': resolve( __dirname, 'examples/multiple-editors/edgeless-edgeless/index.html' ), 'examples/inline': resolve(__dirname, 'examples/inline/index.html'), }, treeshake: true, output: { sourcemapIgnoreList: relativeSourcePath => { const normalizedPath = path.normalize(relativeSourcePath); return normalizedPath.includes('node_modules'); }, manualChunks(id, { getModuleInfo }) { for (const group of Object.keys(chunkGroups)) { const deps = chunkGroups[group]; if (isDepInclude(id, deps, [], getModuleInfo)) { if (chunkSizeReport && id.includes('node_modules')) { console.log(group + ':', id); console.log( group + ':', fs.statSync(id.replace('\x00', '').replace(/\?.*/, '')) .size / 1024, 'KB' ); } return group; } } }, }, }, }, }); };