semanticDiff()
Function in Tooling
Compare two modules at the IR and reflection layer.
import { semanticDiff } from 'typeshade'
The signature, the description and the examples come from the compiler's own source at commit 26de7be8.
Syntax
function semanticDiff( a: ModuleDecl, b: ModuleDecl, opts: SemanticDiffOptions & { readonly transforms: readonly EmitPlugin[] },): ClassifiedSemanticDiff;function semanticDiff( a: ModuleDecl, b: ModuleDecl, opts?: SemanticDiffOptions,): SemanticDiff;Parameters
aModuleDeclthe reference module, the development side when
transformsis declared.bModuleDeclthe module to compare against it, the transformed side.
optsSemanticDiffOptions & { readonly transforms: readonly EmitPlugin[] }the axes to disregard,
namesanddeclOrderby default, and optionally the declared transform pipeline.
Return value
the four buckets, and explained when transforms was declared.
Description
A textual diff of emitted source is hard to read once the optimizer and the production plugins have run; this comparison works above that noise.
It reports four buckets: the entry and vertex interface, the bind-group and layout
resources, the constants (module consts, overrides, and the multiset of body literals),
and the per-statement controlFlow skeleton. Empty in all four means the two modules agree
on everything a host binds to and everything the code does. Each line is prefixed - for a
fact only in a and + for one only in b, and isSemanticallyEqual is the
all-empty test.
A pass that legitimately changes the program is not expected to be empty here. Inlining
rewrites call sites and duplicates literals, which is exactly what the buckets report.
Declare such a pass in transforms, the same plugin array the production emit takes, and
every difference the declared pipeline provably causes moves into explained, one entry
naming the plugin, the bucket and the line.
A line moves to explained only when applying that plugin’s own transformIR to the
reference side actually removes the line from the diff. A regression that merely looks like
an optimizer rewrite stays in its bucket, so a check that compares a development build
against a production build fails only on the unexplained differences.
Text-stage plugins explain nothing, because the comparison never sees emitted text. Declaring
the full production array is therefore safe: minify and aliasTypes
contribute no explained entries and remove no coverage, so you can pass the array you
actually ship.
Renaming with mangle produces no differences under the default options, because
'names' canonicalizes exactly the identifiers that pass is free to rewrite.
Examples
Example
import { semanticDiff, isSemanticallyEqual } from 'typeshade'import { inline, obfuscate } from 'typeshade/emit-prod'
const d = semanticDiff(devModule, prodModule, { transforms: [inline(), ...obfuscate()] })isSemanticallyEqual(d) // true when prod differs from dev only as the declared pipeline dictatesd.explained // [{ transform: 'inline', bucket: 'controlFlow', line: '…' }, …]In the guide
See also
Source
src/core/semantic-diff.ts, line 558, at commit 26de7be8