Getting started
You need Node.js 22.18 or newer (the extraction scripts import TypeScript directly and rely on Node's built-in type stripping) and git. A StarCraft installation is optional: the editor runs without the game's graphics, and the first run offers to download them.
git clone https://github.com/scm-js/scm-js
cd scm-js
npm install
npm run extract # optional: the game's graphics, from an installation you own
npm run dev # http://localhost:5173
The first npm run dev or npm run build needs a network connection: a predev /
prebuild hook fetches the default plugins at their pinned versions into the
gitignored plugins/ folder, where the build compiles them in (see
Defaults and vendoring). After that it is offline. The same
hook reports what game data is on disk and warns, rather than fails, when there is none.
npm run extract looks for StarDat.mpq and BrooDat.mpq on its own or takes a path;
the details and what comes out are in game-data.md.
Without it terrain is flat colours and units are markers, and everything else works.
Everyday commands#
npm run dev # Vite dev server
npm run build # tsc -b (the type-check) + vite build → dist/
npm run preview # serve dist/ locally
npm run lint # oxlint; does not type-check
npm test # vitest, a few seconds, no browser
npm run test:watch
npm run test:maps # write tests/maps again from the code that makes them
npx vitest run tests/chk.test.ts # one file
npx vitest run -t "flood fill" # tests matching a name
npm run check:assets # what game data is on disk
npm run docs:reference # rewrite the generated tables of the trigger and CHK references
npm run build is the type-check: tsc -b covers the app, the scripts and the desktop
main process (tsconfig.app.json, tsconfig.node.json, tsconfig.desktop.json). The
app config is strict: noUnusedLocals, noUnusedParameters, verbatimModuleSyntax (so
import type for types) and erasableSyntaxOnly (so no enums and no constructor
parameter properties). Lint and build both run on every push, so run them before one.
Dev deep-links#
Query parameters jump straight to a UI state, which is the fastest way to iterate on a screen or take a screenshot:
/?nosplash skip the splash
/?nosplash&layer=units select a layer: terrain, doodads, units, sprites, locations, fog, clipboard
/?nosplash&dialog=playerSettings open a dialog by id; repeatable
/?nosplash&zoom=0.5&tileset=ice zoom level and tileset
/?nosplash&mode=tile terrain palette mode: isom, rect, tile
/?nosplash&layer=fog&fogPlayer=3 view and paint one player's fog
An unknown dialog id is reported in the console with the list of valid ones.
In development, React 19's "Components" performance track is switched off, because it
serialises every render's props and turned mounting the chrome into seconds of blocked
main thread. Set VITE_REACT_TRACKS=1 to have it back when profiling renders.