refactor(build): share Monaco public asset copy helper

This commit is contained in:
Shantur Rathore
2026-02-10 10:49:05 +00:00
parent ee9da95044
commit 0d96a9f9ff
6 changed files with 191 additions and 164 deletions

View File

@@ -0,0 +1,7 @@
export type CopyMonacoPublicAssetsParams = {
uiRendererRoot: string
warn?: (message: string) => void
sourceRoots?: string[]
}
export function copyMonacoPublicAssets(params: CopyMonacoPublicAssetsParams): void

View File

@@ -0,0 +1,97 @@
import fs from "fs"
import { resolve } from "path"
/**
* Copy Monaco's AMD `min/vs` assets into the UI renderer public folder.
*
* Monaco is loaded at runtime via `/monaco/vs/loader.js`. These assets are gitignored
* and generated on demand in dev/build so the repo stays clean.
*
* @param {object} params
* @param {string} params.uiRendererRoot Absolute path to `packages/ui/src/renderer`.
* @param {(message: string) => void} [params.warn] Warning logger.
* @param {string[]} [params.sourceRoots] Optional override list of `.../monaco-editor/min/vs` roots.
*/
export function copyMonacoPublicAssets(params) {
const uiRendererRoot = params?.uiRendererRoot
if (!uiRendererRoot) {
throw new Error("copyMonacoPublicAssets: uiRendererRoot is required")
}
const warn = params?.warn ?? ((message) => console.warn(message))
const publicDir = resolve(uiRendererRoot, "public")
const destRoot = resolve(publicDir, "monaco/vs")
const candidates =
params?.sourceRoots?.length > 0
? params.sourceRoots
: [
// Workspace root hoisted deps.
resolve(process.cwd(), "node_modules/monaco-editor/min/vs"),
// UI package local deps (covers non-hoisted installs).
resolve(process.cwd(), "packages/ui/node_modules/monaco-editor/min/vs"),
]
const sourceRoot = candidates.find((p) => fs.existsSync(resolve(p, "loader.js")))
if (!sourceRoot) {
warn("Monaco source directory not found; skipping copy")
return
}
const copyRecursive = (src, dest) => {
const stat = fs.statSync(src)
if (stat.isDirectory()) {
fs.mkdirSync(dest, { recursive: true })
for (const entry of fs.readdirSync(src)) {
copyRecursive(resolve(src, entry), resolve(dest, entry))
}
return
}
fs.copyFileSync(src, dest)
}
// Keep the working tree clean; these assets are generated.
try {
fs.rmSync(destRoot, { recursive: true, force: true })
} catch {
// ignore
}
fs.mkdirSync(destRoot, { recursive: true })
// Copy core Monaco runtime.
for (const dir of ["base", "editor", "platform"]) {
const src = resolve(sourceRoot, dir)
if (fs.existsSync(src)) {
copyRecursive(src, resolve(destRoot, dir))
}
}
// loader.js is required.
copyRecursive(resolve(sourceRoot, "loader.js"), resolve(destRoot, "loader.js"))
// Copy baseline rich language packages + workers.
for (const lang of ["typescript", "html", "json", "css"]) {
const src = resolve(sourceRoot, "language", lang)
if (fs.existsSync(src)) {
copyRecursive(src, resolve(destRoot, "language", lang))
}
}
// Copy baseline basic tokenizers.
for (const lang of ["python", "markdown", "cpp", "kotlin"]) {
const src = resolve(sourceRoot, "basic-languages", lang)
if (fs.existsSync(src)) {
copyRecursive(src, resolve(destRoot, "basic-languages", lang))
}
}
// Copy monaco.contribution.js entrypoints (needed by some loads).
const monacoContribution = resolve(sourceRoot, "basic-languages", "monaco.contribution.js")
if (fs.existsSync(monacoContribution)) {
copyRecursive(monacoContribution, resolve(destRoot, "basic-languages", "monaco.contribution.js"))
}
const underscoreContribution = resolve(sourceRoot, "basic-languages", "_.contribution.js")
if (fs.existsSync(underscoreContribution)) {
copyRecursive(underscoreContribution, resolve(destRoot, "basic-languages", "_.contribution.js"))
}
}

View File

@@ -3,84 +3,11 @@ import { defineConfig } from "vite"
import solid from "vite-plugin-solid"
import { VitePWA } from "vite-plugin-pwa"
import { resolve } from "path"
import { copyMonacoPublicAssets } from "./scripts/monaco-public-assets.js"
const uiPackageJson = JSON.parse(fs.readFileSync(resolve(__dirname, "package.json"), "utf-8")) as { version?: string }
const uiVersion = uiPackageJson.version ?? "0.0.0"
function copyMonacoPublicAssets(opts: { warn: (message: string) => void }) {
const publicDir = resolve(__dirname, "src/renderer/public")
const destRoot = resolve(publicDir, "monaco/vs")
const candidates = [
resolve(__dirname, "../../node_modules/monaco-editor/min/vs"),
resolve(__dirname, "node_modules/monaco-editor/min/vs"),
]
const sourceRoot = candidates.find((p) => fs.existsSync(resolve(p, "loader.js")))
if (!sourceRoot) {
opts.warn("Monaco source directory not found; skipping copy")
return
}
fs.mkdirSync(destRoot, { recursive: true })
const copyRecursive = (src: string, dest: string) => {
const stat = fs.statSync(src)
if (stat.isDirectory()) {
fs.mkdirSync(dest, { recursive: true })
for (const entry of fs.readdirSync(src)) {
copyRecursive(resolve(src, entry), resolve(dest, entry))
}
return
}
fs.copyFileSync(src, dest)
}
// Keep the working tree clean; these assets are generated.
try {
fs.rmSync(destRoot, { recursive: true, force: true })
} catch {
// ignore
}
fs.mkdirSync(destRoot, { recursive: true })
// Copy core Monaco runtime.
for (const dir of ["base", "editor", "platform"] as const) {
const src = resolve(sourceRoot, dir)
if (fs.existsSync(src)) {
copyRecursive(src, resolve(destRoot, dir))
}
}
// loader.js is required.
copyRecursive(resolve(sourceRoot, "loader.js"), resolve(destRoot, "loader.js"))
// Copy baseline rich language packages + workers.
for (const lang of ["typescript", "html", "json", "css"] as const) {
const src = resolve(sourceRoot, "language", lang)
if (fs.existsSync(src)) {
copyRecursive(src, resolve(destRoot, "language", lang))
}
}
// Copy baseline basic tokenizers.
for (const lang of ["python", "markdown", "cpp", "kotlin"] as const) {
const src = resolve(sourceRoot, "basic-languages", lang)
if (fs.existsSync(src)) {
copyRecursive(src, resolve(destRoot, "basic-languages", lang))
}
}
// Copy monaco.contribution.js entrypoints (needed by some loads).
const monacoContribution = resolve(sourceRoot, "basic-languages", "monaco.contribution.js")
if (fs.existsSync(monacoContribution)) {
copyRecursive(monacoContribution, resolve(destRoot, "basic-languages", "monaco.contribution.js"))
}
const underscoreContribution = resolve(sourceRoot, "basic-languages", "_.contribution.js")
if (fs.existsSync(underscoreContribution)) {
copyRecursive(underscoreContribution, resolve(destRoot, "basic-languages", "_.contribution.js"))
}
}
export default defineConfig({
root: "./src/renderer",
plugins: [
@@ -90,10 +17,24 @@ export default defineConfig({
// Ensure Monaco's AMD assets exist in `root/public` for both dev server and builds.
// These files are gitignored and generated on demand.
configureServer(server) {
copyMonacoPublicAssets({ warn: (msg) => server.config.logger.warn(msg) })
copyMonacoPublicAssets({
uiRendererRoot: resolve(__dirname, "src/renderer"),
warn: (msg) => server.config.logger.warn(msg),
sourceRoots: [
resolve(__dirname, "../../node_modules/monaco-editor/min/vs"),
resolve(__dirname, "node_modules/monaco-editor/min/vs"),
],
})
},
buildStart() {
copyMonacoPublicAssets({ warn: (msg) => this.warn(msg) })
copyMonacoPublicAssets({
uiRendererRoot: resolve(__dirname, "src/renderer"),
warn: (msg) => this.warn(msg),
sourceRoots: [
resolve(__dirname, "../../node_modules/monaco-editor/min/vs"),
resolve(__dirname, "node_modules/monaco-editor/min/vs"),
],
})
},
},
{