Repository navigation
build(frontend): migrate react-ystemandchess from CRA to Vite - #252
Draft
Deepesh-Katudia wants to merge 8 commits into
Draft
Deepesh-Katudia wants to merge 8 commits into
Deepesh-Katudia wants to merge 8 commits into
Conversation
Resolves the 33 audit findings that remained after `npm audit fix`, all of which were locked behind react-scripts@5.0.1 (unmaintained upstream). npm reported the only "fix" for react-scripts as version 0.0.0, i.e. none exists, and an npm override could not close the gap either: CRA's own dev-server config calls onBeforeSetupMiddleware/onAfterSetupMiddleware/https, all removed in webpack-dev-server 5, while the advisory fix requires >= 5.2.1. Audit: 47 findings -> 5. Zero critical, high, or low remaining. The 5 moderate are express (+body-parser/qs), which exist only for dead server-side code under src/, and react-router/react-router-dom, deferred to their own task. Build tooling: - Replace react-scripts with vite, @vitejs/plugin-react, vite-plugin-svgr. - Move public/index.html to the project root; %PUBLIC_URL% -> /. - build.outDir 'build' so the Dockerfile's `serve -s build` is unchanged. - Define process.env.REACT_APP_API_URL / NODE_ENV so the two components and the environments module that read them keep working under Vite and Jest. Things CRA had been doing implicitly, now declared explicitly: - postcss.config.js: CRA auto-detected tailwind.config.js and injected the tailwindcss PostCSS plugin. Vite does not, so Tailwind silently stopped compiling until this was added. - uuid: promoted to a real dependency. It is imported by Puzzles.tsx and Student.tsx but resolved only via react-scripts > webpack-dev-server > sockjs, so removing CRA would have broken the build. - jest.config.js: setupFiles (whatwg-fetch, previously supplied by react-app-polyfill/jsdom) and setupFilesAfterEnv (src/setupTests.ts), both of which react-scripts wired up on its own. SVG imports move to Vite's `?react` convention; bare .svg imports stay asset URLs. Both paths verified in a browser. Test suite is now standalone Jest and reaches parity with the CRA baseline (27 suites / 149 tests). Fixing it surfaced two real defects: - babel.config.js lacked the automatic JSX runtime, so `npx jest` failed 14 suites with "React is not defined" while `react-scripts test` passed. - Two ParentSignUp specs submitted a form with empty `required` inputs and passed only because jsdom 16 (Jest 27) did not implement interactive constraint validation. jsdom 20 and every real browser block that, so they now submit the form directly to exercise the component's own validate(). Also: pin the Dockerfile to node:20.19.0-alpine (was node:18.20.8-alpine, EOL April 2025), which Vite requires; drop CI=false from the frontend build; and remove the dead @svgr/webpack devDependency and eslintConfig block that referenced react-scripts' now-absent eslint-config-react-app.
…w-ups Covers why npm overrides could not close the react-scripts-locked findings, the 47 -> 5 audit result, the four behaviours CRA had been supplying implicitly, the two pre-existing test defects the migration surfaced, and the verification that was and was not completed.
Brings in #208 (environment separation), #247 (Tailwind standardization) and the other changes merged since the branch was cut on 2026-08-23. Conflicts in Lessons.tsx, ActivitiesModal.tsx and LeaderboardModal.tsx were import-only: main's side is kept (SCSS modules dropped by #247, environment imported from ../environments), with SVG components switched from CRA's `{ ReactComponent as X }` to Vite's `X from './x.svg?react'`.
environment.prod.js (from #208) read vars via process.env[name]. Vite's define is a literal text replacement and cannot resolve a dynamic key. Vite 8 rewrites the leftover bare `process.env` to `{}`, so the lookup compiled to `{}[name]`, which is always undefined. The required-URL guard then threw on load and blanked the whole site. Jest runs in Node, so the test suite could not catch it. - environment.prod.js: reference every variable by its full literal name - vite.config.mts: define all six REACT_APP_* vars the app reads, loaded via loadEnv so .env files work as they did under CRA; NODE_ENV derives from the Vite mode - scripts/verify-build-env.mjs + CI: fail if process.env survives in shipped JS/HTML, or if a required URL was not inlined into the bundle (the second check is what actually catches the {}[name] case; verified it fails against main's old environment.prod.js and passes on this one) - Dockerfile and volta pin: Node 20.19.0 (EOL) -> 24.21.0 LTS
reset-password.tsx and set-password.tsx read process.env.REACT_APP_API_URL, which nothing sets (not CI, the Dockerfile, deploy.yml or tag_build_containers.sh). Production builds therefore called http://localhost:8000/user/... for both requests. This predates the Vite migration; CRA behaved the same. Both now use environment.urls.middlewareURL, the same base every other /user/ call uses. REACT_APP_API_URL is dropped from vite.config's define. Kept as its own commit so it can be reverted independently if the endpoint behaves differently than expected. Verify a real reset email end to end after deploy.
npm install can resolve versions that differ from package-lock.json, so image builds could drift from what CI tested. npm ci installs exactly the lockfile and fails if it is out of sync with package.json.
The plan covers what changed after #208 merged (the env var blank-page bug, merge conflicts, Node 24, password-reset URL), the prerequisite branches that gate the merge, and coordination with the CD pipeline. The original migration write-up stays as the reference for the migration itself.
Collaborator
|
Please fix the conflicts so i cna review |
Conflicts: - package.json: keep main's engines.node ">=24 <25" from the Node 24 pin. - StreakModal.tsx: keep the branch's `?react` SVG imports, take main's removal of the calendar placeholder, and import environment from `environments` rather than `environments/environment` (which hardcodes the dev URLs in production). Also fix the same environments/environment import that #250's activitiesApi.ts brought in without a conflict.
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.
Summary
Replaces Create React App with Vite for
react-ystemandchess. CRA is unmaintained, and its pinned toolchain was the source of most frontend audit findings: 47 findings go down to 5, none high or critical, with full test parity.This branch was cut before #208 merged. It is now updated against current main, including a fix for a bug that would have blanked the production site after merging.
Draft until: a real
docker buildis run and checked in a browser, and the Phase 0 prerequisites below merge.documentation/frontend-cra-to-vite-migration.mddocumentation/frontend-vite-merge-plan.mdType of Change
Key Changes
react-scriptsremoved. Vite 8 with@vitejs/plugin-reactandvite-plugin-svgrreplaces it, and Jest now runs standalone. Output stays inbuild/on port 3000, so the runtime stage,serve -s build, and deploy build args are unchanged.environment.prod.jsread variables withprocess.env[name]. Vite can't replace a dynamic key, and Vite 8 rewrites the leftover bareprocess.envto{}. The lookup compiled to{}[name], so the required-URL guard threw on load and the page went blank. Build, Jest, the Docker healthcheck, and a curl smoke test all still passed.environment.prod.jsnow references each variable by its full literal name.vite.config.mtsdefines everyREACT_APP_*variable, read withloadEnvso.envfiles work as they did under CRA.scripts/verify-build-env.mjsruns in the build step with the same env as the build. It fails ifprocess.envsurvives in shipped JS/HTML, or if a required URL wasn't inlined. A plainprocess.envgrep would not have caught this bug, because the broken bundle contains noprocess.envtext. The URL check does catch it: the script fails against main's oldenvironment.prod.jsand passes on this branch.baa766d7):reset-password.tsxandset-password.tsxreadREACT_APP_API_URL, which nothing sets, so production fell back tohttp://localhost:8000. Both now useenvironment.urls.middlewareURL. This bug predates Vite.npm ciinstead ofnpm install.Lessons.tsx,ActivitiesModal.tsx, andLeaderboardModal.tsxwere in import lines only. Main's side is kept (no SCSS modules after Tailwind standardization #247, andenvironmentimported from../environments), with SVGs switched to?reactimports.29d8df73, Oct 8):StreakModal.tsx(week2: Wire StreakModal to live streak data; record completedDates on… #251) conflicted on the same import pattern. It's resolved the same way, with?reactSVGs andenvironmentfrom../environments.activitiesApi.ts(feat(middlewareNode): currency ledger + leaderboard swap (Karthik + Jimmy's Rev. 2 lanes) #250) merged cleanly but imported the dev-onlyenvironments/environment, which would have pointed production at the dev URLs, so it's fixed here too.package.jsonkeeps main'sengines.nodefrom the Node 24 pin. After the merge, Jest passes 30/30 suites and 155/155 tests,tscis clean, andverify-build-env.mjspasses.Testing
tsc --noEmitis cleanvite buildwith the production URLs passesverify-build-env.mjsvite previewand loaded it in Chrome. The home page and/reset-passwordrender with no console errors, the bundle contains the production middleware URL, andlocalhost:8000andprocess.envare absentdocker buildwith all build args, then open the container in a browser (Docker was not available locally)Bugs Fixed (if applicable)
process.env[name]lookup from Fix environment separation #208.http://localhost:8000in production.TODO (Follow-up Work)
chore/npm-audit-fix-frontendmerges, and this branch then re-applies its dependency changes (keepsocket.io-client ^4.8.3). The Node 24 pin has merged ✅. Then the CD pipeline (sahana/208-azure-cicd) merge.deploy.yml, in the same release as this PR: it checks that the deployed JS contains the middleware URL. It has to ship together with Vite because the asset path changes from/static/js/to/assets/. See Phase 6 of the plan.setup-nodeis still 20.19.0 for every service. Bump it to 24 separately.public/chessclient/with nginx. That goes in a separate PR, after checking the live board.Additional Notes
Needs pipeline-owner review on the Dockerfile and on the smoke-test coordination. No changes to
deploy.ymlbuild args, contexts, or tags are needed.