|
| 1 | +import { spawn, SpawnOptionsWithoutStdio } from "child_process"; |
| 2 | +import type { Plugin as VitePlugin } from "vite"; |
| 3 | + |
| 4 | +// Utility to invoke a given sbt task and fetch its output |
| 5 | +function printSbtTask(task: string, cwd?: string): Promise<string> { |
| 6 | + const args = ["--batch", `print ${task}`]; |
| 7 | + const options: SpawnOptionsWithoutStdio = { |
| 8 | + cwd: cwd, |
| 9 | + }; |
| 10 | + const child = process.platform === 'win32' |
| 11 | + ? spawn("sbt.bat", args.map(x => `"${x}"`), {shell: true, ...options}) |
| 12 | + : spawn("sbt", args, options); |
| 13 | + |
| 14 | + let fullOutput: string = ''; |
| 15 | + |
| 16 | + child.stdout.setEncoding('utf-8'); |
| 17 | + child.stdout.on('data', data => { |
| 18 | + fullOutput += data; |
| 19 | + process.stdout.write(data); // tee on my own stdout |
| 20 | + }); |
| 21 | + |
| 22 | + child.stderr.setEncoding('utf-8'); |
| 23 | + child.stderr.on('data', data => { |
| 24 | + process.stderr.write(data); // tee on my own stderr |
| 25 | + }); |
| 26 | + |
| 27 | + return new Promise((resolve, reject) => { |
| 28 | + child.on('close', code => { |
| 29 | + if (code !== 0) |
| 30 | + reject(new Error(`sbt invocation for Scala.js compilation failed with exit code ${code}`)); |
| 31 | + else |
| 32 | + resolve(fullOutput.trimEnd().split('\n').at(-1)!); |
| 33 | + }) |
| 34 | + }) |
| 35 | +} |
| 36 | + |
| 37 | +export interface ScalaJSPluginOptions { |
| 38 | + projectID?: string, |
| 39 | + cwd?: string, |
| 40 | +} |
| 41 | + |
| 42 | +export default function scalaJSPlugin(options: ScalaJSPluginOptions = {}): VitePlugin { |
| 43 | + const { projectID, cwd } = options; |
| 44 | + |
| 45 | + let isDev = false; // when resolving the Vite config, this may turn into true |
| 46 | + let scalaJSOutputDir: string; |
| 47 | + |
| 48 | + return { |
| 49 | + name: "scalajs:sbt-scalajs-plugin", |
| 50 | + |
| 51 | + // Vite-specific |
| 52 | + configResolved(resolvedConfig) { |
| 53 | + isDev = resolvedConfig.mode === 'development'; |
| 54 | + }, |
| 55 | + |
| 56 | + // standard Rollup |
| 57 | + async buildStart(options) { |
| 58 | + const task = isDev ? "fastLinkJSOutput" : "fullLinkJSOutput"; |
| 59 | + const projectTask = projectID ? `${projectID}/${task}` : task; |
| 60 | + scalaJSOutputDir = await printSbtTask(projectTask, cwd); |
| 61 | + }, |
| 62 | + |
| 63 | + // standard Rollup |
| 64 | + resolveId(source, importer, options) { |
| 65 | + const parts = /^scalajs:(.*)$/g.exec(source); |
| 66 | + if (!parts) |
| 67 | + return null; |
| 68 | + |
| 69 | + return `${scalaJSOutputDir}/${parts[1]}`; |
| 70 | + }, |
| 71 | + }; |
| 72 | +} |
0 commit comments