Skip to content

Tooling

SuperJS ships a unified CLI that covers every stage of the development workflow: compilation, linting, formatting, and testing. There are no separate packages to install or plugins to configure.

Installation

Install the CLI from npm — a self-contained bundle with no runtime dependencies:

npm install -g @superjsorg/cli

This makes the superjs command available globally.

CLI Reference

Commands take one or more file or directory paths as positional arguments — a directory expands to every .sjs file under it.

superjs build

Compiles .sjs files to JavaScript (plus source maps).

superjs build <files...> [--out-dir <dir>] [--source-map none|inline|external] [--watch] [--no-cache]

Flags:

FlagDescription
--out-dir <dir>Output directory (default: dist)
--source-map <mode>none (default), inline, or external (.js.map alongside output)
--watchWatch mode — recompile on file change
--no-cacheDisable the incremental build cache

Examples:

# Compile a single file to ./dist
superjs build src/main.sjs

# Compile a whole directory with external source maps
superjs build src --out-dir dist --source-map external

# Watch mode during development
superjs build src --out-dir dist --watch

superjs check

Type-checks without emitting output. Exits non-zero if any errors are found.

superjs check <files...> [--format pretty|json]
FlagDescription
--format <mode>pretty (default, colored terminal) or json (machine-readable)
superjs check src                 # type-check a directory
superjs check src --format json   # machine-readable diagnostics for CI

superjs translate

Translates TypeScript .d.ts declaration files into SuperJS .d.sjs, so you can consume existing typed packages. Each file is written next to its source, or under --out-dir.

superjs translate types.d.ts                 # → types.d.sjs
superjs translate types.d.ts --out-dir gen   # → gen/types.d.sjs
FlagDescription
--out-dir <dir>Write the .d.sjs output to this directory instead of alongside the source

Enums translate to sum types, and intersections of object literals merge into one object type. TypeScript constructs SuperJS doesn't model (conditional, mapped, infer, any, unmergeable intersections, …) degrade to dynamic, and top-level functions/classes/namespaces are flagged as not-yet-translated — every case is reported as a warning, never silently dropped:

$ superjs translate types.d.ts
  warning: intersection `A & B` mapped to `dynamic` (reason: intersection-not-mergeable)
translated types.d.ts → types.d.sjs

superjs add

Resolves an installed npm package's types into SuperJS .d.sjs and wires them into your project. Finds the package's .d.ts (its own types entry or the DefinitelyTyped @types/<pkg> fallback), translates it to node_modules/@superjs/types/<pkg>/index.d.sjs, and maps the import specifier in superjs.config.json paths.

npm install fastify
superjs add fastify        # → node_modules/@superjs/types/fastify/index.d.sjs

Unmappable TypeScript forms degrade to dynamic and are reported — never silently dropped. add prints a typed-surface estimate (how much of the API kept a real type) and records it for superjs doctor. (Hand-curated wrappers for the top packages land in a later Stage 2 sprint; today add always translates the published .d.ts.)

superjs format

Rewrites .sjs files in the canonical style (2-space indent, semicolons, one statement per line). --check reports what would change without writing — for CI. The formatter reparses its own output and only rewrites a file when the result is provably the same program, so it never corrupts code.

superjs format src/            # format in place
superjs format src/ --check    # CI: non-zero exit if anything would change

A gitignore-style .sjsignore at the project root excludes files from directory walks (for format, lint, check, build, and doc); explicitly-named files are always processed. See the formatter integration guide for the .sjsignore syntax and the Prettier / Husky coexistence story.

superjs lint

Reports style findings (SJS-L*) and exits non-zero when any are present, so it gates CI. Current rules: prefer-const (L001), no-var (L002), ===/!== (L003), for…of over for…in (L004), no-debugger (L005), no-empty-match (L006), no-redundant-match-arm (L007), prefer-arrow-callback (L008), no-unused-import (L009), import-order (L010), no-unused-var (L012), no-explicit-dynamic (L013), no-shadowing (L014), no-floating-promise (L015), no-unhandled-result (L016), prefer-result-over-throw (L017), and no-mixed-spaces-tabs (L018) — 17 rules in total.

superjs lint src/                 # human-readable
superjs lint src/ --format json   # machine-readable for CI
superjs lint src/ --fix           # apply auto-fixes (no-var → let, drop debugger) in place

superjs doc

Generates API documentation from a module's exported declarations — a built-in JSDoc / TypeDoc alternative. SJS types are explicit and sound, so the signature is the documentation; a leading doc comment adds prose and tags. Outputs Markdown (default) or JSON.

superjs doc src/api.sjs            # Markdown to stdout
superjs doc src/ --out-dir docs    # one .md per file
superjs doc src/ --format json     # machine-readable

(MVP: signatures + doc comments. HTML site / --serve / validated @example blocks are later work — see ADR-009.)

superjs explain

Prints the full spec write-up for a diagnostic code (description, example, fix).

superjs explain SJS-E001
superjs explain E007        # the SJS- prefix is optional

superjs init

Writes a default superjs.config.json into the current directory.

superjs doctor

Reports environment health — Node version, config presence and validity — plus a per-package typed-surface report for everything pulled in with superjs add.

superjs lsp

Starts the SuperJS language server over stdio for editor integration. Speaks standard LSP and implements the full M1 method set: diagnostics, hover, go-to-definition, document outline, folding, completion, signature help, semantic tokens, and formatting.

superjs lsp        # JSON-RPC over stdin/stdout — launched by your editor

Normally an editor spawns it. The VS Code extension does so automatically; for Neovim, Helix, or any LSP-aware editor, point the client at superjs lsp — see the Editor Setup guide for ready-to-paste configs.

Planned commands

These are reserved and currently print a not-yet-implemented notice:

  • superjs test (Stage 5) — a Jest alternative for .sjs. Planned as both @superjs/jest-transform / @superjs/vitest-transform (run .sjs tests in your existing Jest/Vitest) and a native zero-config superjs test runner.

Full plan on the roadmap.

Project Configuration

SuperJS looks for a superjs.config.json file in the project root. All fields are optional and have defaults. Compiler settings are nested under compilerOptions:

{
  "compilerOptions": {
    "strict": false,
    "target": "ES2022",
    "outDir": "dist",
    "sourceMap": "none",
    "noEmitOnError": false
  },
  "jsx": {
    "runtime": "automatic",
    "importSource": "react"
  }
}
FieldTypeDefaultDescription
compilerOptions.strictbooleanfalseEnable strict mode (promotes SJS-W001 implicit-dynamic warnings to errors)
compilerOptions.targetstring"ES2022"Output target: ES2020ES2024 or ESNext
compilerOptions.outDirstringOutput directory for compiled .js files
compilerOptions.sourceMapstring"none"none, inline, or external
compilerOptions.noEmitOnErrorbooleanfalseSkip emitting JS if type errors are present
jsx.runtimestring"automatic"automatic (react/jsx-runtime) or classic (React.createElement)
jsx.importSourcestring"react"Import source for the automatic JSX runtime

The full schema (including paths, output.variants, lsp, and env) lives in specs/config-schema.json.

CLI flags always override config file values.

Error Output

Terminal Output (default)

By default, superjs build and superjs check print colored diagnostics to the terminal:

error SJS-E001  src/main.sjs:12:5
  Null/undefined assigned to non-nullable type 'string'.

warning SJS-W001  src/utils.sjs:8:10
  Implicit 'dynamic' type — enable strict mode to enforce annotations.

Each diagnostic includes:

  • Severity (error / warning)
  • Diagnostic code (e.g., SJS-E001)
  • File path and line/column position
  • Human-readable message

JSON Mode (--format json)

Pass --format json to superjs check to receive machine-readable diagnostics, suitable for editor integrations, CI pipelines, and automated tooling:

{"severity":"error","code":"SJS-E001","file":"src/main.sjs","line":12,"col":5,"message":"Null/undefined assigned to non-nullable type 'string'."}
{"severity":"warning","code":"SJS-W001","file":"src/utils.sjs","line":8,"col":10,"message":"Implicit 'dynamic' type."}

Diagnostic Codes

CodeSeverityDescription
SJS-E001errorNull or undefined assigned to a non-nullable type
SJS-E002errorType mismatch on assignment or return
SJS-W001warningImplicit dynamic type (strict mode only)
SJS-E007errorNon-exhaustive match — a sum type variant is not handled

SJS-E001 — Null Safety

SJS types are non-nullable by default. Use T? to declare a nullable type:

// error: 'string' is not nullable
const name: string = null  // SJS-E001

// correct: use T? for nullable
const name: string? = null

SJS-E002 — Type Mismatch

const count: number = "hello"  // SJS-E002: expected number, got string

SJS-W001 — Implicit Dynamic (strict mode)

// in strict mode, this emits SJS-W001
function process(data) {
  return data
}

// fix: annotate the parameter
function process(data: string): string {
  return data
}

SJS-E007 — Non-Exhaustive Match

type Status = Active | Inactive | Pending

const s: Status = Active

// SJS-E007: 'Pending' variant not handled
const label = match s {
  Active   => "active",
  Inactive => "inactive",
}

Exit Codes

CodeMeaning
0Success — no errors
1One or more errors found
2CLI usage error (bad flags, missing arguments)