Skip to content

Commit 000ee4d

Browse files
committed
feat: 基于 unplugin
1 parent a02795c commit 000ee4d

File tree

9 files changed

+900
-0
lines changed

9 files changed

+900
-0
lines changed

src/const.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const pkgName = process.env.PKG_NAME;
2+
export const pkgVersion = process.env.PKG_VERSION;
3+
export const officialLink = 'https://github.com/FrontEndDev-org/unplugin-react-pages';

src/core/FSRouter.ts

+217
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import nodePath from 'node:path';
2+
import { indentLine, jsxLikeStringify } from '../helpers';
3+
import type { FSNode, FSTree } from './FSTree';
4+
5+
class PageRoute {
6+
parent?: LayoutRoute;
7+
constructor(readonly fsNode: FSNode) {}
8+
9+
get id() {
10+
return this.fsNode.id;
11+
}
12+
13+
get path() {
14+
const paths = [this.fsNode.path];
15+
const find = (fsNode?: FSNode) => {
16+
if (!fsNode)
17+
return;
18+
19+
// 有分组 depth = 1 结束
20+
if (fsNode.group && fsNode.depth === 1) {
21+
paths.unshift(fsNode.path);
22+
return;
23+
}
24+
25+
// 直到包含路由的节点为止
26+
if (!fsNode.group && fsNode.layoutFileName) {
27+
return;
28+
}
29+
30+
paths.unshift(fsNode.path);
31+
find(fsNode.parent);
32+
};
33+
34+
find(this.fsNode.parent);
35+
return paths.length === 0 || (paths.length === 1 && paths.at(0) === '') ? '' : nodePath.join(...paths);
36+
}
37+
38+
render(indent = 0, tabSize = 4) {
39+
const indent_1 = indent * tabSize;
40+
const indent_2 = (indent + 1) * tabSize;
41+
const lines = [
42+
//
43+
indentLine('{', indent_1),
44+
];
45+
46+
// path
47+
lines.push(indentLine(`path: ${JSON.stringify(this.path)},`, indent_2));
48+
49+
// element
50+
const pageName = FSRouter.makePageComponentName(this.fsNode.id);
51+
const element = jsxLikeStringify({
52+
tag: 'Suspense',
53+
props: {},
54+
children: [{ tag: pageName }],
55+
});
56+
lines.push(indentLine(`element: ${element},`, indent_2));
57+
lines.push(indentLine('},', indent_1));
58+
59+
return lines;
60+
}
61+
}
62+
63+
class LayoutRoute {
64+
parent?: LayoutRoute;
65+
children: (LayoutRoute | PageRoute)[] = [];
66+
constructor(readonly fsNode: FSNode) {}
67+
68+
get id() {
69+
return this.fsNode.id;
70+
}
71+
72+
render(indent = 0, tabSize = 4) {
73+
const indent_1 = indent * tabSize;
74+
const indent_2 = (indent + 1) * tabSize;
75+
const lines = [
76+
//
77+
indentLine('{', indent_1),
78+
];
79+
80+
// element
81+
const layoutName = FSRouter.makeLayoutComponentName(this.fsNode.id);
82+
const element = jsxLikeStringify({
83+
tag: 'Suspense',
84+
props: {},
85+
children: [{ tag: layoutName }],
86+
});
87+
lines.push(indentLine(`element: ${element},`, indent_2));
88+
89+
if (this.children.length) {
90+
lines.push(indentLine('children: [', indent_2));
91+
lines.push(...this.children.flatMap(child => child.render(indent + 2, tabSize)));
92+
lines.push(indentLine('],', indent_2));
93+
}
94+
95+
lines.push(indentLine('},', indent_1));
96+
return lines;
97+
}
98+
}
99+
100+
export class FSRouter {
101+
constructor(readonly fsTree: FSTree) {}
102+
103+
pageRoutes = new Map<number, PageRoute>();
104+
addPageNode(fsNode: FSNode) {
105+
this.pageRoutes.set(fsNode.id, new PageRoute(fsNode));
106+
}
107+
108+
layoutRoutes = new Map<number, LayoutRoute>();
109+
addLayoutNode(fsNode: FSNode) {
110+
this.layoutRoutes.set(fsNode.id, new LayoutRoute(fsNode));
111+
}
112+
113+
build() {
114+
this.pageRoutes.forEach((pageRoute) => {
115+
const find = (node: FSNode) => {
116+
if (node.layoutFileName) {
117+
const layoutRoute = this.layoutRoutes.get(node.id);
118+
119+
if (layoutRoute) {
120+
pageRoute.parent = layoutRoute;
121+
layoutRoute.children.push(pageRoute);
122+
return;
123+
}
124+
}
125+
126+
if (node.parent)
127+
find(node.parent);
128+
};
129+
130+
find(pageRoute.fsNode);
131+
});
132+
133+
this.layoutRoutes.forEach((layoutRoute) => {
134+
const find = (node: FSNode) => {
135+
const parent = node.parent;
136+
if (!parent)
137+
return;
138+
139+
// 独立分组布局根节点
140+
if (node.group && node.depth === 1)
141+
return;
142+
143+
if (parent.layoutFileName) {
144+
const parentRoute = this.layoutRoutes.get(parent.id);
145+
146+
if (parentRoute) {
147+
layoutRoute.parent = parentRoute;
148+
parentRoute.children.push(layoutRoute);
149+
return;
150+
}
151+
}
152+
153+
find(parent);
154+
};
155+
156+
find(layoutRoute.fsNode);
157+
});
158+
}
159+
160+
render(tabSize = 4) {
161+
const importPages = ['// pages'];
162+
const importLayouts = ['// layouts'];
163+
164+
const pageLines: string[] = [];
165+
this.pageRoutes.forEach((route) => {
166+
const importName = FSRouter.makePageComponentName(route.id);
167+
importPages.push(`const ${importName} = lazy(() => import("${route.fsNode.pageFile}"));`);
168+
169+
if (!route.parent) {
170+
pageLines.push(...route.render(0, tabSize));
171+
}
172+
});
173+
174+
const layoutLines: string[] = [];
175+
this.layoutRoutes.forEach((route) => {
176+
const importName = FSRouter.makeLayoutComponentName(route.id);
177+
importLayouts.push(`const ${importName} = lazy(() => import("${route.fsNode.layoutFile}"));`);
178+
179+
if (!route.parent) {
180+
layoutLines.push(...route.render(0, tabSize));
181+
}
182+
});
183+
184+
return [
185+
'/**',
186+
' * generated by pkg-name-for-test@pkg-version-for-test',
187+
' * @ref https://github.com/FrontEndDev-org/unplugin-react-pages',
188+
' * @ref https://react.dev',
189+
' * @ref https://reactrouter.com',
190+
' */',
191+
'',
192+
'import { Suspense, lazy, createElement } from "react";',
193+
'import { Outlet } from "react-router-dom";',
194+
'',
195+
...importPages,
196+
'',
197+
...importLayouts,
198+
'',
199+
'export const routes = [',
200+
...pageLines,
201+
...layoutLines,
202+
'];',
203+
'',
204+
].join('\n');
205+
}
206+
207+
// TODO
208+
update() {}
209+
210+
static makeLayoutComponentName(id: number) {
211+
return `Layout_${id}`;
212+
}
213+
214+
static makePageComponentName(id: number) {
215+
return `Page_${id}`;
216+
}
217+
}

0 commit comments

Comments
 (0)