Files
scriptc/docs/content/docs/coverage.mdx
T
Malte UblandClaude Opus 5.5 5a342ad1a5 Parse date strings with V8's grammar when the bounded parser rejects them
Date.parse and new Date(string) answered NaN natively for every form
outside ISO 8601 with an explicit offset and the X509 certificate shape,
so RFC 1123 HTTP dates ("Mon, 01 Jan 2024 00:00:00 GMT") never parsed and
the fresh/express/send conditional GET never matched. The runtime now
falls back to a port of V8's DateParser (the ES5 date-time format, then
the Safari-compatible legacy grammar: weekday and month names, RFC 2822
offsets, zone abbreviations, AM/PM, parenthesized comments, slashes) for
exactly the strings the bounded grammars reject, so the timestamps they
accept cost nothing more. Zone-less dates are local time, converted with
the spec's UTC(t) over the host zone (DST gaps read with the offset before
the transition, overlaps answer the earlier instant).

With native parsing matching Node for every input, the SC6005 literal
warning has nothing left to report and is retired rather than extended to
non-literal inputs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 13:34:55 -07:00

204 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`: a record passed to, stored as, asserted as, or stored as an array element of a record type with different properties 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.
## 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.