Bundling the Loom language server
Vite bundles the language server entry, src/index.ts, into a single dist/loom-server.js. The bundle targets Node 20 in CommonJS form, the shape a Node host loads it as.
The build
Some of the server's dependencies — the vscode-*-languageservice packages and jsonc-parser — ship only UMD bundles, the older module format that rolldown cannot follow statically. The umd2esm plugin rewrites each such import to its ESM sibling so the bundler can inline it. This is the same trick volarjs/starter uses.
vscode-markdown-languageservice, which ProseLanguage's Markdown service pulls in, imports vscode-uri by its default binding, which that package's ES-module build does not provide. An alias resolves vscode-uri to its UMD build instead, where the bundler's CommonJS interop supplies one.
import { defineConfig } from 'vite'
import { builtinModules, createRequire } from 'node:module'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const here = dirname(fileURLToPath(import.meta.url))
const requireFromHere = createRequire(import.meta.url)
const nodeBuiltins = [...builtinModules, ...builtinModules.map((m) => `node:${m}`)]
const umd2esm = {
name: 'umd2esm',
enforce: 'pre' as const,
resolveId(source: string, importer: string | undefined) {
if (/^(vscode-.*-languageservice|jsonc-parser)/.test(source)) {
const fromDir = importer ? dirname(importer) : here
const resolved = requireFromHere.resolve(source, { paths: [fromDir] })
return resolved.replace(/\/umd\//, '/esm/').replace(/\\umd\\/g, '\\esm\\')
}
return null
},
}Node's built-in modules stay external — they are present in every Node runtime — and everything else is inlined. The single entry builds to dist/loom-server.js.
export default defineConfig({
plugins: [umd2esm],
resolve: {
conditions: ['node'],
mainFields: ['main', 'module'],
alias: {
'vscode-uri': requireFromHere
.resolve('vscode-uri')
.replace(/esm[\\/]index\.mjs$/, 'umd/index.js'),
},
},
build: {
target: 'node20',
outDir: 'dist',
emptyOutDir: true,
sourcemap: true,
minify: false,
lib: {
entry: resolve(here, 'src/index.ts'),
formats: ['cjs'],
fileName: () => 'loom-server.js',
},
rollupOptions: { external: nodeBuiltins },
},
})