Skip to content

Commit eb794ea

Browse files
committed
Migrate from Create React App to Vite
This migration modernizes the build tooling from the deprecated Create React App to Vite, bringing significant performance improvements and better developer experience. Key changes: - Replace react-scripts with Vite and related plugins - Update build configuration with vite.config.ts - Move index.html to project root (Vite requirement) - Update tsconfig.json for Vite's bundler mode resolution - Upgrade TypeScript from v4 to v5 - Upgrade styled-components from v5 to v6 for React 18 compatibility - Replace Jest with Vitest for testing - Update all test files to use Vitest API (vi.mock, vi.fn) - Update package.json scripts (dev, start, build, preview, test) - Update Makefile to use new Vite commands - Add type declarations for lz-string module - Remove CRA-specific files (react-app-env.d.ts) - Add vite-env.d.ts for Vite client types - Update .gitignore for TypeScript build artifacts Build performance improvements: - Development server starts instantly with Vite's esbuild - Hot Module Replacement (HMR) is significantly faster - Production builds use Rollup for optimized bundles All tests passing (61 tests across 5 test files).
1 parent 657db2b commit eb794ea

15 files changed

+2531
-8780
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@
2121
npm-debug.log*
2222
yarn-debug.log*
2323
yarn-error.log*
24+
25+
# TypeScript
26+
*.tsbuildinfo

Makefile

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,52 @@
1-
.PHONY: help install test test-coverage build lint lint-fix clean ci start
1+
.PHONY: help install dev start test test-ui test-coverage build preview lint lint-fix clean ci
22

33
# Default target
44
help:
55
@echo "Available targets:"
66
@echo " make install - Install dependencies"
7-
@echo " make test - Run tests"
7+
@echo " make dev - Start Vite development server"
8+
@echo " make start - Start Vite development server (alias for dev)"
9+
@echo " make test - Run tests with Vitest"
10+
@echo " make test-ui - Run tests with Vitest UI"
811
@echo " make test-coverage - Run tests with coverage"
912
@echo " make build - Build production bundle"
13+
@echo " make preview - Preview production build locally"
1014
@echo " make lint - Check code formatting"
1115
@echo " make lint-fix - Fix code formatting"
1216
@echo " make clean - Clean build artifacts"
1317
@echo " make ci - Run all CI checks (test + build + lint)"
14-
@echo " make start - Start development server"
1518

1619
# Install dependencies
1720
install:
1821
yarn install --frozen-lockfile
1922

20-
# Run tests without coverage
23+
# Start Vite development server
24+
dev:
25+
yarn dev
26+
27+
# Alias for dev
28+
start: dev
29+
30+
# Run tests with Vitest
2131
test:
22-
yarn test --watchAll=false
32+
yarn test --run
33+
34+
# Run tests with Vitest UI
35+
test-ui:
36+
yarn test:ui
2337

2438
# Run tests with coverage
2539
test-coverage:
26-
yarn test --watchAll=false --coverage
40+
yarn test:coverage --run
2741

2842
# Build production bundle
2943
build:
3044
yarn build
3145

46+
# Preview production build
47+
preview:
48+
yarn preview
49+
3250
# Check code formatting with Prettier
3351
lint:
3452
yarn prettier --check "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}"
@@ -45,7 +63,3 @@ clean:
4563

4664
# Run all CI checks
4765
ci: test-coverage build lint
48-
49-
# Start development server
50-
start:
51-
yarn start

index.html

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<link rel="icon" href="/favicon.ico" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
<meta name="theme-color" content="#000000" />
8+
<meta
9+
name="description"
10+
content="A math LaTeX sandbox."
11+
/>
12+
<link rel="apple-touch-icon" href="/logo192.png" />
13+
<link rel="manifest" href="/manifest.json" />
14+
<title>MathJojo</title>
15+
</head>
16+
<body>
17+
<noscript>You need to enable JavaScript to run this app.</noscript>
18+
<div id="root"></div>
19+
<script type="module" src="/src/index.tsx"></script>
20+
</body>
21+
</html>

package.json

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,24 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6-
"@testing-library/jest-dom": "^5.14.1",
7-
"@testing-library/react": "^13.0.0",
8-
"@testing-library/user-event": "^13.2.1",
9-
"@types/jest": "^27.0.1",
10-
"@types/katex": "^0.14.0",
11-
"@types/lz-string": "^1.3.34",
12-
"@types/node": "^16.7.13",
13-
"@types/react": "^18.0.0",
14-
"@types/react-dom": "^18.0.0",
15-
"@types/shallowequal": "^1.1.1",
16-
"@types/styled-components": "^5.1.26",
17-
"husky": "^8.0.1",
186
"katex": "^0.16.2",
197
"latex.css": "^1.8.0",
20-
"lint-staged": "^13.0.3",
218
"lz-string": "^1.4.4",
22-
"prettier": "^2.7.1",
239
"react": "^18.2.0",
2410
"react-dom": "^18.2.0",
25-
"react-scripts": "5.0.1",
11+
"react-is": "^19.2.0",
2612
"shallowequal": "^1.1.0",
27-
"styled-components": "^5.3.6",
28-
"typescript": "^4.4.2",
13+
"styled-components": "^6",
2914
"web-vitals": "^2.1.0"
3015
},
3116
"scripts": {
32-
"start": "react-scripts start",
33-
"build": "react-scripts build",
34-
"test": "react-scripts test",
35-
"eject": "react-scripts eject"
36-
},
37-
"eslintConfig": {
38-
"extends": [
39-
"react-app",
40-
"react-app/jest"
41-
]
17+
"dev": "vite",
18+
"start": "vite",
19+
"build": "tsc -b && vite build",
20+
"preview": "vite preview",
21+
"test": "vitest",
22+
"test:ui": "vitest --ui",
23+
"test:coverage": "vitest --coverage"
4224
},
4325
"browserslist": {
4426
"production": [
@@ -61,5 +43,29 @@
6143
"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
6244
"prettier --write"
6345
]
46+
},
47+
"devDependencies": {
48+
"@testing-library/dom": "^10.4.1",
49+
"@testing-library/jest-dom": "^6",
50+
"@testing-library/react": "^14",
51+
"@testing-library/user-event": "^14",
52+
"@types/jest": "^30.0.0",
53+
"@types/katex": "^0.16.7",
54+
"@types/node": "^24.10.1",
55+
"@types/react": "^18",
56+
"@types/react-dom": "^18",
57+
"@types/shallowequal": "^1.1.5",
58+
"@types/styled-components": "^5.1.36",
59+
"@vitejs/plugin-react": "^5.1.1",
60+
"@vitest/coverage-v8": "^4.0.13",
61+
"@vitest/ui": "^4.0.13",
62+
"husky": "^8.0.1",
63+
"jsdom": "^27.2.0",
64+
"lint-staged": "^13.0.3",
65+
"prettier": "^2.7.1",
66+
"typescript": "^5.9.3",
67+
"vite": "^7.2.4",
68+
"vite-tsconfig-paths": "^5.1.4",
69+
"vitest": "^4.0.13"
6470
}
6571
}

src/App.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ describe("URL parameter handling", () => {
8686
});
8787

8888
afterEach(() => {
89-
window.location = originalLocation;
89+
window.location = originalLocation as any;
9090
});
9191

9292
test("loads value from URL parameter", () => {

src/cheatsheet.test.tsx

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,37 @@
11
import React from "react";
22
import { render, screen, fireEvent } from "@testing-library/react";
3+
import { vi } from "vitest";
34
import CheatSheet from "./cheatsheet";
45

56
// Mock the QuickInsert component
6-
jest.mock("./quickinsert", () => {
7-
return function QuickInsert(props: any) {
8-
return (
9-
<a
10-
href="#insert"
11-
onClick={(e) => {
12-
e.preventDefault();
13-
props.onClick();
14-
}}
15-
data-testid={`quick-insert-${props.source}`}
16-
>
17-
{props.source}
18-
</a>
19-
);
7+
vi.mock("./quickinsert", () => {
8+
return {
9+
default: function QuickInsert(props: any) {
10+
return (
11+
<a
12+
href="#insert"
13+
onClick={(e) => {
14+
e.preventDefault();
15+
props.onClick();
16+
}}
17+
data-testid={`quick-insert-${props.source}`}
18+
>
19+
{props.source}
20+
</a>
21+
);
22+
},
2023
};
2124
});
2225

2326
describe("CheatSheet", () => {
2427
const defaultProps = {
2528
displayCheatSheet: false,
26-
insertSource: jest.fn(),
27-
toggleCheatSheet: jest.fn(),
29+
insertSource: vi.fn(),
30+
toggleCheatSheet: vi.fn(),
2831
};
2932

3033
beforeEach(() => {
31-
jest.clearAllMocks();
34+
vi.clearAllMocks();
3235
});
3336

3437
describe("when cheatsheet is hidden", () => {

src/katex.test.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import React from "react";
22
import { render } from "@testing-library/react";
3+
import { vi } from "vitest";
34
import Katex from "./katex";
45
import katex from "katex";
56

67
// Mock katex
7-
jest.mock("katex", () => ({
8-
render: jest.fn(),
8+
vi.mock("katex", () => ({
9+
default: {
10+
render: vi.fn(),
11+
},
912
}));
1013

1114
describe("Katex", () => {
1215
beforeEach(() => {
13-
jest.clearAllMocks();
16+
vi.clearAllMocks();
1417
});
1518

1619
test("renders span element", () => {
@@ -92,7 +95,7 @@ describe("Katex", () => {
9295
});
9396

9497
test("calls beforeRender callback if provided", () => {
95-
const beforeRender = jest.fn();
98+
const beforeRender = vi.fn();
9699

97100
render(<Katex source="x^2" beforeRender={beforeRender} />);
98101

@@ -102,11 +105,11 @@ describe("Katex", () => {
102105
test("calls beforeRender before katex.render", () => {
103106
const callOrder: string[] = [];
104107

105-
const beforeRender = jest.fn(() => {
108+
const beforeRender = vi.fn(() => {
106109
callOrder.push("beforeRender");
107110
});
108111

109-
(katex.render as jest.Mock).mockImplementation(() => {
112+
vi.mocked(katex.render).mockImplementation(() => {
110113
callOrder.push("katex.render");
111114
});
112115

src/lz-string.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
declare module 'lz-string' {
2+
export function compress(input: string): string;
3+
export function decompress(compressed: string): string | null;
4+
export function compressToBase64(input: string): string;
5+
export function decompressFromBase64(compressed: string): string | null;
6+
export function compressToUTF16(input: string): string;
7+
export function decompressFromUTF16(compressed: string): string | null;
8+
export function compressToUint8Array(input: string): Uint8Array;
9+
export function decompressFromUint8Array(compressed: Uint8Array): string | null;
10+
export function compressToEncodedURIComponent(input: string): string;
11+
export function decompressFromEncodedURIComponent(compressed: string): string | null;
12+
}

src/quickinsert.test.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import React from "react";
22
import { render, screen, fireEvent } from "@testing-library/react";
3+
import { vi } from "vitest";
34
import QuickInsert from "./quickinsert";
45

56
// Mock the Katex component
6-
jest.mock("./katex", () => {
7-
return function Katex(props: any) {
8-
return <span data-testid="katex-mock">{props.source}</span>;
7+
vi.mock("./katex", () => {
8+
return {
9+
default: function Katex(props: any) {
10+
return <span data-testid="katex-mock">{props.source}</span>;
11+
},
912
};
1013
});
1114

1215
describe("QuickInsert", () => {
13-
const mockOnClick = jest.fn();
16+
const mockOnClick = vi.fn();
1417

1518
beforeEach(() => {
16-
jest.clearAllMocks();
19+
vi.clearAllMocks();
1720
});
1821

1922
test("renders a link", () => {
@@ -56,7 +59,7 @@ describe("QuickInsert", () => {
5659
const event = new MouseEvent("click", { bubbles: true, cancelable: true });
5760

5861
Object.defineProperty(event, "preventDefault", {
59-
value: jest.fn(),
62+
value: vi.fn(),
6063
writable: true,
6164
});
6265

src/react-app-env.d.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)