Overview

You do not install Node, Vite, or esbuild. spliceFull is the production bundle Scala.js 1.21 asked you to get from Vite: type-aware Scala minify, then a general JS minifier that leaves protocol names (render, setState) and class extends Error alone. The minifier is a pinned esbuild binary. First spliceFull fetches it through Coursier for this OS/arch, checks sha256, and runs it. A clone of the project is enough. JDK and sbt were already required.

Scala.js lets you write import "preact" from Scala, usually with @JSImport("preact") (or require for CommonJS). The linker then emits a JavaScript file that still says import "preact". A browser cannot resolve that name. There is no node_modules folder, and the browser does not ask npm what "preact" means. The leftover name is a specifier. sbt-splice points each specifier at pinned bytes (a file in the repo, a WebJar, or a CDN/GitHub download with sha256) and writes one JavaScript file for a <script> tag.

If the program has no npm imports, leave spliceLibs empty. spliceFull is still the production file. NoModule is the natural linker kind in that case.

Fast vs full

spliceFast is development: concat, usually seconds. spliceFull is production: Scala.js minify, then pinned esbuild so the file is small. That pass runs even when spliceLibs is empty. spliceClosure is optional Closure advanced (unused-vendor DCE). Vanilla Scala.js fastLinkJS / fullLinkJS are unchanged; splice writes its own output next to them.

List("spliceFast" -> "development", "spliceFull" -> "production minify", "spliceClosure" -> "optional Closure")
List((spliceFast,development), (spliceFull,production minify), (spliceClosure,optional Closure))

If you already use Vite

You can drop Node from the Scala.js production path. Vite's minify leaves JS property names alone and keeps class / super(). So does spliceFull. What you do not get is Vite's ESM tree-shaker walking node_modules. Splice never reads package.json, never runs npm, and never leaves import "preact" for a bundler to fix. Libraries are sealed pins. The output is one <script> file.

How that compares to Closure, and why property renaming stays off, is on Production minify.