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

Performance History

Latest Results

chore: add changeset for JSX sequence fix
fix/minifier-jsx-sequence-progress
1 hour ago
fix(es/minifier): avoid JSX sequence inlining loop
fix/minifier-jsx-sequence-progress
2 hours ago
chore: add swc_core to Flow component changeset
fix/flow-component-type-semantics
6 hours ago
feat(es/parser): add opt-in parser-only TSRX lowering (#12120) **Description:** This rewrites the initial TSRX implementation as an opt-in, parser-only lowering instead of extending `swc_ecma_ast`. The parser recognizes TSRX through the default-disabled `swc_ecma_parser/tsrx` Cargo feature and immediately lowers the syntax to existing TypeScript/JSX AST nodes. As a result, the shared AST, generated visitors, codegen, transforms, JavaScript bindings, WASM bindings, configuration schema, and filename-based defaults remain unchanged. Rust facade exposure is intentionally limited to: - `swc_ecmascript/tsrx-parser` - `swc_core/ecma_parser_tsrx` Most TSRX-specific behavior lives in `swc_ecma_parser/src/parser/tsrx.rs`, including parser-private IR, validation, standard AST construction, React helper tracking, collision-safe generated identifiers, conditional import injection, and JSX directive handling. The lexer and existing parser modules contain only feature-gated delegation and JSX rescanning hooks. With the feature disabled, the TSRX parser state and the JSX `@` scan path are not compiled. TSRX is defined as module-only syntax. `parse_module` and `parse_program` are supported, and `parse_program` always returns a module when TSRX is enabled. Script/CommonJS and standalone-expression entry points report dedicated diagnostics when TSRX syntax is used. The lowering covers: - direct function `@{ setup; render }` bodies and expression `@{}` IIFEs - `@if` conditionals, using branch IIFEs only when setup statements require them - classic `for`, `for-in`, `for-of`, and `for-await` loops with preserved `break`/`continue`, per-iteration index bindings, key handling, and entered-state-based `@empty` - `@switch` with fallthrough and explicit break behavior preserved - `@try` content components with optional React `Suspense`, `TsrxErrorBoundary`, catch/reset patterns, setup statements, and synchronous JavaScript `finally` - single-evaluation dynamic tag aliases - JSX prop shorthand normalization - raw `<style>` bodies represented as ordinary JSX string expression children Required React helpers are inserted after directive prologues and only when used. Generated bindings avoid source-level name collisions and use a separate `SyntaxContext`. This deliberately does not implement hook analysis, helper/static hoisting, scoped CSS extraction, or optimization parity with the canonical `@tsrx/react` compiler. In particular, `@finally` follows synchronous JavaScript `try/finally` semantics and does not model a Suspense asynchronous lifetime. Validation includes feature-gated fixture snapshots for the full syntax surface, comments/trivia, nested JSX, all loop forms, switch behavior, try/pending/catch/reset/finally, helper import placement, name collisions, module-only diagnostics, and invalid syntax. Lowered TSX snapshots are reparsed by the regular TSX parser, and serialized AST checks ensure no TSRX-specific public node types are emitted. Checks run locally: - `cargo fmt --all -- --check` - `cargo clippy --all --all-targets -- -D warnings` - `cargo clippy -p swc_ecma_parser --no-default-features --features tsrx --all-targets -- -D warnings` - TSRX fixture tests with and without snapshot updates - feature-disabled JSX fixture tests - `cargo test -p swc_ecmascript --no-default-features --features tsrx-parser` - `cargo test -p swc_core --no-default-features --features ecma_parser_tsrx` - parser benchmark comparison against the PR base with the TSRX feature disabled; Criterion detected no performance change The full parser suite was also run with both `typescript` and `tsrx`. The existing `errors_tests__typescript_errors__type_only_import_specifier__invalid_type_only__input_ts` fixture fails identically in both configurations; all TSRX-specific tests pass. **Related issue (if exists):** N/A --------- Co-authored-by: DongYun Kang <kdy.1997.dev@gmail.com>
main
7 hours ago
fix(es/minifier): drop spans of cached `globals` values (#12129) **Related issue:** none filed yet — happy to open one if you would like the report separately. Rewritten after your review: the fix moved from `inline_globals` to the cache that produces the values, and the PR now leads with a failing test. ## Description `GlobalPassOption::build` parses each `jsc.transform.optimizer.globals.vars` value into an anonymous file of the `SourceMap` it is handed, and memoizes the parsed `Expr` in a process-wide `static CACHE`. A span is only meaningful to the `SourceMap` that produced it, so the cache outlives what its contents point at. For a host that builds a fresh `SourceMap` per file — which is what `swc::Compiler` does, and what a bundler does — the emitted source map depends on whether the call hit the cache: - on the call that parses the value, the mappings point into the anonymous file. With `source_file_name` set, `map_file_name_to_source` reports that file under the name of the file being compiled, so the position is silently attributed to the user's source; - on every call that hits the cache, the position belongs to a `SourceMap` that no longer exists: it is dropped when it is past the end of the file being compiled, and points at an unrelated line when that file is longer. Three things that cannot affect the semantics of `globals` currently affect its output: 1. **the order the host passes the defines in** — the cache key is a `Vec<(Atom, Atom)>` built from `vars`, so a host that holds the defines in a `std::collections::HashMap` (whose iteration order differs per instance) produces a different key per call, and therefore a different hit/miss pattern; 2. **whether the call hits the process-wide cache** — a cache should be semantically a no-op; 3. **which file was compiled first in the process** — the cached position is derived from the size of that file, so in a parallel build it depends on scheduling. That is how this surfaced: in a Rspack build the source map is part of the module hash, so the chunk hash moved between builds of unchanged sources, and with minification on every mangled name in the chunk moved with it. ## Fix Drop the spans before the value is cached. Parsing still happens in the caller's `SourceMap`, so a syntax error in a define value is still reported with a position — but nothing `SourceMap`-dependent survives into the cache. Substituted values then carry `DUMMY_SP`, which `srcmap!` skips, so no mapping is emitted for them on any call, whether it parsed the value or got it from the cache. The previous revision of this PR overwrote the spans at the four substitution sites in `inline_globals` instead. It left the cache poisoned for any other consumer, added a subtree visit per substitution on a hot path, and gave synthesized nodes the span of real source. This one is a single `drop_span` at the point the value is created, which is also what `inline_globals`' own tests already assume — they build their globals map with `DropSpan`. If you would rather keep a mapping for substituted values (`__DEV__` mapping back to `__DEV__` is nicer than no mapping at all), that can be layered on top cheaply now: with the cached children already dummy, the substitution site only needs `value.set_span(ident.span)`, no visitor. I left it out because it is a separate change. ## Tests `crates/swc/tests/source_map.rs` gets a test that compiles the same input twice, each in a fresh `SourceMap`, and compares the emitted maps. It is committed before the fix and fails there: ``` assertion failed: `(left == right)`: the same input produced two different source maps < {"version":3,"sources":["input.js","<anon>"],"sourcesContent":[...,"true"],...} > {"version":3,"sources":["input.js"],"sourcesContent":[...],...} ``` The first compilation leaks the define value itself into the map as a source file; the second, which hits the cache, does not. - `cargo test -p swc_ecma_transforms_optimization` — pass - `cargo test -p swc --test projects` — 889 pass - `cargo test -p swc --test rust_api` — pass - `cargo test -p swc --test source_map` — pass except `issue_622` and the `stacktrace` fixtures, which fail on `main` on my machine too (Node version) The end-to-end numbers in the original description (400 transforms → 1 distinct source map, 40 builds → 1 distinct chunk hash) were measured with the previous revision. I have not rebuilt the downstream binding against this revision; the invariant is the one the new test asserts, and this revision removes the mappings entirely rather than rewriting them.
main
7 hours ago
Merge main into fix/flow-component-type-semantics
fix/flow-component-type-semantics
7 hours ago
fix(parser): diagnose empty tsrx closing tags
trueadm:codex/tsrx-parser
7 hours ago
Merge branch 'main' into fix/inline-globals-span
upupming:fix/inline-globals-span
8 hours ago

Latest Branches

CodSpeed Performance Gauge
0%
fix(es/minifier): avoid JSX sequence inlining loop#12149
2 hours ago
1e5e3ed
fix/minifier-jsx-sequence-progress
CodSpeed Performance Gauge
0%
6 hours ago
824213f
fix/flow-component-type-semantics
CodSpeed Performance Gauge
0%
feat(es/parser): add opt-in parser-only TSRX lowering#12120
7 hours ago
c8cb07a
trueadm:codex/tsrx-parser
© 2026 CodSpeed Technology
Home Terms Privacy Docs