Files
scriptc/docs/content/docs/coverage.mdx
T

205 lines
10 KiB
Plaintext

---
title: "Coverage Reports"
---
`scriptc coverage` tells you how much of a program compiles before you build it. Use it when you bring an existing Node.js program to scriptc, add a dependency, or want CI to catch unsupported code early. It analyzes the program without producing an executable, counts the statements that compile, and groups anything unsupported by diagnostic code with a location and a hint.
<span id="static-programs" />
## Run a report
Pass the program's entry file:
```console
$ cat hello.ts
const who: string = process.argv.length > 2 ? process.argv[2] : "world";
console.log(`hello, ${who}`);
$ scriptc coverage hello.ts
statements analyzed 2
compile statically 2 (100%)
fully static — all analyzed statements compile to native code.
```
Every statement in this program compiles, so `scriptc build` will succeed. The counts describe this program only, not JavaScript or Node.js support in general.
<span id="report-fields" />
## Reading the report
When a program has unsupported operations, the report adds a group for each root cause. A group shows the diagnostic code, the reason, the first few sites in source order, and a hint. A generic declaration that fails for several type arguments appears once.
<table>
<thead><tr><th>Field</th><th>Meaning</th></tr></thead>
<tbody>
<tr><td><code>statements analyzed</code></td><td>Executable statements in application code, excluding dependency implementation code.</td></tr>
<tr><td><code>compile statically</code></td><td>Statements that compile to native instructions.</td></tr>
<tr><td><code>blockers</code></td><td>Unsupported operations with counts, diagnostic codes, reasons, locations, and hints.</td></tr>
<tr><td><code>in unreached code</code></td><td>Blockers in code the entry never reaches. A build never compiles that code, so these cannot fail it.</td></tr>
<tr><td><code>differs from Node</code></td><td>Sites that compile but can behave differently from Node.js. See <a href="#node-divergences">Node divergences</a>.</td></tr>
<tr><td><code>npm package compilation</code></td><td>Native package compilation outcomes and refusal reasons.</td></tr>
</tbody>
</table>
Blockers in unreached code and deferred operations in JavaScript packages are listed separately because they don't fail a build. A deferred operation throws a runtime error if it runs, so a successful build doesn't mean every API of a package works; see [unsupported code inside a package](/docs/dependencies#unsupported-code-inside-a-package).
If a function's signature can't compile, the statements in its body count as not compiling, so the percentage reflects how much of the program compiles.
<span id="programs-that-require-dynamic-execution" />
<span id="analyze-with-dynamic" />
<span id="embedded-nodejs-builtins" />
## Diagnostic codes
Coverage and `scriptc build` report the same diagnostic codes. Common ones include:
- `SC2020`: a declared standard-library or Node.js API has no supported compiler implementation.
- `SC2012`: this API form cannot compile natively.
- `SC2013`: a package cannot compile natively; inspect the package-specific reason and the [dependency guide](/docs/dependencies).
- `SC1100`: a value whose type is only known at runtime, such as `unknown`, is used where scriptc needs a specific type. Narrow or cast the value as the diagnostic suggests.
## Node divergences
Some code compiles but can behave differently than under Node.js. Coverage reports the cases it can detect as `SC6xxx` warnings, and `scriptc build` prints the same warnings after a successful build. They never fail a build, but `--fail-on=divergences` can fail a CI job on them.
- `SC6001`: an object passed to, or stored as, a narrower record type is copied, and the program then writes through the copy or compares it by identity.
- `SC6002`: an object literal's key order differs from its type's declaration order, and the program observes the order through `JSON.stringify`, `Object.keys`, `Object.values`, `Object.entries`, `for...in`, or console output.
- `SC6003`: `localeCompare` compares a string literal outside the built-in Latin-script collation.
- `SC6004`: a call passes a class instance to a function that casts it to an unrelated class.
- `SC6005`: a `Date` string literal uses a form the native parser does not accept.
## Gating builds and CI
By default, `scriptc coverage` exits with status 0 whenever the program type-checks, so you can use the report to track progress. To fail a CI job instead, add `--fail-on`:
```console
$ scriptc coverage src/main.ts --fail-on=blockers
```
`--fail-on=blockers` exits with status 1 when the program has blockers that would fail `scriptc build`. Blockers in unreached code and deferred operations don't count. `--fail-on=divergences` also fails on Node divergences. A program that doesn't type-check always exits with status 1.
Coverage caches its result. Running it again on an unchanged program, or after edits that only touch comments, reuses the previous verdict. Set `SCRIPTC_NO_CACHE=1` to turn the cache off.
## Machine-readable diagnostics
For editors, CI annotations, and other tools, `scriptc coverage --print=diagnostics` and `scriptc build --print=diagnostics` write one JSON document to stdout instead of the human-readable report. The exit status is unchanged. The document looks like this:
```json
{
"schema": "scriptc-diagnostics",
"schemaVersion": 1,
"compilerVersion": "0.3.0",
"command": "coverage",
"entry": "/work/app/src/main.ts",
"success": false,
"phase": "compile",
"stats": {
"statementsTotal": 13,
"statementsStatic": 12,
"statementsDynamic": 0,
"statementsFailed": 1,
"functionsSkipped": 0,
"percentStatic": 92
},
"groups": [
{
"id": "SC1031-c2cc8425",
"code": "SC1031",
"category": "unsupported",
"severity": "error",
"scope": "reached",
"message": "object destructuring of non-record values (the source is array-typed)",
"hint": "...",
"count": 1,
"primary": { "file": "/work/app/src/main.ts", "line": 13, "column": 7 },
"sites": [{ "file": "/work/app/src/main.ts", "line": 13, "column": 7 }]
}
],
"diagnostics": [
{
"code": "SC1031",
"category": "unsupported",
"severity": "error",
"scope": "reached",
"file": "/work/app/src/main.ts",
"line": 13,
"column": 7,
"endLine": 13,
"endColumn": 28,
"start": 352,
"end": 373,
"message": "object destructuring of non-record values (the source is array-typed) are not supported yet",
"hint": "...",
"group": "SC1031-c2cc8425"
}
]
}
```
<table>
<thead>
<tr>
<th>Field</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>schemaVersion</code></td>
<td>Incremented when a field is renamed or removed or its meaning changes. New optional fields, categories, and codes keep the version.</td>
</tr>
<tr>
<td><code>success</code></td>
<td>Coverage: no blockers on the entry path (and, with <code>--fail-on=divergences</code>, no divergences). Build: an artifact was produced, named by <code>artifact</code>.</td>
</tr>
<tr>
<td><code>phase</code></td>
<td><code>preflight</code> when type checking, configuration, or the module graph stopped the command; <code>compile</code> when blockers remain; <code>native</code> when native code generation or linking failed; otherwise <code>complete</code>.</td>
</tr>
<tr>
<td><code>stats</code></td>
<td>Coverage only, absent after a preflight failure: the report's statement counts. <code>statementsDynamic</code> is always 0 and remains for schema compatibility.</td>
</tr>
<tr>
<td><code>category</code></td>
<td><code>invalid-source</code> (the program has a TypeScript or JSON error), <code>unsupported</code> (valid code scriptc cannot compile yet), <code>divergence</code> (compiles, but can behave differently from Node.js), <code>environment</code> (project configuration, scriptc's type world, toolchain, or target), or <code>internal</code> (a compiler bug).</td>
</tr>
<tr>
<td><code>severity</code></td>
<td><code>error</code>, or <code>warning</code> for divergences.</td>
</tr>
<tr>
<td><code>scope</code></td>
<td><code>preflight</code>, <code>reached</code> (fails a build), <code>unreached</code> (code the entry never reaches), or <code>deferred</code> (a JavaScript statement that compiles to a throw of its diagnostic, which happens only if it executes).</td>
</tr>
<tr>
<td><code>line</code>, <code>column</code></td>
<td>1-based line and UTF-16 column; <code>start</code> and <code>end</code> are 0-based UTF-16 offsets.</td>
</tr>
<tr>
<td><code>group</code></td>
<td>The <code>id</code> of the root-cause group the diagnostic belongs to. A group's <code>primary</code> site is its first in source order.</td>
</tr>
</tbody>
</table>
## Type errors
Coverage needs a program that type-checks. If type checking fails, the report lists the type errors instead of statement counts; fix them first.
scriptc type-checks with its own settings: strict null checks, the ES2025 library, and Node.js types (its bundled declarations when the project has no `@types/node`). An error those settings cause, rather than the project's own `tsc` configuration, carries a hint naming the cause, such as a library member newer than ES2025 or a global of another runtime, and machine-readable output reports it in the `environment` category.
## External host type declarations
When your code will run inside a host application that provides its own modules, those modules aren't in `node_modules`. Use `--external-types` to give coverage their declarations so it can analyze the rest of your code:
```console
$ scriptc coverage src/core.ts --external-types @native-sdk/core=types/native-sdk-core.d.ts
```
The option is repeatable. Relative declaration paths resolve from the current working directory. Supported declaration extensions are `.d.ts`, `.d.mts`, and `.d.cts`. Relative declaration imports and re-exports are included, so the mapped file can be an `index.d.ts` barrel. Project-owned code using these types participates in coverage; type-only imports add no runtime boundary.
The mapping supplies types for analysis, not a runtime implementation. Value imports and uses remain `SC1010` external-host blockers while analysis counts the rest of the application. This option is available only for coverage; builds require a module implementation or runtime integration.