Skip to content

Commit 431a8ee

Browse files
justin808claude
andcommitted
Add comprehensive documentation and address code review feedback
Documentation: - Added JSDoc comments to commonWebpackConfig() and configureServer() - Created patches/README.md explaining patch necessity and maintenance - Updated variable names for clarity (baseClientRspackConfig -> baseWebpackConfig) - Added inline comments explaining critical fixes Code Quality: - Added comment about safe mutation pattern (fresh config each call) - Added clarifying comments for console.warn (not throwing error) - Improved CSS modules fix comments with examples - Added bundler auto-detection explanations Upstream Contributions: - Filed issue with @glennsl/rescript-json-combinators: #9 - Updated patches/README.md with issue link - Comprehensively updated react_on_rails issue #1863 with ALL migration challenges Key documentation improvements: - Timeline of 11 commits and issues resolved - Root cause analysis for each problem - Complete code examples for each fix - Impact assessment (3 days → 2-3 commits with docs) This addresses all code review feedback from the PR review. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 4c761bb commit 431a8ee

File tree

3 files changed

+98
-10
lines changed

3 files changed

+98
-10
lines changed

config/webpack/commonWebpackConfig.js

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ const { generateWebpackConfig, merge } = require('shakapacker');
66

77
const commonOptions = {
88
resolve: {
9+
// Add .bs.js extension for ReScript-compiled modules
910
extensions: ['.css', '.ts', '.tsx', '.bs.js'],
1011
},
1112
};
1213

13-
// add sass resource loader
14+
// Sass resource loader config - globally imports app variables
1415
const sassLoaderConfig = {
1516
loader: 'sass-resources-loader',
1617
options: {
@@ -19,16 +20,33 @@ const sassLoaderConfig = {
1920
};
2021

2122
const ignoreWarningsConfig = {
23+
// React 19 uses react-dom/client but not all deps have migrated yet
2224
ignoreWarnings: [/Module not found: Error: Can't resolve 'react-dom\/client'/],
2325
};
2426

25-
// Copy the object using merge b/c the baseClientWebpackConfig and commonOptions are mutable globals
27+
/**
28+
* Generates the common webpack/rspack configuration used by both client and server bundles.
29+
*
30+
* IMPORTANT: This function calls generateWebpackConfig() fresh on each invocation, so mutations
31+
* to the returned config are safe and won't affect other builds. The config is regenerated
32+
* for each build (client, server, etc.).
33+
*
34+
* Key customizations:
35+
* - CSS Modules: Configured for default exports (namedExport: false) for backward compatibility
36+
* - Sass: Configured with modern API and global variable imports
37+
* - ReScript: Added .bs.js to resolve extensions
38+
*
39+
* @returns {Object} Webpack/Rspack configuration object (auto-detected based on shakapacker.yml)
40+
*/
2641
const commonWebpackConfig = () => {
27-
const baseClientWebpackConfig = generateWebpackConfig();
42+
// Generate fresh config - safe to mutate since it's a new object each time
43+
const baseWebpackConfig = generateWebpackConfig();
2844

29-
// Fix all CSS-related loaders to use default exports instead of named exports
30-
// Shakapacker 9 defaults to namedExport: true, but existing code expects default exports
31-
baseClientWebpackConfig.module.rules.forEach((rule) => {
45+
// Fix CSS Modules to use default exports for backward compatibility
46+
// Shakapacker 9 changed default to namedExport: true, breaking existing imports like:
47+
// import css from './file.module.scss'
48+
// This ensures css is an object with properties, not undefined
49+
baseWebpackConfig.module.rules.forEach((rule) => {
3250
if (rule.use && Array.isArray(rule.use)) {
3351
const cssLoader = rule.use.find((loader) => {
3452
const loaderName = typeof loader === 'string' ? loader : loader?.loader;
@@ -42,15 +60,16 @@ const commonWebpackConfig = () => {
4260
}
4361
});
4462

45-
const scssConfigIndex = baseClientWebpackConfig.module.rules.findIndex((config) =>
63+
const scssConfigIndex = baseWebpackConfig.module.rules.findIndex((config) =>
4664
'.scss'.match(config.test) && config.use,
4765
);
4866

4967
if (scssConfigIndex === -1) {
5068
console.warn('No SCSS rule with use array found in webpack config');
69+
// Not throwing error since config might work without SCSS
5170
} else {
5271
// Configure sass-loader to use the modern API
53-
const scssRule = baseClientWebpackConfig.module.rules[scssConfigIndex];
72+
const scssRule = baseWebpackConfig.module.rules[scssConfigIndex];
5473
const sassLoaderIndex = scssRule.use.findIndex((loader) => {
5574
if (typeof loader === 'string') {
5675
return loader.includes('sass-loader');
@@ -73,10 +92,10 @@ const commonWebpackConfig = () => {
7392
}
7493
}
7594

76-
baseClientWebpackConfig.module.rules[scssConfigIndex].use.push(sassLoaderConfig);
95+
baseWebpackConfig.module.rules[scssConfigIndex].use.push(sassLoaderConfig);
7796
}
7897

79-
return merge({}, baseClientWebpackConfig, commonOptions, ignoreWarningsConfig);
98+
return merge({}, baseWebpackConfig, commonOptions, ignoreWarningsConfig);
8099
};
81100

82101
module.exports = commonWebpackConfig;

config/webpack/serverWebpackConfig.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,27 @@ const { config } = require('shakapacker');
66
const commonWebpackConfig = require('./commonWebpackConfig');
77

88
// Auto-detect bundler from shakapacker config and load the appropriate library
9+
// This allows the same config to work with both Webpack and Rspack
910
const bundler = config.assets_bundler === 'rspack'
1011
? require('@rspack/core')
1112
: require('webpack');
1213

14+
/**
15+
* Generates the server-side rendering (SSR) bundle configuration.
16+
*
17+
* This creates a separate bundle optimized for server-side rendering:
18+
* - Single chunk (no code splitting for Node.js execution)
19+
* - CSS extraction disabled (uses exportOnlyLocals for class name mapping)
20+
* - No asset hashing (not served directly to clients)
21+
* - Outputs to ssr-generated/ directory
22+
*
23+
* Key differences from client config:
24+
* - Removes CSS extraction loaders (mini-css-extract-plugin/CssExtractRspackPlugin)
25+
* - Preserves CSS Modules configuration but adds exportOnlyLocals: true
26+
* - Disables optimization/minification for faster builds and better debugging
27+
*
28+
* @returns {Object} Webpack/Rspack configuration object for server bundle
29+
*/
1330
const configureServer = () => {
1431
// We need to use "merge" because the clientConfigObject, EVEN after running
1532
// toWebpackConfig() is a mutable GLOBAL. Thus any changes, like modifying the

patches/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Patches
2+
3+
This directory contains patches applied to npm packages using [patch-package](https://github.com/ds300/patch-package).
4+
5+
## Why Patches?
6+
7+
Patches are used when npm packages need modifications that haven't been released upstream yet, or when quick fixes are needed for compatibility issues.
8+
9+
## Current Patches
10+
11+
### @glennsl/rescript-json-combinators+1.4.0.patch
12+
13+
**Issue**: This package ships with only ReScript source files (`.res`), not compiled JavaScript files (`.bs.js`). Its `bsconfig.json` lacks the `package-specs` configuration needed to generate compiled output.
14+
15+
**Impact**: Without this patch, Rspack/Webpack cannot resolve imports like:
16+
```javascript
17+
import * as Json from "@glennsl/rescript-json-combinators/src/Json.bs.js";
18+
```
19+
20+
**What the patch does**:
21+
1. Removes reference to non-existent `examples` directory
22+
2. Adds `package-specs` configuration for ES module output
23+
3. Adds `suffix: ".bs.js"` to generate `.bs.js` files
24+
25+
**When applied**: Automatically during `yarn install` via the `postinstall` script
26+
27+
**Upstream status**:
28+
- Opened issue: https://github.com/glennsl/rescript-json-combinators/issues/9
29+
- This is a common pattern for in-source builds with ReScript
30+
- May not be accepted upstream if author prefers source-only distribution
31+
32+
**TODO**: Check if patch is still needed when upgrading `@glennsl/rescript-json-combinators`
33+
34+
## How Patches Work
35+
36+
1. **Creation**: When you modify a package in `node_modules/`, run:
37+
```bash
38+
npx patch-package package-name
39+
```
40+
41+
2. **Application**: Patches are automatically applied after `yarn install` via the `postinstall` script in `package.json`
42+
43+
3. **Updating**: If the package is updated and the patch fails, you'll need to either:
44+
- Regenerate the patch with the new version
45+
- Remove the patch if it's no longer needed
46+
- Update the patch manually
47+
48+
## Maintenance
49+
50+
- **Before upgrading patched packages**: Check if the patch is still necessary
51+
- **If patch fails to apply**: The build will fail with a clear error message
52+
- **Review patches regularly**: Consider contributing fixes upstream to reduce maintenance burden

0 commit comments

Comments
 (0)