Repository navigation
EPBDS-16211 H9: burn the dev-tooling audit down to zero, and record today's decisions - #128
Merged
Merged
Conversation
…oday's decisions
npm audit: 28 findings (2 critical, 11 high, 12 moderate, 3 low) -> 0.
The production audit was 0 and stays 0.
- `npm audit fix` moved 45 lock entries inside their declared ranges and
closed 21, both criticals among them.
- webpack-dev-server 5 -> 6 closed webpack-dev-server, sockjs, uuid and
qs (Express 5 resolves qs 6.16). Only `npm start` uses it; the demo
starts, hot-reloads over ws, and logs no error.
- react-router and react-router-dom 6 -> 7 (demo only; 8 needs
React >= 19.2.7). Version 7 wraps location updates in startTransition,
which brought back the demo's extra history entry:
NavTabs.history.test.js caught it, and the demo now mounts
BrowserRouter with useTransitions={false}. Checked in the running demo:
two tab clicks push two entries, and Back and Forward push none. The
test loads the router after installing Node's TextEncoder, which the
router reads at module load and jsdom lacks.
- moment's dev copy ~2.29.4 -> ~2.31.0 (GHSA-4p3w-j4w9-5jqw). The peer
range stays ^2.29.4: the library never calls moment.locale, and a
host's floor is a consumer-facing decision.
All 282 files build-lib publishes are byte-identical before and after.
The plan records the burn-down and the decisions taken today:
- the TypeScript tail is a go;
- moment stays;
- ESM is not now;
- the form-stack bump is postponed, because since `dependencies` was
emptied it no longer caps a host's React. Its cost on 7.0.1 is
recorded: mount commits 107 -> 118 on the corpus, `translate` calls
1013 -> 1046, `defaultValue` missing from the first render, and 5
failing tests including a popup regression.
It also marks Phase 6 done in §10 and corrects two stale §8 rows.
This was referenced Sep 30, 2026
AlexSamBY
added a commit
that referenced
this pull request
Sep 30, 2026
…#130) ## The problem, introduced by #128 #128 moved `webpack-dev-server` to 6. `@pmmmwh/react-refresh-webpack-plugin` 0.5.17 declares it as an optional peer only up to 5.x: ``` peerOptional webpack-dev-server@"3.x || 4.x || 5.x" from @pmmmwh/react-refresh-webpack-plugin@0.5.17 ``` `npm ci` installs the lock regardless, which is why #128's and #129's CI stayed green. But every `npm install <package>` since then aborts with `ERESOLVE`. It was found while adding a devDependency for the React 19 leg. ## The fix `@pmmmwh/react-refresh-webpack-plugin` `^0.5.16` → `^0.6.3`. 0.6.3 (2026-08-27) adds `webpack-dev-server` 6.x to that peer range ([#1046](pmmmwh/react-refresh-webpack-plugin#1046) upstream). The one breaking release between them, 0.6.0, raised minimums we already exceed (Node 18.12, webpack 5.2, webpack-dev-server 4.8). It also removed `overlay.sock*` options we never set: the demo uses `new ReactRefreshWebpackPlugin()` with no options. ## Checked - **The install that failed now succeeds**, run as `npm install --dry-run --save-dev <the same packages>`. - **`npm ls --all` reports no invalid peer**, and `npm audit` stays at 0. - **Fast refresh works on the new plugin.** In the running demo (`npm start`), the refresh runtime is injected (`__reactRefreshInjected`). A temporary edit to `NavTabs.jsx`, reverted after, is hot-applied: `[HMR] Updated modules: ./src/demo/components/NavTabs.jsx` and `[HMR] App is up to date.` A marker set on `window` beforehand survives, so the page did not reload. - **The published package is unchanged.** All 282 files `build-lib` publishes are byte-identical to master's; the plugin is demo-only. ## Gates All exited 0: - `lint:js`, `lint:css`, `typecheck`; - `test:env-flags`, `test:coverage`, `build`, `build-lib`; - `test:react16` and `test:react17`, 2907 tests on each; - the manifest contract. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
AlexSamBY
added a commit
that referenced
this pull request
Sep 30, 2026
On master since #130 merged, which fixed the `npm install` peer conflict this change needed gone to add a devDependency. The rebased tree is identical to the one the local gates below ran on. ## The flip `peerDependencies`: `react` and `react-dom` now read **`^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0`**. The range is widened additively, so hosts on 16.14, 17 and 18 change nothing. This closes §8's fast path: - gates 1 (Semantic UI React gone) and 3 (`defaultProps`, then the automatic JSX runtime in #129) were met; - gate 2 (the form stack) lapsed when `dependencies` was emptied (#128 records why); - this PR is gate 4. ## How 19 is gated, in three matrices | | 16.14 | 17 | 18 | 19 | |---|---|---|---|---| | whole jest suite | `test:react16` | `test:react17` | `npx jest` / `test:coverage` | **`test:react19` (new)** | | packed artifact server-rendered | `test:pack:peers` | `test:pack:peers` | `test:pack:consumer` | **`test:pack:peers` (added 19.3.0)** | | published `.d.ts` compiled | `test:types` | `test:types` | `test:types` | **`test:types` (added `@types/react` 19.3.0)** | - **The suite on React 19 is a fixture leg, like 16.14 and 17.** `scripts/fixtures/react19` is an install-only package pinning `react`/`react-dom` 19.3.0. Plain `npm ci` nests it, it is mapped in at resolve time, and `npm run test:react19` runs it locally too. The harness still asserts the loaded React (19.3.0) and scheduler (0.28) in every worker. - **What differs for 19.** React 19 has no legacy root and ships `react-dom/client`, so the leg renders through `createRoot` as the default suite does. `floors.js` records `legacyRoot: false`, which skips three things that exist only for 16/17: the RTL `legacyRoot` patch, the `ReactDOM.render` assertion and the `react-dom/client` stub. - **CI.** The new `react-19` job replaces the `continue-on-error` `react-19-advisory` job, which installed React 19 over the lock with `--legacy-peer-deps`; the new job needs neither. No required checks are configured on master today, so `react-19` gates exactly as `react-16-floor` and `react-17` do. If branch protection ever named `react-19-advisory`, it should name `react-19` now. - **Types.** The published declarations compile against `@types/react` 19, with both the interop default import and the direct CommonJS import. That matters because 19 removed the global `JSX` namespace. `gen-ts` stays on the dev tree's 18; the plan's "pin `@types/react@19` for `gen-ts`" item was written assuming the dev tree would move to 19, and it does not. ## Docs - README and the demo's docs page state the new range. - The demo's changelog (Unreleased) says React 19 is supported and how it is tested. It also records #129's change: the bundle imports `react/jsx-runtime` from the host's React, which a host that aliases `react` to one copy has to alias too. Two older Unreleased paragraphs that said the range was not widened yet are updated, so the section does not contradict itself. - `CLAUDE.md` lists the three React legs. - The plan closes §8's gate 4, the CI-coverage row, the `gen-ts` row, the Appendix C "React 19 flip" checklist and §10's Phase 7 React 19 item. ## Gates All exited 0: - `lint:js`, `lint:css`, `typecheck`, `typecheck:contract`; - `css:fixture:check`, `docs:props:check`, `docs:views:check`; - `test:env-flags`, `test:types`, `test:coverage`, `build`; - `test:pack`, `test:pack:peers` (16.14.0, 17.0.2, 19.3.0); - `test:react16`, `test:react17`, `test:react19`, 2907 tests on each, as on 18; - `test:e2e`, 41 Playwright tests; - the manifest contract. Two first runs lost a jest worker to `SIGSEGV`, each killing one suite before its tests ran: `test:react19` once and `test:react17` once. That is the Node 24 V8 garbage-collector crash ([nodejs/node#62393](nodejs/node#62393)), fixed on `v24.x-staging` but not in a release yet. Both passed 2907/2907 on rerun. CI runs Linux, where it has not been seen. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Audit: 28 → 0
npm auditnpm audit --omit=devnpm audit fixmoved 45 lock entries inside their declared ranges (Babel, PostCSS, browserslist, svgo, axios, ws, …). It closed 21 findings, both criticals among them (shell-quote,websocket-driver).webpack-dev-server5 → 6 closedwebpack-dev-server,sockjs,uuidandqs: Express 5 resolvesqs6.16, where Express 4 pinned~6.14.0. Onlynpm startuses the dev server, since Playwright builds the demo and serves it statically. Checked in the running demo: it compiles, hot-reloads overws, and logs no error.react-routerandreact-router-dom6 → 7.18.4. The demo is the only user; 8.x needs React ≥ 19.2.7. Version 7 wraps location updates instartTransitionby default. That brought back the extra history entryNavTabs.history.test.jspins: after a click the tabs were still given the previous location's tab for a render,StandaloneTabsreported it, andNavTabsnavigated back to/. Traced with the tab reports logged. The demo now mountsBrowserRouterwithuseTransitions={false}, which is 6's synchronous update. Checked in the running demo: two tab clicks push two history entries, and Back then Forward push none. The test also loads the router after installing Node'sTextEncoder, which 7 reads at module load and jsdom does not provide. That is scoped to this suite, the only one that loads the router.momentdev copy~2.29.4→~2.31.0(GHSA-4p3w-j4w9-5jqw, a path traversal through a crafted locale name). The peer range stays^2.29.4: the library never callsmoment.locale, so the advisory is not reachable through it. Raising a host's floor is a consumer-facing decision this PR does not take.The published package did not change
build-libwas run before and after, and all 282 files it publishes (dist/and the rootstatic/payload) are byte-identical. That includesstatic/all.cssatdcdb0a40…, the hash the plan has recorded since 2026-09-17. Two builds of the same tree were identical first, so the comparison is meaningful.The plan
docs/UPGRADE-PLAN.mdrecords the burn-down in the §10 gate table and the Appendix B baseline. It also records the decisions taken today:moment: keep (§9.7-F2).dependencieswas emptied.react ^18peer caps nobody, and the React 19 advisory job runs the whole suite on 6.5.9 green. The cost measured today on 7.0.1 is recorded:translatecalls 1013 → 1046 over the 38 examples;defaultValueis missing from the first render;It also marks Phase 6 done in §10, and corrects two stale §8 rows (the
semantic-ui-reactblocker, gone with the package;propTypesimporters 40 → 31).Gates
All exited 0:
lint:js,lint:css,typecheck;css:fixture:check,docs:props:check,docs:views:check;test:env-flags,test:coverage,build,build-lib;test:react16andtest:react17, 2907 tests on each;npx jest, 2907 tests;test:e2e, 41 Playwright tests;🤖 Generated with Claude Code