Fix webpack-dev-server static config: default to false, fix YAML passthrough - #1032
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughDefaults and YAML handling for webpack-dev-server Changeswebpack-dev-server static config fix
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR fixes three real bugs in Shakapacker's webpack-dev-server Key findings:
Confidence Score: 5/5Safe to merge — the core bug fixes are correct and all remaining findings are P2 style/edge-case suggestions. Both open findings are P2: the CHANGELOG placeholder is a trivial text fix, and the silent drop of non-object
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[createDevServerConfig called] --> B[Build base config\nstatic: false]
B --> C{devServerYamlConfig.static\n!== undefined?}
C -- No --> F[Keep static: false]
C -- Yes --> D{static === false?}
D -- Yes --> E[config.static = false]
D -- No --> G{typeof static === 'object'\n&& static !== null?}
G -- Yes --> H[config.static = static object]
G -- No --> I[⚠️ Silently ignored\nconfig.static stays false\ne.g. static: true or string]
E --> J[delete devServerYamlConfig.static]
H --> J
I --> J
F --> K[Process remaining YAML keys\nsnake_case to camelCase]
J --> K
K --> L[Return WebpackDevServerConfig]
Reviews (1): Last reviewed commit: "docs: update changelog with PR number" | Re-trigger Greptile |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2645056893
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
ReviewThe fix is well-motivated and the root-cause analysis is solid. Defaulting One correctness issue to address:
Everything else looks good:
|
ReviewThe fix is well-motivated and the core change is correct: One real issue:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@package/webpackDevServerConfig.ts`:
- Around line 104-114: The file is missing a trailing newline and the
devServerYamlConfig.static handling only accounts for false and object types;
update the code to add a final newline and to set config.static when
devServerYamlConfig.static is a string or array (e.g., if typeof === "string"
assign the string, if Array.isArray assign the array cast to Array<string |
Record<string, unknown>>), keeping the existing false and object branches and
still deleting devServerYamlConfig.static afterwards; also update the
WebpackDevServerConfig.static type definition (the static property in the config
interface) to include string and Array<string | object> variants so the types
align with webpack-dev-server API.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5796f134-083b-442b-855a-214624d3508d
📒 Files selected for processing (4)
CHANGELOG.mdlib/install/config/shakapacker.ymlpackage/webpackDevServerConfig.tstest/package/webpackDevServerConfig.test.js
💤 Files with no reviewable changes (1)
- lib/install/config/shakapacker.yml
2645056 to
e7a1417
Compare
ReviewThis is a solid, focused fix for a real pain point. The root cause analysis is correct: the old What is good
One issue to address See the inline comment on lines 104–113 of Minor nit The test for "passes through static: false from YAML config" is technically redundant with the default test (the default is already |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e7a1417458
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
ReviewThe fix is correct and well-motivated. The old default ( Bug: unhandled truthy non-object When Minor: one test does not cover the logic it implies The test "passes through static: false from YAML config" sets Everything else looks good
|
ReviewThe fix is well-motivated and the core logic is sound. The three bugs (wrong default, Two issues to address before merging: 1. TypeScript type doesn't cover The 2. Off-by-one indentation in the A stray space gives the closing Everything else looks good:
|
3f975a9 to
2c4bedd
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2c4bedd784
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
Overall: solid fix for a real bug. The truthy-check correction and false default are correct. One type-safety issue to address: see inline comments. |
ReviewThe fix is correct and well-motivated. Defaulting A few nits/issues to address: 1. Redundant
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0083f34f51
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
6230078 to
d809bbe
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
test/package/webpackDevServerConfig.test.js (1)
117-125: ⚡ Quick winAdd an explicit regression test for
static: null(YAML bare key).This behavior is part of the intended contract; a focused test will prevent regressions where
nullaccidentally overrides defaults.Suggested test case
test("passes through static array from YAML config", () => { const devServer = require("../../package/dev_server") devServer.static = ["/path1", "/path2"] const createDevServerConfig = require("../../package/webpackDevServerConfig") const config = createDevServerConfig() expect(config.static).toStrictEqual(["/path1", "/path2"]) }) + + test("treats static: null as unset and keeps default", () => { + const devServer = require("../../package/dev_server") + devServer.static = null + + const createDevServerConfig = require("../../package/webpackDevServerConfig") + const config = createDevServerConfig() + + expect(config.static).toBe(false) + })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/package/webpackDevServerConfig.test.js` around lines 117 - 125, Add a focused regression test in test/package/webpackDevServerConfig.test.js that verifies a YAML bare key producing static: null does not override defaults: require("../../package/dev_server"), set devServer.static = null, call createDevServerConfig (require("../../package/webpackDevServerConfig")) and assert the returned config.static equals the expected default (i.e., the same value used when devServer.static is undefined) so null does not replace the default static configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CHANGELOG.md`:
- Line 65: Remove the duplicate "### Fixed" heading inside the v10.0.0 release
block: locate both occurrences of the "### Fixed" heading within that release
section and delete the second one, then move any bullet(s) under the removed
heading so all "Fixed" items are listed together under the single remaining "###
Fixed" heading.
---
Nitpick comments:
In `@test/package/webpackDevServerConfig.test.js`:
- Around line 117-125: Add a focused regression test in
test/package/webpackDevServerConfig.test.js that verifies a YAML bare key
producing static: null does not override defaults:
require("../../package/dev_server"), set devServer.static = null, call
createDevServerConfig (require("../../package/webpackDevServerConfig")) and
assert the returned config.static equals the expected default (i.e., the same
value used when devServer.static is undefined) so null does not replace the
default static configuration.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 37e2d069-8053-4988-bc28-fb99a8b84a0d
📒 Files selected for processing (4)
CHANGELOG.mdlib/install/config/shakapacker.ymlpackage/webpackDevServerConfig.tstest/package/webpackDevServerConfig.test.js
💤 Files with no reviewable changes (1)
- lib/install/config/shakapacker.yml
d809bbe to
7658135
Compare
Code ReviewThis is a well-motivated fix for a real user-facing issue (#1031). The root cause analysis in the PR description is accurate, the YAML passthrough matrix is thorough, and having 11 new tests is great. A few things to address before merging: Must-Fix1. CHANGELOG entry is under The entry was inserted just before Code Quality2. The All four branches do the same thing: assign the value to if (devServerYamlConfig.static != null) {
config.static = devServerYamlConfig.static as WebpackDevServerConfig["static"]
delete devServerYamlConfig.static
}( 3. New tests use direct module mutation; existing tests use The two existing tests in the file use 4. Missing test for The PR description calls out the test("treats static: null (YAML ~) as unset, falling back to false", () => {
const devServer = require("../../package/dev_server")
devServer.static = null
const createDevServerConfig = require("../../package/webpackDevServerConfig")
const config = createDevServerConfig()
expect(config.static).toBe(false)
})Positives
|
| if ( | ||
| devServerYamlConfig.static !== undefined && | ||
| devServerYamlConfig.static !== null | ||
| ) { | ||
| if (devServerYamlConfig.static === false) { | ||
| config.static = false | ||
| } else if (Array.isArray(devServerYamlConfig.static)) { | ||
| config.static = devServerYamlConfig.static as Array< | ||
| string | Record<string, unknown> | ||
| > | ||
| } else if (typeof devServerYamlConfig.static === "object") { | ||
| config.static = devServerYamlConfig.static as Record<string, unknown> | ||
| } else { | ||
| config.static = | ||
| devServerYamlConfig.static as WebpackDevServerConfig["static"] | ||
| } | ||
| delete devServerYamlConfig.static | ||
| } |
There was a problem hiding this comment.
All four branches here do the same thing — assign the value to config.static — with different casts for TypeScript. Since devServerYamlConfig.static is typed as unknown (see types.ts), a single cast to the full union type covers every case. The Array.isArray / typeof === "object" guards add runtime branching without changing the runtime result. Consider simplifying:
| if ( | |
| devServerYamlConfig.static !== undefined && | |
| devServerYamlConfig.static !== null | |
| ) { | |
| if (devServerYamlConfig.static === false) { | |
| config.static = false | |
| } else if (Array.isArray(devServerYamlConfig.static)) { | |
| config.static = devServerYamlConfig.static as Array< | |
| string | Record<string, unknown> | |
| > | |
| } else if (typeof devServerYamlConfig.static === "object") { | |
| config.static = devServerYamlConfig.static as Record<string, unknown> | |
| } else { | |
| config.static = | |
| devServerYamlConfig.static as WebpackDevServerConfig["static"] | |
| } | |
| delete devServerYamlConfig.static | |
| } | |
| if (devServerYamlConfig.static != null) { | |
| config.static = devServerYamlConfig.static as WebpackDevServerConfig["static"] | |
| delete devServerYamlConfig.static | |
| } |
!= null catches both null and undefined, which matches the intent of the existing two-condition guard.
There was a problem hiding this comment.
Acknowledged — a single cast to WebpackDevServerConfig["static"] would compress fine. The current four-branch shape was reached deliberately over earlier review rounds (the "honest TypeScript cast" thread) to make the runtime cases explicit at the point of assignment rather than hide them behind one wide cast. Same overall behavior either way; leaving the explicit form for now unless @ihabadham wants to revisit.
| test("passes through static: false from YAML config", () => { | ||
| const devServer = require("../../package/dev_server") | ||
| devServer.static = false | ||
|
|
||
| const createDevServerConfig = require("../../package/webpackDevServerConfig") | ||
| const config = createDevServerConfig() | ||
|
|
||
| expect(config.static).toBe(false) | ||
| }) |
There was a problem hiding this comment.
Two concerns with the testing approach used in the new tests:
Pattern inconsistency: The two existing tests in this file use jest.isolateModules + jest.doMock() for full module isolation. These new tests instead require the live module and mutate its exports directly (devServer.static = false). This works because jest.resetModules() in beforeEach clears the cache so webpackDevServerConfig picks up the mutation when it's required next, but it's fragile and inconsistent with the file's established style.
This specific test: The result (config.static === false) is identical to the default behavior, so the test would pass even if the explicit-false passthrough branch were removed. It still has documentation value and would catch a regression if the default ever changed, but it's weaker than it appears. A comment explaining what regression it guards against would help future readers.
There was a problem hiding this comment.
Considered both points:
- Pattern:
jest.isolateModules+jest.doMockis needed when several modules must be mocked together (the middleware-hook tests mock bothdev_serverandconfig). For thestatictests we mutate a single property ondevServerandjest.resetModules()inbeforeEachalready gives a clean require cache between tests. Wrapping each one inisolateModuleswould add boilerplate without changing isolation. - Coverage of the explicit-
falsetest: It's intentionally a regression guard against the default ever flipping back to a truthy value. The test name documents the intent. Leaving as-is.
Documents expected behavior for static config: default to false, pass through YAML static: false, and pass through static objects. Some tests fail against current code — fixes follow in next commits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three bugs fixed:
1. Default static changed from { publicPath: contentBase } to false.
The old default set static.publicPath to a filesystem path (config.outputPath)
instead of a URL path, and never set static.directory, causing
webpack-dev-server to watch the entire public/ directory unnecessarily.
In Rails apps, static file serving is handled by ActionDispatch::Static,
not webpack-dev-server.
2. The truthy check 'if (devServerYamlConfig.static)' now uses
'if (devServerYamlConfig.static !== undefined)' so that setting
static: false in shakapacker.yml is no longer silently ignored.
3. When a user provides a static object in YAML, it is passed through
directly instead of being merged with the (now-removed) buggy base.
Also removes static.watch from the default shakapacker.yml template,
since static is now false by default and the watch config was only
needed to work around the old buggy static default.
Fixes #1031
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… them If a user sets static: true, a string, or any other value in shakapacker.yml, pass it through to webpack-dev-server as-is instead of silently discarding it.
- Type now accepts boolean | string | string[] | object (matching webpack-dev-server's actual schema) instead of just false | object - Bare `static:` in YAML (parses as null via js-yaml) is now treated the same as undefined — skipped, default false sticks
…pt cast Previously, an array `static: ["/p1", "/p2"]` in shakapacker.yml entered the `typeof === "object"` branch and was cast to `Record<string, unknown>`, which is a type lie. Add an explicit `Array.isArray` branch and widen the type union accordingly. Behavior unchanged at runtime.
Addresses PR review: - CHANGELOG entry was incorrectly placed under released [v10.0.0] with a duplicate ### Fixed heading. Moved it to [Unreleased]. - Added explicit test that static: null (YAML bare `static:` / `static: ~`) falls back to the default false, matching the behavior promised in the PR description.
b23d5bc to
22e7150
Compare
Code ReviewOverall: This is a well-motivated fix for a real bug. The root cause analysis in the PR description is accurate — the old default was a misconfigured object that caused webpack-dev-server to watch A few things to address: 1. Dispatch chain in
|
| if (devServerYamlConfig.static === false) { | ||
| config.static = false | ||
| } else if (Array.isArray(devServerYamlConfig.static)) { | ||
| config.static = devServerYamlConfig.static as Array< | ||
| string | Record<string, unknown> | ||
| > | ||
| } else if (typeof devServerYamlConfig.static === "object") { | ||
| config.static = devServerYamlConfig.static as Record<string, unknown> | ||
| } else { | ||
| config.static = | ||
| devServerYamlConfig.static as WebpackDevServerConfig["static"] | ||
| } | ||
| delete devServerYamlConfig.static |
There was a problem hiding this comment.
All four branches of this dispatch do the same thing at runtime: assign devServerYamlConfig.static to config.static. TypeScript as casts are compile-time only — they have no effect at runtime, so the Array.isArray / typeof === "object" / else split is pure ceremony.
Since WebpackDevServerConfig["static"] is already a union that covers boolean | string | Array<...> | object, a single cast is both accurate and simpler:
| if (devServerYamlConfig.static === false) { | |
| config.static = false | |
| } else if (Array.isArray(devServerYamlConfig.static)) { | |
| config.static = devServerYamlConfig.static as Array< | |
| string | Record<string, unknown> | |
| > | |
| } else if (typeof devServerYamlConfig.static === "object") { | |
| config.static = devServerYamlConfig.static as Record<string, unknown> | |
| } else { | |
| config.static = | |
| devServerYamlConfig.static as WebpackDevServerConfig["static"] | |
| } | |
| delete devServerYamlConfig.static | |
| config.static = devServerYamlConfig.static as WebpackDevServerConfig["static"] | |
| delete devServerYamlConfig.static |
| const createDevServerConfig = require("../../package/webpackDevServerConfig") | ||
| const config = createDevServerConfig() | ||
|
|
||
| // Test app has hmr: true, so liveReload should default to false |
There was a problem hiding this comment.
This comment narrates what the test expects rather than explaining a non-obvious constraint — the project style guide asks us to omit such comments. It also creates a fragile implicit dependency on the test app's YAML (hmr: true): a future change to that config would break this assertion with no obvious connection between the symptom and the cause.
Consider making the dependency explicit by setting hmr in the test itself (similarly to how other tests mutate devServer.static):
| // Test app has hmr: true, so liveReload should default to false | |
| test("defaults liveReload to inverse of hmr", () => { | |
| const devServer = require("../../package/dev_server") | |
| devServer.hmr = true | |
| const createDevServerConfig = require("../../package/webpackDevServerConfig") | |
| const config = createDevServerConfig() | |
| expect(config.liveReload).toBe(false) | |
| }) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 22e71501f4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # port than your Rails server and the browser blocks cross-origin asset requests): | ||
| # headers: | ||
| # "Access-Control-Allow-Origin": "*" |
There was a problem hiding this comment.
Keep
static key in generated dev_server config
Removing dev_server.static from the install template makes SHAKAPACKER_DEV_SERVER_STATIC=... impossible to apply in newly generated apps, because package/dev_server.ts only reads env overrides for keys already present in YAML (Object.keys(devServerConfig)). In this same change, webpackDevServerConfig now correctly passes through boolean/string/array/object static values, so a template default of static: false would preserve the new default while still allowing environment-only toggles; with the key deleted, operators cannot re-enable static serving via env without editing YAML.
Useful? React with 👍 / 👎.
## Summary Stamps the `v10.1.0-rc.1` header and collapses `v10.1.0-rc.0` per the project's RC collapse convention. - Renamed `## [v10.1.0-rc.0] - May 20, 2026` → `## [v10.1.0-rc.1] - May 21, 2026` - Moved the post-rc.0 `Unreleased` Fixed entry for PR #1032 (webpack-dev-server `static` config) into the merged `### Fixed` section - Dropped the PR #1120 entry (`shakapacker:check_node` regression fix) — it only fixes a bug introduced by PR #1110 that shipped solely in `v10.1.0-rc.0`, so users going from `v10.0.0` → `v10.1.0-rc.1` never see it - Updated `[Unreleased]` and `[v10.1.0-rc.1]` diff links ## Test plan - [ ] CI passes - [ ] Maintainer confirms the rc.0 → rc.1 collapse looks right <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk documentation-only change that updates release notes and comparison links without affecting runtime code. > > **Overview** > **Updates `CHANGELOG.md` for the `v10.1.0-rc.1` release.** > > Renames the `v10.1.0-rc.0` section to `v10.1.0-rc.1` (with the new date), moves the webpack-dev-server `static` fix (PR #1032) from *Unreleased* into the release’s *Fixed* section, removes the rc.0-only `shakapacker:check_node` regression note (PR #1120), and updates the bottom compare links to point at `v10.1.0-rc.1`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit eaf83d5. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Fixes three bugs in Shakapacker's webpack-dev-server
staticconfiguration that cause unnecessary inotify watches and ENOSPC crashes on systems with limited headroom.staticfrom a misconfigured object tofalse— Rails servespublic/viaActionDispatch::Static, webpack-dev-server doesn't need toif (devServerYamlConfig.static)truthy check sostatic: falseinshakapacker.ymlis no longer silently ignoredstatic.watchfrom the default install template — it was a v3→v4 migration artifact that caused webpack-dev-server to watchpublic/via chokidarstaticvalues (true, strings, arrays, objects) from YAML — if a user explicitly overrides the default, we respect itFixes #1031
Details
The old default set
static.publicPathto a filesystem path (config.outputPath) instead of a URL path, and never setstatic.directory, causing webpack-dev-server to default to watching the entirepublic/directory. This has been present since v6.0.0-rc.7 but was harmless on most systems. On systems near their inotify watch limit (e.g., Fedora with auto-calculated limits + watch-heavy tools like Warp terminal), it triggersENOSPC: System limit for number of file watchers reached.Setting
static: falseis safe because:Shakapacker::DevServerProxy(hardwired in the railtie) proxies/packs/*to webpack-dev-server — the browser never accesses webpack-dev-server directlydevMiddleware, which is independent ofstaticActionDispatch::Staticservespublic/files in RailsUsers who explicitly set
staticinshakapacker.yml(totrue, an object, a string path, etc.) will have their value passed through to webpack-dev-server as-is. Barestatic:(YAML null) is treated the same as unset — the defaultfalseapplies.Test plan
test/package/webpackDevServerConfig.test.jscovering default behavior,false/true/string/object/array passthrough, and existing key mappingyarn lint— 0 errorsyarn type-check— cleanspec/dummy— wrote a harness that swaps the dummy app'sshakapacker-webpack.ymland runscreateDevServerConfig()against each scenario:statickey in YAMLstatic: false(new default)static: false(regression)static: false(previously silently ignored)static: truestatic: true(passed through)static: /custom/static"/custom/static"(string passed through)static: { directory, watch.ignored }static: [/path1, /path2]static: ~(YAML null)false🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
false(previously defaulted to an invalid value that caused unintended watching ofpublic/)static: falsesetting from configuration files to be properly respected instead of being silently ignored