Migrate from Create React App to Vite#305
Migrate from Create React App to Vite#305ankit0504 wants to merge 5 commits intoLanguage-Mapping:masterfrom
Conversation
Remove the dead IE-only msMaxTouchPoints check in isTouchEnabled (removed from lib.dom.d.ts in TS 4+; IE11 was EOL'd in 2022 and is already excluded by the project browserslist).
Replace react-scripts 3.4.1 with Vite 6 + @vitejs/plugin-react. This drops the NODE_OPTIONS=--openssl-legacy-provider workaround required for Node 17+ on CRA, and shrinks cold dev start to ~125ms and prod build to ~5s (from ~33s). Changes: - Swap scripts: yarn start -> vite; yarn build -> tsc --noEmit && vite build; yarn preview -> vite preview; drop eject/test. - Replace CRA deps with vite, @vitejs/plugin-react, vite-plugin-svgr, vite-tsconfig-paths. - Add vite.config.ts configuring the React and SVGR plugins, the REACT_APP_ env prefix (keeps .env working as-is, with VITE_ also accepted), port 3000, and build output to build/ (preserving the Netlify publish dir). - Move public/index.html to project root. Replace %PUBLIC_URL%/ with / and add the Vite module entry script. - Remove the CRA eslintConfig stanza in package.json; real config lives in .eslintrc. - Add a direct eslint@^7.32.0 devDep (previously hoisted by CRA), drop the react-app extends that no longer resolves, and remove the deleted serviceWorker.ts path from ignorePatterns. - Delete src/serviceWorker.ts and its import; it was already calling unregister() only, so removal is a no-op at runtime. - Rewrite six process.env.REACT_APP_* reads as import.meta.env. - Change Logo.tsx SVG import to use the vite-plugin-svgr ?react suffix. - Update tsconfig.json: target ES2020, add vite/client and vite-plugin-svgr/client types, exclude *.test.ts/tsx files from the build since there is no test runner anymore. - Delete src/setupTests.ts (orphaned CRA jest setup). Known follow-ups (deliberately out of scope for this PR): - @mapbox/mapbox-gl-geocoder imports the Node events module, which Vite externalizes for the browser. If the geocoder search breaks at runtime, it can be addressed by swapping react-map-gl-geocoder for a direct mapbox-gl-geocoder integration in a later phase. - yarn lint surfaces 55 pre-existing errors across the codebase that were previously masked by CRA's react-app eslint preset. These are unrelated to the build migration and should be addressed in a follow-up. Pre-commit lint-staged only runs against staged files, so new commits are unaffected.
Removing react-scripts in the previous commit unmasked pre-existing
eslint errors that CRA's react-app preset had been silencing.
Cleanup by category:
- 40 @typescript-eslint/no-unused-vars: set { args: "none" } in the
rule config. These were all unused callback parameters (mostly
createStyles theme args and event/props args in handlers) required
by library type signatures. The project-level policy is to not
enforce unused function arguments; genuine unused variables and
imports are still checked.
- 5 @typescript-eslint/no-use-before-define: reorder the five helper
function definitions so each precedes its call site. Affected:
createLayerStyles (hooks.points.ts), handleClose (SplitCrumbs.tsx),
scrollToTop (ResultsTable.tsx), clearFiltersBtnClick
(ResultsToolbar.tsx), excludeUTFtext (exporting.ts).
- 6 jsx-a11y/anchor-is-valid: five MUI <Link> usages that are
semantically buttons now use component="button" and drop the dummy
href="#" plus its e.preventDefault() plumbing. The sixth
(MapOptionsMenu) already uses component={Button}. All six get an
eslint-disable-next-line comment because the rule inspects JSX
element names and doesn't analyze MUI's component prop -- the
markup is correct, the rule is a false positive.
- 3 parser errors in excluded test files: add src/**/*.test.ts(x) to
.eslintrc ignorePatterns. The tsconfig already excludes these from
the build.
|
@ankit0504 I have upgraded other repos from CRA to Vite in the past and I fully support this, but just note that this is a pretty big change so I would like to be ready to do the smoke test, so it might not be until this weekend. Once we get past this housekeeping/upgrade stuff though, we should be able to move a little more quickly, especially if we can coordinate a good testing cadence with @rperlin-ela. Stay tuned and thanks again for contributing! |
The airtable package references process.env at runtime, which webpack/CRA provided but Vite does not. Defining an empty object prevents the ReferenceError without leaking server-side env vars. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
abettermap
left a comment
There was a problem hiding this comment.
@ankit0504 good stuff! Just make those Link -> Button changes unless you see a reason not to, and rename that vite config file (not sure why it worked for you but not me with the .ts extension), then I'll approve.
There was a problem hiding this comment.
@ankit0504 I was getting errors starting vite. GPT suggested workarounds:
- rename it (this worked for me):
mv vite.config.ts vite.config.mts - or set
"type": "module"in package.json. This was not advised since it's an older app, however, so let's go with the renaming of the file.
There was a problem hiding this comment.
ah i think it might have to do with running different node versions? seems like .mts is the right fix so i'll push that up!
There was a problem hiding this comment.
@ankit0504 while this does work, I'd prefer to just use a legit solution since there are 6 instances of this.
<Button
color="secondary"
// ...other props...
>
some text
</Button>I'm not sure why I used Link for this (maybe for styling?), but it's definitely not a link so let's go with Button (here and in the other 5 spots) unless you see any reason not to.
| import React, { FC } from 'react' | ||
| import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' | ||
| import { ReactComponent as ProjectLogo } from '../../img/logo.svg' | ||
| import ProjectLogo from '../../img/logo.svg?react' |
There was a problem hiding this comment.
suggestion, not related to this pr, but TS does have a nice way to use absolute imports so we're not stuck with lots of ../../ like this. If it's set up properly, we could do img/logo.svg?react from anywhere in the repo.
Just a wishlist thing for later though.
There was a problem hiding this comment.
same comment for here and the others, regarding Link -> Button
- Rename vite.config.ts to vite.config.mts for ESM compatibility - Replace MUI Link-as-button with Button in 6 components - Pass Airtable API key explicitly instead of relying on process.env - Migrate remaining process.env reference to import.meta.env Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@abettermap thanks for the feedback! i think the vite issue was because i am running a different version of node. fixed that and addressed your other comments :) also, i think i'm missing access to the mapbox tileset and the airtable census data base. are you able to share those two things with me? |
ross was able to help me with this, we're all set here! tested this PR with access to those and it's running totally fine for me locally! |
Issues resolved by this pull request
None — this is phase 2 of the staged upgrade effort. It does not close a tracked issue but does eliminate the OpenSSL workaround required to run the project on any Node version above 16, and clears the pre-existing lint backlog the CRA preset had been hiding.
Prerequisites
yarn installafter checkout (new Vite/SVGR deps; old CRA deps removed).NODE_OPTIONS=--openssl-legacy-providerflag is no longer required foryarn startoryarn build. Thenetlify.tomldoes not reference this flag, so no Netlify config change is needed..envchanges required — Vite is configured withenvPrefix: ['REACT_APP_', 'VITE_'], so existingREACT_APP_*variables continue to work.Review type
What's in this PR
Commit 1 — CRA → Vite migration
Build system:
react-scripts3.4.1 withvite@^6,@vitejs/plugin-react@^4,vite-plugin-svgr@^4,vite-tsconfig-paths@^5.vite.config.tsat project root.public/index.html→ project root (Vite convention). Replace%PUBLIC_URL%/tokens with/and add the module entry script.eslintConfigstanza inpackage.json(CRA artifact; real config is in.eslintrc).Env vars:
process.env.REACT_APP_*reads asimport.meta.env.REACT_APP_*..envfile as CRA did.Assets:
Logo.tsxSVG component import switched from CRA's{ ReactComponent as X }to vite-plugin-svgr's?reactsuffix convention.Cleanup:
src/serviceWorker.tsand its import (was alreadyunregister()-only, so no runtime behavior change).src/setupTests.ts(orphaned CRA/jest scaffolding).*.test.{ts,tsx}fromtsconfig.jsoncompile (there is no test runner anymore; files preserved on disk for future Vitest wiring).ESLint knock-on:
eslint@^7.32.0devDep. CRA was hoisting a compatible version transitively; removing react-scripts let an older eslint@5 bubble up, which broke config parsing.react-appentry from.eslintrcextends.src/serviceWorker.tspath fromignorePatterns.Commit 2 — Fix 55 pre-existing lint errors unmasked by removing
react-apppresetThe CRA preset had been silencing real errors. Cleaned up in-place rather than deferring:
@typescript-eslint/no-unused-vars: set{ args: "none" }in the rule config. All were unused callback params (mostlycreateStyles((theme) => ...)and event handlers) required by type signatures. The project-level policy is now to not enforce unused function arguments; genuine unused variables and imports are still checked.@typescript-eslint/no-use-before-define: reorder the five helper function definitions so each precedes its call site. Affected:createLayerStyles,handleClose,scrollToTop,clearFiltersBtnClick,excludeUTFtext.jsx-a11y/anchor-is-valid: five MUI<Link>usages that are semantically buttons now usecomponent="button"and drop the dummyhref="#"pluse.preventDefault()plumbing. The sixth already usescomponent={Button}. All six also get aneslint-disable-next-linecomment with an explanation, because the rule inspects JSX element names and doesn't analyze MUI'scomponentprop — the markup is correct, the rule is a false positive.*.test.tsxfiles: add the same glob to.eslintrcignorePatternsso ESLint skips what the tsconfig is already skipping.yarn lintis now clean (0 errors, 0 warnings).What to review
ReadMore), "Clear filters" inline link (FiltersWarning), "Show/Hide world map" toggle (WorldRegionMap), "View image" link (EndoImageModal), neighborhood-list popover trigger (LocationLink). Visual styling should be unchanged; behavior should be unchanged.yarn startruns withoutNODE_OPTIONS=--openssl-legacy-provider.yarn buildemits tobuild/(unchanged output dir) so Netlify's defaultpublishbehavior still works.Verification done locally
yarn build— passes (tsc--noEmit+ Vite build, ~5s vs ~33s with CRA).yarn start— Vite dev server ready in ~125ms, HTTP 200 onlocalhost:3000.yarn lint— 0 errors, 0 warnings.Known follow-ups (out of scope)
@mapbox/mapbox-gl-geocoder(viareact-map-gl-geocoder) imports Node'seventsmodule. Vite externalizes it for the browser, which shows up as a build-time warning. If the search box breaks at runtime, the fix is to swapreact-map-gl-geocoderfor a directmapbox-gl-geocoderintegration — planned for phase 5.React.lazy,manualChunksfor Mapbox/material-table) but are not part of a build-system swap.@testing-library/*,jest-canvas-mock,jest-environment-jsdom-sixteen,@types/jestare now unused. Left in place to minimize this PR's diff; should be removed (or replaced with Vitest) when a test strategy is decided.What's next
createRoot, strict-mode audit).material-tableandreact-swipeable-views.react-map-gl-geocoder.