swc-project / swc-project/plugins
[loadable-components] Skipping transform for `ssr: false` breaks SSR with every published `@loadable/component`
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 389
- Forks
- 106
- Avg merge
- 10m
- Merged PRs (30d)
- 1
Description
Summary
Since #592 ("fix(loadable-components): skip transformation when ssr: false is specified"), @swc/plugin-loadable-components no longer transforms loadable(fn, { ssr: false }) call sites. This breaks server-side rendering at runtime for every published version of @loadable/component (checked 5.15.3 and latest 5.16.7): the server throws
Invariant Violation: loadable: SSR requires `@loadable/babel-plugin`, please install it
for any ssr: false component rendered under a ChunkExtractor, turning whole pages into HTTP 500s.
The skip is present in all releases containing #592, including the current latest 11.19.0.
Root cause
@loadable/component's runtime checks the transform metadata before it looks at options.ssr. From dist/cjs/loadable.cjs.js (identical in 5.15.3 and 5.16.7):
function InnerLoadable(props) {
// ...
invariant(!props.__chunkExtractor || ctor.requireSync,
'SSR requires `@loadable/babel-plugin`, please install it'); // <-- fires first
if (props.__chunkExtractor) {
// This module has been marked with no SSR
if (options.ssr === false) {
return _assertThisInitialized(_this); // <-- ssr:false handled only after the invariant
}
// ...
}
}
So under SSR (__chunkExtractor present), an untransformed ssr: false call site hits the invariant and throws. ssr: false is a runtime rendering decision (render the fallback on the server); it does not mean the call site may be left untransformed.
The reference implementation agrees: @loadable/babel-plugin (5.16.0, latest) transforms these call sites unconditionally — its source does not mention ssr at all. The client-side runtime also uses the metadata (isReady/chunkName) that the transform provides.
Reproduction
Input:
import loadable from '@loadable/component';
const A = loadable(() => import('./a'), { ssr: false });
export default A;
Transform with @swc/plugin-loadable-components >= the release containing #592:
- Actual: the call is left untouched (no
requireAsync/requireSync/chunkNameobject). Rendering<A />insideChunkExtractor.collectChunks()then throwsInvariant Violation: loadable: SSR requires @loadable/babel-plugin→ 500. - Expected (matches
@loadable/babel-pluginand this plugin before #592): the first argument is replaced with the metadata object; at runtimessr: falsemakes the server render the fallback, as documented.
The same input through @loadable/babel-plugin@5.16.0 produces the full transform.
Note on #465
#592 was made to fix #465 ("loadable-components plugin does not support ssr false"), but the linked docs feature ("Disable SSR on a specific loadable") does not require skipping the transform — with @loadable/babel-plugin the call site is transformed and the runtime handles ssr: false by rendering the fallback on the server. Skipping the transform is what actually breaks ssr: false support: before #592 the plugin handled ssr: false exactly like babel does; after #592 it crashes SSR.
Suggested fix
Revert #592 (i.e. remove has_ssr_false and the early return in transform_import_expr). If skipping is ever desired as an optimization, it can only be done together with a runtime that checks options.ssr === false before the invariant — no published @loadable/component does that.
Happy to send a PR with the revert.
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 the SWC plugin code at transform_import_expr and the has_ssr_false handling introduced by #592; compare it with the pre-#592 behavior and the @loadable/babel-plugin reference. Reproduce the supplied ssr:false case under ChunkExtractor, then verify the call is transformed with metadata and no SSR invariant is raised.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, rust
- Domain
- compilers, tooling
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100