swc-project / swc-project/swc-node
Allow user-supplied `jsc.experimental` options
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2k
- Forks
- 95
- Avg merge
- 14d 11h
- Merged PRs (30d)
- 1
Description
Summary
@swc-node/core's transformOption constructs jsc.experimental as a hardcoded { keepImportAttributes: true } literal with no way for callers to extend it (see here). This makes it effectively impossible to enable SWC plugins (e.g. @swc-contrib/mut-cjs-exports) when going through @swc-node/core or @swc-node/jest without re-implementing the entire transformOption body.
This is especially painful with @swc-node/jest: the only knob exposed by its public API is the Options argument, but Options has no experimental field, and overriding via swc: { jsc: {...} } clobbers every other tsconfig-derived jsc setting (target, paths, parser, transform, etc). See also #700.
Motivation
The most common reason users want to pass a plugin through @swc-node/jest is to make jest.mock / jest.requireActual / Sinon stubs work on modules with circular imports (exactly the use case in #713). The recommended fix in the SWC ecosystem is the @swc-contrib/mut-cjs-exports plugin, which turns ESM export const bindings (compiled to CJS getters by SWC) into plain assignments so they can be mocked.
There is currently no clean way to wire that plugin into @swc-node/jest. Users either:
- Drop
@swc-node/jestand switch to@swc/jest+.swcrc(loses tsconfig integration), or - Write a custom Jest transformer that calls
core.transformJestwith a manually-rebuiltjsc(~70 lines of code that re-implementstransformOption), or - Patch
node_modules/@swc-node/core/lib/index.js(what I'm currently doing viapatch-package).
Proposed change
Allow callers to pass an experimental field on Options and merge it into the constructed jsc.experimental. One spread, no behavior change for existing callers.
packages/core/index.ts: extend the Options interface:
export interface Options {
// ...existing fields...
swc?: SwcOptions
ignoreDynamic?: boolean
+ experimental?: {
+ plugins?: Array<[string, Record<string, unknown>]>
+ keepImportAttributes?: boolean
+ }
}
packages/core/index.ts: merge inside transformOption:
baseUrl: opts.baseUrl,
experimental: {
keepImportAttributes: true,
+ ...(opts.experimental ?? {}),
},
After this change, @swc-node/jest users get plugin support for free, with no other changes needed in @swc-node/jest:
// jest.config.js
module.exports = {
transform: {
"^.+\\.[tj]sx?$": [
"@swc-node/jest",
{
experimental: {
plugins: [["@swc-contrib/mut-cjs-exports", {}]],
},
},
],
},
};
Backwards compatibility
- New optional field; all existing call sites unaffected.
keepImportAttributes: trueremains the default; user can opt out by passingexperimental: { keepImportAttributes: false }.- No changes to
@swc-node/jest's public API, it already spreads its second-arg options intocore.transformJest.
Related
- #713 - Mocking with jest doesn't work (the canonical "I need this plugin" ticket; this change unblocks the community-recommended workaround).
- #700 -
@swc-node/jestdoesn't pass options from tsconfig to swc properly (related:swc: { jsc: {...} }is currently the only override path and is too coarse).
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/core/index.ts at the Options interface and transformOption, then inspect how @swc-node/jest forwards its second-argument options to core.transformJest. Add support for merging the optional experimental settings while preserving the existing default, and verify that plugin options and keepImportAttributes overrides reach the constructed jsc.experimental object.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- testing, tooling
- Issue type
- Feature
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100