import { promises as fsp } from 'node:fs' import path from 'node:path' import process from 'node:process' import { pathToFileURL } from 'node:url' import { build } from 'esbuild' import { importFileDefault } from 'vuepress/utils' import type { ThemeConfig } from '../../shared/theme-data.js' import { hash } from '../utils/index.js' export async function compiler(configPath?: string, ): Promise<{ config: ThemeConfig dependencies: string[] }> { if (!configPath) { return { config: {}, dependencies: [] } } const dirnameVarName = '__vite_injected_original_dirname' const filenameVarName = '__vite_injected_original_filename' const importMetaUrlVarName = '__vite_injected_original_import_meta_url' const result = await build({ absWorkingDir: process.cwd(), entryPoints: [configPath], outfile: 'out.js', write: false, target: ['node18'], platform: 'node', bundle: true, format: 'esm', sourcemap: 'inline', metafile: true, define: { '__dirname': dirnameVarName, '__filename': filenameVarName, 'import.meta.url': importMetaUrlVarName, }, plugins: [ { name: 'externalize-deps', setup(build) { build.onResolve({ filter: /.*/ }, ({ path: id }) => { // externalize bare imports if (id[0] !== '.' && !path.isAbsolute(id)) { return { external: true } } return null }) }, }, { name: 'inject-file-scope-variables', setup(build) { build.onLoad({ filter: /\.[cm]?[jt]s$/ }, async (args) => { const contents = await fsp.readFile(args.path, 'utf8') const injectValues = `const ${dirnameVarName} = ${JSON.stringify( path.dirname(args.path), )};` + `const ${filenameVarName} = ${JSON.stringify(args.path)};` + `const ${importMetaUrlVarName} = ${JSON.stringify( pathToFileURL(args.path).href, )};` return { loader: args.path.endsWith('ts') ? 'ts' : 'js', contents: injectValues + contents, } }) }, }, ], }) const { text } = result.outputFiles[0] const tempFilePath = `${configPath}.${hash(text)}.mjs` let config: ThemeConfig try { await fsp.writeFile(tempFilePath, text) config = await importFileDefault(tempFilePath) } finally { await fsp.rm(tempFilePath) } return { config, dependencies: Object.keys(result.metafile?.inputs ?? {}), } }