https://github.com/soundscript-lang/soundscript
Sound checker and language tooling for TypeScript.
https://github.com/soundscript-lang/soundscript
lsp soundscript static-analysis type-checker typescript
Last synced: about 2 months ago
JSON representation
Sound checker and language tooling for TypeScript.
- Host: GitHub
- URL: https://github.com/soundscript-lang/soundscript
- Owner: soundscript-lang
- License: isc
- Created: 2026-04-01T23:56:18.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-22T20:40:49.000Z (about 2 months ago)
- Last Synced: 2026-04-22T22:28:32.219Z (about 2 months ago)
- Topics: lsp, soundscript, static-analysis, type-checker, typescript
- Language: TypeScript
- Homepage: https://soundscript.dev/docs
- Size: 14.6 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
Awesome Lists containing this project
README
# soundscript
soundscript is a sound checker and language tooling layer for TypeScript projects.
Prebuilt macOS, Linux, and Windows CLI downloads are attached to GitHub releases.
The intended stable v1 surface is the checker, mixed `.ts` / `.sts` adoption story, the VS Code
extension, a very small stdlib, and a narrow macro surface. Broader macro work and Wasm remain
experimental.
For the fastest orientation on the builtin surface and the recommended coding patterns, see
[docs/reference/builtin-modules.md](docs/reference/builtin-modules.md) and
[docs/guides/idiomatic-soundscript.md](docs/guides/idiomatic-soundscript.md) and
[docs/guides/common-rewrites.md](docs/guides/common-rewrites.md).
It adds a second file type, `.sts`, for code checked under a stricter rule set. Ordinary `.ts` files
keep normal TypeScript behavior. The goal is incremental adoption: move the parts of a codebase you
care about into `.sts`, keep the rest as `.ts`, and make interop explicit.
## Status
The release-facing v1 surface is:
- `.sts` for sound code
- mixed `.ts` / `.sts` projects
- `soundscript check`
- LSP support
- import-scoped macro authoring through the compiler-provided `sts:macros` builtin module
- user-authored macro modules are `.macro.sts` compile-time plugin modules, not ordinary `.sts`,
`.ts`, or `.js` scripts
- compiler-owned builtin modules under `sts:*`, with the stable v1 surface centered on
`sts:prelude`, `sts:result`, `sts:match`, `sts:failures`, `sts:url`, `sts:fetch`, `sts:text`,
`sts:random`, `sts:json`, `sts:compare`, `sts:hash`, `sts:decode`, `sts:encode`, `sts:codec`,
`sts:derive`, `sts:async`, `sts:hkt`, `sts:typeclasses`, and `sts:macros`
- the canonical runtime/toolchain npm package `@soundscript/soundscript`, with emitted runtime and
TS interop imports under `@soundscript/soundscript/*`
This repository also contains broader implemented experimental work beyond the stable v1 contract.
That includes the `soundscript compile` path, experimental builtin surfaces such as `sts:numerics`,
`sts:value`, and `sts:experimental/*`, `#[newtype]` and `#[value]`, proof and framework macros like
`#sql`, `#css`, `#graphql`, and `#component`, and the broader public Wasm target/runtime matrix
work. Those surfaces exist in the repo, but they are not the stable release-facing v1 contract.
## Quick start
Build the CLI:
```bash
deno task build
./bin/soundscript --help
```
Start a new project:
```bash
./bin/soundscript init
./bin/soundscript check
```
That creates `src/main.sts` and a `tsconfig.json` that includes both `src/**/*.ts` and
`src/**/*.sts`.
Add soundscript to an existing TypeScript project:
```bash
./bin/soundscript init --mode existing
./bin/soundscript check --project tsconfig.soundscript.json
```
That creates a `tsconfig.soundscript.json` overlay so you can start adding `.sts` files without
rewriting the rest of the project.
For CI or tooling:
```bash
./bin/soundscript check --project tsconfig.soundscript.json --format json
./bin/soundscript check --project tsconfig.soundscript.json --format ndjson
./bin/soundscript deno run src/main.sts
./bin/soundscript explain SOUND1002
```
For local Node execution, use `@soundscript/register`:
```bash
node --import @soundscript/register src/main.sts
```
You can also opt selected `.ts` / `.tsx` / `.mts` / `.cts` files into local soundscript behavior
without renaming them:
```json
{
"soundscript": {
"include": ["src/**/*.ts", "src/**/*.tsx"]
}
}
```
`@soundscript/register` and `soundscript deno` expect `@soundscript/soundscript` to be installed in
the current project or an ancestor workspace, because the transformed graph imports the runtime
package.
For published libraries, the intended package shape is:
- ordinary ESM `js + d.ts` for TypeScript and runtime consumers
- shipped authored `.sts` source under `package.json#soundscript.exports`
- `soundscript build` as the canonical package emit flow
- `@soundscript/register` and `soundscript deno` as local runtime wrappers for mixed `.ts/.sts` apps
- explicit adapter packages for local source-driven apps and bundlers: `@soundscript/register`,
`@soundscript/bun-plugin`, `@soundscript/vite`, and `@soundscript/webpack-loader`
- source maps back to original `.sts` so stack traces and debuggers stay on authored source
The repository split is now:
- `soundscript` for the checker, CLI, LSP, runtime package, and generic project-transform support
used by first-party adapters
- `website` for the public docs site and mirrored reference pages
- `adapters` for the explicit adapter packages and their host-specific implementations
- `editors` for editor clients such as the VS Code extension and `@soundscript/tsserver-plugin`
The public website and docs now live in the standalone website repository:
[soundscript-lang/website](https://github.com/soundscript-lang/website).
The leaf adapter packages are no longer vendored in this repo. Use the standalone adapters
repository: [soundscript-lang/adapters](https://github.com/soundscript-lang/adapters).
The editor packages are no longer vendored in this repo. Use the standalone editors repository:
[soundscript-lang/editors](https://github.com/soundscript-lang/editors).
For package emit:
```bash
./bin/soundscript build --project tsconfig.soundscript.json
```
The intended platform design is Deno-inspired:
- prefer Web-standard APIs first
- keep stdlib modules small and composable
- expose portable globals and leaf modules such as `fetch`, `URL`, and text encoding where the
current runtime supports them
- keep host-specific behavior behind explicit wrappers instead of promising extra stable runtime
modules
This package model is designed for external macro-authored libraries and frameworks. The soundscript
repo keeps only small generic fixture coverage for packaged macro resolution and runtime
materialization; framework implementations now live in their own repositories.
Exit codes are:
- `0` for success with no blocking diagnostics
- `1` for project diagnostics or unsupported-code findings
- `2` for CLI usage, project setup/configuration failures, or unexpected internal tool errors
## Release automation
The canonical release version comes from [src/cli/cli.ts](src/cli/cli.ts).
For a normal public release, bump `VERSION` there, push `main`, then run the `Publish Release`
GitHub Actions workflow. It will:
- run the release checks
- prepare and publish the npm package set
- create and push the `v` tag
- create the GitHub release
- attach the platform CLI archives plus checksums
The workflow is set up for npm trusted publishing from GitHub Actions. npm must be configured to
trust the `publish-release.yml` workflow file in this repository for every package in the release
set:
- `@soundscript/cli-darwin-arm64`
- `@soundscript/cli-darwin-x64`
- `@soundscript/cli-linux-arm64`
- `@soundscript/cli-linux-x64`
- `@soundscript/cli-win32-x64`
- `@soundscript/soundscript`
- `soundscript`
If npm returns a `404` during `npm publish`, the usual cause is that the package's trusted-publisher
settings have not been added yet. The workflow also needs GitHub's `id-token: write` permission,
which is already configured in the workflow file.
You can bulk-configure the package set from a shell after logging in to npm with an account that has
write access to all seven packages:
```bash
for package in \
@soundscript/cli-darwin-arm64 \
@soundscript/cli-darwin-x64 \
@soundscript/cli-linux-arm64 \
@soundscript/cli-linux-x64 \
@soundscript/cli-win32-x64 \
@soundscript/soundscript \
soundscript
do
npm trust github "$package" \
--repo soundscript-lang/soundscript \
--file publish-release.yml \
--yes
sleep 2
done
```
If you need to reattach CLI archives to an existing release, use the separate `Backfill CLI Assets`
workflow.
## What `.sts` means
`.sts` files are checked in soundscript.
`.ts` files are left alone.
When `.sts` code imports ordinary TypeScript, JavaScript, or declaration-only packages, the import
needs `// #[interop]`:
```ts
// #[interop]
import { readConfig } from './legacy.ts';
```
From the other direction, `.ts` can import `.sts` without any annotation. It sees a projected public
TypeScript surface for the `.sts` module.
That is the adoption model. Existing TypeScript stays where it is. New or critical code can move
into `.sts`.
## Remaining rough edges
soundscript removes a large set of TypeScript unsoundness paths, but it still lives inside JS/TS
runtime semantics. The main remaining rough edges are:
- `null` vs `undefined`, especially at JSON, regex, and trusted-interop boundaries
- arbitrary foreign throws and rejections; expansion-enabled source normalizes `catch (error)` and
built-in Promise rejection handlers to plain `Error`, but other boundaries still require explicit
normalization such as `sts:failures.normalizeThrown(...)`
- stable v1 still defaults to ordinary JS `number` behavior, including `NaN`, `Infinity`, and `-0`;
the repo also contains experimental machine numerics work under `sts:numerics`, but that is
outside the stable v1 contract
- stable v1 still relies heavily on structural typing for same-shape interface and object values;
class nominality plus `#[newtype]` and `#[value]` exist in the repo, but the broader nominal and
value-semantics story remains outside the stable v1 contract
- there is no active effort to remove raw `null` from the language; it remains part of the honest
platform model
The release-facing contract for those boundaries is in
[docs/reference/v1-user-contract.md](docs/reference/v1-user-contract.md).
## Example
```ts
// src/math.sts
export function area(radius: number): number {
return Math.PI * radius * radius;
}
const raw = JSON.parse('{"radius": 3}');
if (
typeof raw === 'object' &&
raw !== null &&
'radius' in raw &&
typeof raw.radius === 'number'
) {
console.log(area(raw.radius));
}
```
There is no new syntax here. The difference is the checker.
## Commands
The main commands are:
- `soundscript init`
- `soundscript check`
- `soundscript build`
- `soundscript expand`
- `soundscript deno`
- `soundscript explain`
- `soundscript lsp`
The repo also ships `soundscript compile`. `compile` and the broader compiler / Wasm surface remain
experimental; the checker is still the main entry point.
## Editor support
The language server runs over stdio:
```bash
soundscript lsp
```
There is also a VS Code client in the separate editors repo:
```bash
git clone https://github.com/soundscript-lang/editors.git ../editors
cd ../editors/packages/vscode
npm install
npm run compile
```
Then:
1. open the `editors` repo root in VS Code
2. run the `Run soundscript extension` launch configuration
3. in the spawned Extension Development Host, use `File -> Open Folder...` to open the workspace you
want to test, for example `soundscript-example`
4. open an `.sts` file to confirm the `soundscript` language mode and TextMate grammar are active
Notes:
- the VS Code extension and `@soundscript/tsserver-plugin` live in
[soundscript-lang/editors](https://github.com/soundscript-lang/editors)
- work from `../editors/packages/vscode` when developing the extension locally
- the extension prefers a workspace-installed `soundscript` binary and falls back to PATH unless you
override `soundscript.server.command`
- `soundscript.server.*` settings only configure how the already-loaded extension launches the
language server; those settings do not make VS Code discover the extension
- custom grammar-based syntax highlighting currently applies only to `.sts`; `.ts` and `.tsx` use
the built-in TypeScript grammar and only pick up LSP features from soundscript
Current LSP support includes diagnostics, hover, signature help, definition, references, rename,
completions, document symbols, formatting, semantic tokens, and code actions / quick fixes.
## Repository layout
The repo also contains:
- checker and CLI
- `.ts` / `.sts` interop
- LSP
- macro work
- compiler / Wasm work
You do not need macros or Wasm to use soundscript as a checker.
## Development
The maintainer toolchain is Deno:
```bash
deno task build
deno task check
deno task fmt
deno task lint
deno task test
deno task verify
```
## Docs
Start here:
- [docs/architecture/spec.md](docs/architecture/spec.md)
- [docs/project/roadmap.md](docs/project/roadmap.md)
- [docs/README.md](docs/README.md)
- [docs/reference/v1-user-contract.md](docs/reference/v1-user-contract.md)