Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

[v6] Rework the SWC setup: native Flow support and a modernized getJsTransformRules

Open
#1,422 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
30/100
Issue type
Refactor
Clarity
Mostly clear
Activity status
Active
Tech stack
react-native, typescript

Research direction

Read the current getJsTransformRules, getSwcLoaderOptions, and getFlowTransformRules entry points, then check the Rspack parser limitation described here. Use tests/integration and the proposed tester-app or config-matrix coverage as validation starting points. Done requires an agreed v6 Flow path, updated rules and options, documentation, and coverage for component, hook, enums, and unused imports.

Written by the indexing model from the issue text.

Description

area:repack type:feature

Motivation

Re.Pack's SWC-native pipeline (getJsTransformRulesgetSwcLoaderOptions + getFlowTransformRules) strips Flow types in a separate JavaScript pass (flow-remove-types behind @callstack/repack/flow-loader) before handing sources to builtin:swc-loader. SWC can now parse and strip Flow natively (jsc.parser.syntax: "flow", docs) — landed via swc#11685, shipped in @swc/core 1.15.21 (March 2026), with React Native parity fixes continuing through 1.15.46 (current). That makes the extra pass redundant — and it is already broken on modern React Native.

Part of #1414 (Roadmap to V6), which lists "use swc built in flow removal".

The current Flow pass is broken on RN ≥ 0.81

flow-remove-types only erases type annotations. It does not desugar Flow's component / hook syntax or Flow enums — the keywords stay behind, producing invalid JavaScript that SWC then refuses to parse. React Native ships component syntax in core since 0.81 (Libraries/Components/View/View.js:25: component View(), so getJsTransformRules() cannot build RN 0.81+. Reproduced with the workspace versions (flow-remove-types@2.324.0, @swc/core@1.15.46):

flow-remove-types keeps the component keyword; swc parse of the output:
  x Expected ';', '}' or <eof>  (View.js:25)
swc one-pass { syntax: 'flow', jsx: true, enums: true, components: true }: OK

This goes unnoticed because nothing in this repo exercises getJsTransformRulestemplates/ and all tester apps use @callstack/repack/babel-swc-loader. The Rspack migration guide does document getJsTransformRules + flow-loader as the setup to adopt, though (the Metro guide embeds the babel-swc-loader templates and is unaffected).

The extra pass is also the slowest part of the pipeline

Stripping all 455 .js files in react-native/Libraries (4.6 MB): flow-remove-types 659 ms (strip only, single-threaded JS) vs SWC flow mode 209 ms serial / 59 ms parallel — for strip plus full transform. Dropping the loader also removes a parse → print → source-map round trip per Flow module and the flow-remove-types + hermes-parser install footprint.

What SWC's Flow mode covers

Verified on @swc/core@1.15.46 with { syntax: 'flow', jsx: true, enums: true, components: true }:

  • Annotations, import type, opaque type, declare, export type * — stripped (parity with flow-remove-types).
  • component / hook — desugared correctly (flow-remove-types emits invalid JS).
  • Flow enums — transpiled (flow-remove-types passes them through verbatim), but not to the flow-enums-runtime representation — see behaviour notes below.
  • enums and components are not default — without them these constructs are parse errors. requireDirective must stay off to match our current all: true (with it, files without a @flow pragma get no stripping at all).

Blocker: Rspack does not expose syntax: "flow"

True on every published version — verified on 1.6.0 and 2.0.0-alpha.1 (unknown variant 'flow', expected 'ecmascript' or 'typescript'), and latest 2.1.6 / main has no flow variant in SwcLoaderParserConfig either. Bumping to Rspack 2 does not fix it. Rspack main is already on swc_core = "72.0.0"; the gap is a Cargo feature: SWC gates the Flow parser behind base_flow (swc_coreswc/flowswc_ecma_parser/flow), and rspack_loader_swc doesn't enable it. The upstream fix is one feature flag, the corresponding SwcLoaderParserConfig type, and a detectSyntax mapping decision for .flow / .js.flow. Precedent for this kind of ask: rspack#13843 (swc_core bump + exposing jsc.preserveSymlinks) was accepted and shipped.

Fallback if upstream is slow: route Flow-typed modules through a Re.Pack loader backed by standalone @swc/core, which has the feature compiled in. babelSwcLoader's lazyGetSwc already resolves rspack.experiments.swc@rspack/core@swc/core, so the plumbing exists. Costs the JS↔Rust boundary again, but stays single-pass and works on webpack too.

Behaviour differences vs Babel

  • Unused value imports are elided by default. SWC's flow mode applies TypeScript-style import elision (import Unused from './u'; console.log(1)console.log(1)). jsc.transform.verbatimModuleSyntax: true fixes this — verified it preserves unused value imports while still stripping import type and type specifiers. Set it and pin with a regression test.
  • Flow enums don't use flow-enums-runtime. SWC emits a TS-style object IIFE. Member access matches Flow semantics, but the runtime API (.members(), .cast(), .isValid(), .getName()) doesn't exist on the result. RN core doesn't call those methods today; needs a documented caveat (and a check when bumping RN).
  • exportDefaultFrom is unavailable under syntax: "flow" (export v from './v' → parse error), while getParserOptions enables it for all .js (RN's preset includes @babel/plugin-proposal-export-default-from). So flow cannot blanket-replace ecmascript for .js — keep it scoped to Flow-typed modules until SWC supports the combination.

Other issues in getSwcLoaderOptions / getJsTransformRules

Independent of Flow, worth fixing in the same pass:

  • development is tied to the JSX runtime, not mode. getJSCTransformOptions sets development: jsxRuntime === 'classic', so with the default automatic runtime dev builds always get jsx instead of jsxDEV (verified) — losing __source/__self, accurate component stacks, and "open in editor". RN's preset adds jsx-source/jsx-self whenever dev. Should follow mode.
  • The six-branch oneOf collapses under Rspack 2. detectSyntax: 'auto' (new in 2.0.0) infers the parser from the extension, leaving only the sourcemaps-off-for-node_modules split; tsRules/tsxRules are byte-identical today anyway. Unrecognized extensions map to { syntax: 'typescript', tsx: true }, so .flow needs an explicit branch either way.
  • env.targets: { node: 24 } is a sentinel, not a target ("assume everything supported, re-add via env.include"). It works but drifts silently, and Rspack 2 derives loader targets from the top-level target when unset. Re-derive env.include (16 entries) from the current RN preset and write down each inclusion/omission — e.g. transform-react-display-name is missing from both SWC paths, and the preset's regenerator-gated trio (optional-catch-binding, nullish-coalescing, for-of) is dev+Hermes-only, so omitting it may be correct but should be a written decision.
  • lazyImports defaults to false, while RN's preset defaults lazy to its lazy-imports allowlist. isModule is never set (babelSwcLoader sets it from Babel's sourceType; isModule: 'unknown' is probably right here).
  • getSwcParserConfig (babelSwcLoader) and getParserOptions (getSwcLoaderOptions) are near-duplicates with different answers (exportDefaultFrom). Fold into one shared helper.
  • getCodegenTransformRules keeps its Babel pass@react-native/babel-plugin-codegen has no SWC equivalent — but the "must run before Flow stripping" ordering constraint deserves a re-check once SWC parses Flow natively.

babel-swc-loader

Stays as-is — it is the default in templates/ and every tester app, and it is correct by construction (it reads the project's real Babel config and only offloads transforms it can prove equivalent). Follow-up once Flow support is available: teach swc.ts to map transform-flow-strip-types / transform-flow-enums / syntax-hermes-parser to jsc.parser.syntax: 'flow' (+ enums, components), so RN core files go through SWC instead of falling back to Babel — the biggest remaining Babel fallback for a stock RN app.

Scope

Upstream
  • Rspack: file the issue/PR enabling base_flow in rspack_loader_swc, exposing the flow parser options in SwcLoaderParserConfig, and deciding the detectSyntax mapping for .flow
  • SWC: file the issue for exportDefaultFrom under syntax: "flow"
Core
  • Switch getFlowTransformRules from flow-loader to SWC-native Flow (or the @swc/core-backed fallback loader), keeping the FLOW_TYPED_MODULES include/exclude surface; set verbatimModuleSyntax: true
  • Decide the fate of @callstack/repack/flow-loader (public export) — deprecate as escape hatch or drop in v6
  • Rewrite getJsTransformRules on detectSyntax: 'auto', collapsing the oneOf
  • Drive jsc.transform.react.development from mode
  • Re-derive env.include from the current RN preset; align lazyImports / isModule defaults
  • Fold getSwcParserConfig and getParserOptions into one helper
  • Drop the flow-remove-types dependency once nothing uses it
v5 patch (independent of v6)
  • Stop getJsTransformRules emitting invalid JS for component / hook / enums on RN ≥ 0.81 — either the SWC path or babel-plugin-syntax-hermes-parser + babel-plugin-transform-flow-enums in flow-loader
Apps, tests, docs
  • Add a tester-app variant or config-matrix entry that actually builds with getJsTransformRules — this class of bug is invisible today
  • tests/integration coverage for component / hook / Flow enums in a node_modules dependency; regression test pinning unused-import preservation
  • Update flow-loader, get-flow-transform-rules, get-swc-loader-options, get-js-transform-rules docs and the Rspack migration guide; note the minimum Rspack version in the v5 → v6 migration guide

Open questions

  • Wait on upstream Rspack, or ship the @swc/core-backed loader first (works today, keeps webpack) and switch to builtin:swc-loader when the feature flag lands?
  • Does getJsTransformRules remain a second-class alternative to babel-swc-loader, or does v6 make it the default in templates/? #1414 targets Rspack 2+ as a minimum, which makes it viable as a default for the first time — but only if it becomes the path we actually test.
Dominant language
TypeScript
Stars
1.9k
Forks
164
Avg merge
10d 13h
Merged PRs (30d)
10

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from callstack/repack

All issues in callstack/repack

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.