Skip to content

Commit 53bb47d

Browse files
committed
feat: generated-routes
1 parent 482ecd3 commit 53bb47d

27 files changed

+352
-91
lines changed

bun.lockb

632 Bytes
Binary file not shown.

package.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@
1616
"react-ga": "^3.3.1",
1717
"react-hook-form": "^7.53.0",
1818
"react-intersection-observer": "^9.13.0",
19-
"react-router-dom": "^6.26.1",
19+
"react-router": "^7.1.3",
2020
"react-star-ratings": "^2.3.0",
2121
"recoil": "^0.7.7"
2222
},
23+
"type": "module",
2324
"scripts": {
25+
"predev": "bun gen:routes",
2426
"dev": "vite",
2527
"build": "vite build",
2628
"lint": "eslint",
27-
"compile": "tsc"
29+
"compile": "tsc",
30+
"gen:routes": "bun scripts/generateRoutes.ts"
2831
},
2932
"devDependencies": {
3033
"@eslint/js": "^9.9.1",

scripts/generateRoutes.ts

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
4+
const PAGES_DIR = path.join(process.cwd(), 'src/pages');
5+
6+
const fileNameToPath = (fileName: string) => {
7+
if (fileName === 'Main.tsx') return '/';
8+
9+
const specialCases: Record<string, string> = {
10+
'NotFound.tsx': '/404',
11+
'BadGateway.tsx': '/502',
12+
'NoticeDetail.tsx': '/notice/detail',
13+
};
14+
15+
if (fileName in specialCases) {
16+
return specialCases[fileName];
17+
}
18+
19+
return fileName
20+
.replace(/\.(tsx|jsx)$/, '')
21+
.replace(/([A-Z])/g, (_match, letter, offset) => {
22+
return offset === 0 ? letter.toLowerCase() : letter.toLowerCase();
23+
})
24+
.toLowerCase()
25+
.replace(/\[(.+)\]/, ':$1');
26+
};
27+
28+
const generateRoutes = () => {
29+
const routes: { path: string; component: string }[] = [];
30+
31+
const scanDir = (dir: string) => {
32+
fs.readdirSync(dir).forEach((file) => {
33+
const filePath = path.join(dir, file);
34+
35+
if (fs.statSync(filePath).isDirectory()) {
36+
scanDir(filePath);
37+
38+
return;
39+
}
40+
41+
if (!file.match(/\.(tsx|jsx)$/)) return;
42+
43+
const relativePath = path.relative(PAGES_DIR, filePath);
44+
const routePath = fileNameToPath(relativePath);
45+
46+
routes.push({
47+
path: routePath.startsWith('/') ? routePath : `/${routePath}`,
48+
component: relativePath.replace(/\.tsx$/, ''),
49+
});
50+
});
51+
};
52+
53+
scanDir(PAGES_DIR);
54+
55+
routes.push({
56+
path: '/*',
57+
component: 'NotFound',
58+
});
59+
60+
const GENERATED_DIR = path.join(process.cwd(), 'src/__generated__');
61+
62+
if (fs.existsSync(GENERATED_DIR)) {
63+
fs.rmSync(GENERATED_DIR, { recursive: true });
64+
}
65+
fs.mkdirSync(GENERATED_DIR, { recursive: true });
66+
67+
const typesContent = `/* generated file by scripts/generateRoutes.ts -- do not edit */
68+
/* authored by tooooo1 */
69+
70+
/* tslint:disable */
71+
/* eslint-disable */
72+
73+
type BaseRoutePath = ${routes.map((route) => `'${route.path}'`).join(' | ')};
74+
export type RoutePath = BaseRoutePath | \`\${BaseRoutePath}?\${string}\`;
75+
export type RoutePathWithQuery<T extends string> = \`\${BaseRoutePath}?\${T}\`;
76+
`;
77+
78+
const routesContent = `/* generated file by scripts/generateRoutes.ts -- do not edit */
79+
/* authored by tooooo1 */
80+
81+
/* tslint:disable */
82+
/* eslint-disable */
83+
import { createBrowserRouter, type NavigateOptions } from 'react-router';
84+
import { RoutePath } from './routes.types';
85+
86+
import Layout from '../components/Etc/Layout';
87+
88+
export const router = createBrowserRouter([
89+
{
90+
element: <Layout />,
91+
children: [
92+
${routes
93+
.map(
94+
(route) => `{
95+
path: '${route.path}' as RoutePath,
96+
lazy: async () => {
97+
const { default: Component } = await import('../pages/${route.component}');
98+
99+
return { Component };
100+
}
101+
}`,
102+
)
103+
.join(',\n')}
104+
]
105+
}
106+
]);
107+
108+
declare module 'react-router' {
109+
export function useNavigate(): (path: RoutePath, options?: NavigateOptions) => void;
110+
}
111+
`;
112+
113+
fs.writeFileSync(path.join(GENERATED_DIR, 'routes.types.ts'), typesContent);
114+
fs.writeFileSync(path.join(GENERATED_DIR, 'routes.generated.tsx'), routesContent.trim());
115+
116+
console.log('\n🚀 Routes generated successfully!\n');
117+
console.log('📍 Available routes:');
118+
routes.forEach((route) => {
119+
console.log(` ${route.path.padEnd(20)} 🔗`);
120+
});
121+
console.log('\n✨ Route generation completed!\n');
122+
};
123+
124+
generateRoutes();

src/App.tsx

+4-45
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,11 @@
11
import { isLoginState } from 'app/recoilStore';
2-
import Layout from 'components/Etc/Layout';
3-
import {
4-
BadGateway,
5-
BanReason,
6-
EmailSignUp,
7-
Exit,
8-
HistoryTest,
9-
IdSearch,
10-
LectureInfo,
11-
Login,
12-
Main,
13-
MyInfo,
14-
MyPosting,
15-
NotFound,
16-
Notice,
17-
NoticeDetail,
18-
PwSearch,
19-
ResetPassword,
20-
Search,
21-
SignUp,
22-
} from 'pages';
23-
import { Navigate, Route, Routes } from 'react-router-dom';
2+
import { RouterProvider } from 'react-router';
243
import type { MutableSnapshot } from 'recoil';
254
import { RecoilRoot } from 'recoil';
265
import { getAccessToken } from 'utils/tokenManeger';
276

7+
import { router } from './__generated__/routes.generated';
8+
289
const App = () => {
2910
const initialState = ({ set }: MutableSnapshot) => {
3011
const isLogin = !!getAccessToken();
@@ -33,29 +14,7 @@ const App = () => {
3314

3415
return (
3516
<RecoilRoot initializeState={initialState}>
36-
<Layout>
37-
<Routes>
38-
<Route path="/" element={<Main />} />
39-
<Route path="/notice" element={<Notice />} />
40-
<Route path="/login" element={<Login />} />
41-
<Route path="/pwsearch" element={<PwSearch />} />
42-
<Route path="/idsearch" element={<IdSearch />} />
43-
<Route path="/signup" element={<SignUp />} />
44-
<Route path="/myinformation" element={<MyInfo />} />
45-
<Route path="/myposting" element={<MyPosting />} />
46-
<Route path="/lectureinfo" element={<LectureInfo />} />
47-
<Route path="/search" element={<Search />} />
48-
<Route path="/emailsignup" element={<EmailSignUp />} />
49-
<Route path="/notice/detail" element={<NoticeDetail />} />
50-
<Route path="/historytest" element={<HistoryTest />} />
51-
<Route path="/resetpassword" element={<ResetPassword />} />
52-
<Route path="/exit" element={<Exit />} />
53-
<Route path="/banreason" element={<BanReason />} />
54-
<Route path="/404" element={<NotFound />} />
55-
<Route path="/502" element={<BadGateway />} />
56-
<Route path="/*" element={<Navigate replace to="/404" />} />
57-
</Routes>
58-
</Layout>
17+
<RouterProvider router={router} />
5918
</RecoilRoot>
6019
);
6120
};

src/__generated__/routes.generated.tsx

+173
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/__generated__/routes.types.ts

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)