Skip to content

Commit 649e0bd

Browse files
ihabadhamclaude
andcommitted
Wire RSC loader for both SWC and Babel transpilers
The previous implementation only handled Array.isArray(rule.use) and only looked for babel-loader. The tutorial uses swc as its transpiler (shakapacker.yml: javascript_transpiler: swc), which makes Shakapacker generate rule.use as a FUNCTION, not an array. The RSC loader was therefore never attached to the transpilation rule — 'use client' files were left untransformed in the RSC bundle, producing 134 webpack warnings (export 'useState' not found in 'react' etc.) and setting up a runtime error when the RSC renderer would try to call client components directly instead of emitting client references. Follow the pattern from docs/oss/migrating/rsc-preparing-app.md:167 verbatim, which handles both forms and both loader names. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent bc716e0 commit 649e0bd

1 file changed

Lines changed: 21 additions & 8 deletions

File tree

config/webpack/rscWebpackConfig.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,29 @@ const configureRsc = () => {
88
};
99
rscConfig.entry = rscEntry;
1010

11-
// Runs before babel-loader (webpack loaders execute right-to-left) to
12-
// detect 'use client' directives in raw source before transpilation.
11+
// Runs before babel/swc (webpack loaders execute right-to-left) to detect
12+
// 'use client' directives in raw source before transpilation. Shakapacker
13+
// generates rule.use as an array for Babel and as a function for SWC, so
14+
// handle both forms.
1315
const { rules } = rscConfig.module;
1416
rules.forEach((rule) => {
15-
if (Array.isArray(rule.use)) {
16-
const babelLoader = extractLoader(rule, 'babel-loader');
17-
if (babelLoader) {
18-
rule.use.push({
19-
loader: 'react-on-rails-rsc/WebpackLoader',
20-
});
17+
if (typeof rule.use === 'function') {
18+
const originalUse = rule.use;
19+
rule.use = function rscLoaderWrapper(data) {
20+
const result = originalUse.call(this, data);
21+
const resultArray = Array.isArray(result) ? result : result ? [result] : [];
22+
const resolvedRule = { use: resultArray };
23+
const jsLoader =
24+
extractLoader(resolvedRule, 'babel-loader') || extractLoader(resolvedRule, 'swc-loader');
25+
if (jsLoader) {
26+
return [...resultArray, { loader: 'react-on-rails-rsc/WebpackLoader' }];
27+
}
28+
return result;
29+
};
30+
} else if (Array.isArray(rule.use)) {
31+
const jsLoader = extractLoader(rule, 'babel-loader') || extractLoader(rule, 'swc-loader');
32+
if (jsLoader) {
33+
rule.use = [...rule.use, { loader: 'react-on-rails-rsc/WebpackLoader' }];
2134
}
2235
}
2336
});

0 commit comments

Comments
 (0)