build: migrate from create-react-app (craco) to Vite - #157
Merged
Merged
Conversation
Prepares for the Vite migration (Vite only applies JSX transform to .jsx by default). Pure git mv, no content changes. Also sets ESLint root: true and ignores build/out output directories.
- Replace react-scripts/craco with vite + @vitejs/plugin-react; drop webpack,
web-vitals and the CRA babel plugin.
- Move index.html to the project root and point it at src/index.jsx.
- vite.config.mjs: `@` alias, dev server pinned to port 3000 (main.js), outDir
kept as build/ (electron-serve, forge, deploy scripts and CI), and a small
plugin that writes version.txt into the resolved outDir (replaces the craco
VersionPlugin).
- SVG components: `import { ReactComponent as X }` -> `import X from '...svg?react'`
via vite-plugin-svgr; plain .svg imports keep resolving to URLs.
- Rename the remaining 5 JSX-containing .js files to .jsx.
- Tests: jest -> vitest (jsdom, globals, jest-dom/vitest). Fake timers are now
enabled per test so Vitest's own timeout timer is not faked; alias
`globalThis.jest = vi` so testing-library's waitFor auto-advances fake timers.
Fix a stale class assertion (bg-green-500 -> bg-status-success-bg).
- Storybook: @storybook/react-webpack5 + preset-create-react-app ->
@storybook/react-vite (alias/svgr/PostCSS inherited from vite.config.mjs).
- Add postcss.config.js (tailwind + autoprefixer).
Bump the Windows publish workflow to Node 22 and declare engines.node so local installs on older Node fail early.
- Use jsdom 27 so the test environment matches the declared Node >=20.19 range (jsdom 30 requires Node 22.22+/24.15+). - Make build:staticFiles an alias of build instead of a duplicate command. - Narrow the `jest` global shim in setupTests to the advanceTimersByTime facade testing-library needs. - Drop the redundant jest-dom import and the no-op runOnlyPendingTimers call in toaster.test.jsx. - Correct the Storybook config comment about PostCSS discovery.
Vite inlines small SVGs as data: URIs containing single quotes; an unquoted CSS url() rejects those as bad-url tokens, so production builds lost the toast status/close icons. Wrap the interpolation in double quotes (Vite's SVG encoding never emits double quotes).
- lint: pass --ext .js,.jsx,.mjs so the renamed .jsx files are actually linted (ESLint 8 eslintrc mode only picks up .js by default). - version plugin: resolve outDir against config.root and write from writeBundle so nothing is emitted when the build fails. - CI: pin Node 22 via setup-node in the three ubuntu workflows instead of relying on the runner default (Vite 7 needs Node 20.19+/22.12+). - Regenerate package-lock.json so its root engines match package.json.
- Remove unused dependencies core-js, i and @heroicons/react (nothing imports them; forge would ship them in every installer). - Remove src/__mocks__ (only referenced by the deleted craco Jest config) and the CRA sample manifest.json plus its index.html link. - Remove build:staticFiles (pure alias of build); build:windows calls npm run build directly. - Correct the fake-timer comment in toaster.test.jsx: Vitest's own test timeout runs on real timers either way; per-test arming exists to give each test a fresh fake clock.
Nothing ran npm test in CI before; the stale assertion on main proved regressions could land silently.
- toast.jsx: keep the dismiss setTimeout in a ref and clear it in the unmount cleanup so it cannot fire against the store after removal. - setupTests: stub the whole vi object as the jest global; the narrow facade let ESLint-accepted jest.* calls explode at runtime. - Replace the per-file eslint-env jest directive with a vitest globals override in .eslintrc.js; drop the now-redundant clearAllTimers in the toaster suite.
jamliaoo
marked this pull request as ready for review
September 13, 2026 09:37
wendyyuchensun
approved these changes
Sep 14, 2026
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
移除 AsciiMath 之後,專案不再需要綁定 create-react-app 的 webpack 特殊處理,因此把建置工具鏈換成 Vite 7。對外行為與產物結構維持不變:
build/(含index.html、version.txt、locales/、access8math-web-template.zip),main.js(electron-serve)、Forge、deploy script 與 GitHub workflows 都不需要改。strictPort),npm run start:desktop流程不變。npm run build/build:staticFiles/build:windows/build:macOS/test/lint/format:check/storybook指令名稱不變。Changes
refactor: rename JSX-containing .js files to .jsx— 37 個含 JSX 的檔案純git mv(Vite 預設只對.jsx做 JSX 轉換)。ESLint 加root: true並忽略build/、out/。build: migrate from create-react-app/craco to Vitereact-scripts、@craco/craco、webpack、web-vitals、@storybook/preset-create-react-app、@storybook/react-webpack5;新增vite、@vitejs/plugin-react、vite-plugin-svgr、vitest、jsdom、@storybook/react-vite、autoprefixer。index.html移到根目錄;vite.config.mjs提供@alias、port 3000、outDir: build、以及寫version.txt的小 plugin(取代 cracoVersionPlugin;寫入實際 resolvedoutDir,所以storybook build只會寫到storybook-static/)。import { ReactComponent as X } from 'a.svg'→import X from 'a.svg?react'(172 處,機械替換);純 URL 用法(toast 的mask-image)不變。beforeEach啟用(模組頂層呼叫會把 vitest 自己的逾時計時器也假造掉),globalThis.jest = vi讓 testing-library 的waitFor能自動推進假時鐘;修正一個過時的 class 斷言(bg-green-500→bg-status-success-bg,該測試在 main 上原本就失敗)。@storybook/react-vite,alias/svgr/PostCSS 從vite.config.mjs繼承。ci: require Node >=20.19 for Vite 7— Windows publish workflow 改 Node 22,package.json加engines(最後一個 commit 把範圍對齊為^20.19.0 || ^22.12.0 || >=24.0.0,與 Vite 7、jsdom 27 一致)。docs: bump Node requirement to 20.19 in READMEbuild: address review findings for the Vite migration— jsdom 27(engines 與 Node ≥20.19 一致)、build:staticFilesalias、jest全域只暴露advanceTimersByTimefacade、測試檔清理。為什麼把
.js改成.jsxVite 以 esbuild 轉譯,預設只對
.jsx/.tsx啟用 JSX;含 JSX 的.js會在載入時報語法錯誤。CRA 底層是 Babel 會把所有.js當 JSX 解析,所以過去不需區分。改副檔名是 Vite 官方建議的遷移方式。雖然可以用 esbuild loader 加自訂 plugin 讓
.js走 JSX 轉換,但所有.js都會變慢、屬社群 workaround 易壞,且 ESLint 等工具依副檔名判斷語法,.js裝 JSX 正是先前 lint 漏掉 36 個檔案的根源,因此不採用。只改內容含 JSX 的 37 個檔案,全部
git mv零內容變更,blame 歷史可延續;純邏輯檔(store.js、i18n.js等)維持.js。Verification
npm run build:產物齊全,build/version.txt正常;vite preview於瀏覽器手動驗證編輯器、svgr 圖示、i18n(zh-TW)、MathJax 渲染,console 無錯誤。npm test:8/8 通過。npx storybook build:成功。npm run lint、npm run format:check:通過。npm run start:確認綁定http://localhost:3000/。npx electron-forge package:成功。Notes
@vitejs/plugin-react5 + vitest 3。package.json的browserslist/homepage為 CRA 時代殘留,這次刻意不動;Vite 預設 target 為 baseline-widely-available,未加@vitejs/plugin-legacy。build:staticFiles已移除(build:windows直接呼叫npm run build)。sourcemap 部分已確認非行為變更:main 上 Windows 產物帶 .map 只是GENERATE_SOURCEMAP=false在 cmd 下無法解析的意外,macOS 產物本來就沒有 sourcemap。app://下會持續重試載入不存在的locales/*/translation.json(i18next 預設命名空間),以同一探針測試 main 上的 CRA 打包版也是相同行為。之後另外開 issue 處理。