Avatar for the swc-project user
swc-project
swc
BlogDocsChangelog

Performance History

Latest Results

fix(es/react-compiler): Keep recovered spans out of renames and rebase loc offsets Recovered spans exist only for source mapping. Record the positions of identifiers whose spans were recovered from loc and exclude them from the span.lo-keyed rename plan, so a generated identifier that inherits the loc of an original reference can never be rewritten to a user binding name. Derive the loc rebase offset from the root node (absolute start minus file-relative loc index) instead of assuming BytePos(1), so recovered spans stay inside the current SourceFile when it does not start at 1.
Chastrlove:fix-react-compiler-spans
11 hours ago
chore: Add changeset
Chastrlove:fix-react-compiler-spans
12 hours ago
fix(es/minifier): deconflict IIFE-hoisted bindings under mangle IIFE inlining hoists parameter (and body-level `var`) bindings into the enclosing scope while reusing their original nested syntax context. When the hoist target is the top level and a same-symbol binding already lives there, the two bindings collide. The non-mangle pipeline is saved by `hygiene()`, which renames the hoisted binding (e.g. to `x1`). But `hygiene()` is skipped once mangling is enabled, and the mangler cannot rename the hoisted binding: its context is a descendant of the top-level mark, so the preserver keeps its name, and with the default `top_level: false` the top level is never renamed. The result was two top-level `x` declarations: SyntaxError: Identifier 'x' has already been declared (SWC's parser does not enforce lexical redeclaration, so the invalid output was emitted silently; Node rejects it at load time.) Fix: when mangling is enabled, remap each hoisted binding's identifier to a fresh synthesized syntax context (a mark whose parent is the root, like `private_ident!`). Such a context is not a descendant of the top-level mark, so the mangler's existing "force rename synthesized names" path deconflicts it exactly as `hygiene()` would. The remap is applied to the hoisted declarators and to every reference (assignment LHS and body uses), which all share the same original id. The react-pdf-renderer fixture output improves as a side effect: the fresh context unblocks a UMD-wrapper simplification the optimizer previously could not perform. Closes: https://github.com/swc-project/swc/issues/11977 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lukesandberg:fix/issue-11977-iife-mangle-collision
13 hours ago
fix(react-compiler): move index.ts/binding.js to package root Reported by an automated reviewer: the previous layout (index.ts under src/, binding.js/binding.d.ts also under src/, tsc's rootDir "./src" + outDir "./") had `tsc -d` (running before `napi build` in build:dev, same order as CI) generate its OWN weak, type-inferred root binding.d.ts from src/binding.js (a plain JS file with no real type annotations -- roughly `export = nativeBinding; declare let nativeBinding: any`), since tsc treats already-existing .d.ts files as ambient declarations it never copies to outDir, and only emits declarations for .js/.ts source files it actually compiles. napi build's own --dts flag only ever wrote the real, richly-typed declarations to src/binding.d.ts, never to the root file that actually ships. So `export type { Diagnostic, ... } from './binding'` and `lint`/`lintSync`'s `binding.Diagnostic[]` return types resolved fine while developing (tsc checks src/index.ts against its real, correctly-located sibling src/binding.d.ts) but were broken for any real downstream consumer of the published package, who only gets the root-level files. Confirmed by simulating a consumer against only the final built root/index.d.ts + root/binding.d.ts (no access to a src/ directory): `Cannot find module '../binding'` before this fix, clean after. @swc/core avoids this because its binding.js/binding.d.ts live at the package root already, not under src/ -- an initial attempt to copy that exact pattern here (keep index.ts under src/, use a type-only `typeof import("../binding")` + separate runtime `require("./binding")` split, matching @swc/core's own src/index.ts) turned out to have the same latent bug: tsc emits the *literal* import-specifier text into the compiled .d.ts without rewriting it for the new location, so `../binding` ends up wrong in the emitted root index.d.ts too, once a public signature (unlike @swc/core's internal-only use of that pattern) actually needs to resolve it. Verified this by re-running the same consumer simulation against that version -- still broken. The actual fix: index.ts now lives at the package root too. It was the only real source file under src/ (binding.js/binding.d.ts are napi build's output, not authored TS/JS) once those moved out for the same reason, so the src/ subdirectory no longer serves any purpose -- having rootDir/outDir differ was the entire source of the bug, and with only one file, there's no reason to keep that split. `./binding` is now unambiguously correct everywhere: same directory, before and after compilation, for the source file, the compiled JS's require call, and the emitted .d.ts's import specifier alike. Re-verified the consumer simulation passes clean, and the runtime test suite (tests/lint.test.cjs) still passes 4/4 against the new layout. @swc/minifier has the identical structural bug (binding.js/d.ts under src/, minifySync/minify return binding.TransformOutput) and is already shipping it in production -- confirmed separately, left as a distinct out-of-scope issue for this PR.
imjordanxd:feat/react-compiler-lint-diagnostics
20 hours ago
fix(react-compiler): convert diagnostic positions to UTF-16 code units swc_ecma_react_compiler's Position (loc.column/index) is intentionally byte-based -- it also feeds DiagnosticMessage.span, which @swc/core's existing react-compiler transform-error path turns into a real swc_common::Span/BytePos for terminal error rendering. That consumer genuinely needs byte offsets, so the shared representation must stay byte-based. lint/lintSync are a different consumer exposing these positions to JS/ESLint, whose string indexing is UTF-16-based. For source containing any non-ASCII character before a diagnostic's location, returning raw byte offsets there produces wrong positions for any JS consumer. Fixes this at this package's own boundary instead of the shared upstream representation: diagnostic_from_message (bindings/ binding_react_compiler_node/src/diagnostics.rs) now takes the original source text and re-derives column/index as UTF-16 code units, using the byte-based line/column as a starting point. For ASCII-only source this is a no-op. Verified against the real built binary: for source with an emoji before a violation, source.slice(loc.start.index, loc.end.index) (JS's own UTF-16 string slicing) now correctly extracts just the violating expression. An earlier version of this fix changed swc_ecma_react_compiler's shared position-computation function directly, which would have fixed this for lint/lintSync but broken the unrelated, already-shipped transform-error span rendering for the same class of input. Caught before landing.
imjordanxd:feat/react-compiler-lint-diagnostics
1 day ago

Latest Branches

CodSpeed Performance Gauge
0%
fix(es/react-compiler): Recover spans from loc for compiler-generated nodes#11979
11 hours ago
d6e90d0
Chastrlove:fix-react-compiler-spans
CodSpeed Performance Gauge
-2%
13 hours ago
9a85718
lukesandberg:fix/issue-11977-iife-mangle-collision
CodSpeed Performance Gauge
0%
20 hours ago
d07e188
imjordanxd:feat/react-compiler-lint-diagnostics
© 2026 CodSpeed Technology
Home Terms Privacy Docs