Skip to content

Commit 0ef9f88

Browse files
author
Training
committed
Initial commit
0 parents  commit 0ef9f88

File tree

7 files changed

+750
-0
lines changed

7 files changed

+750
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Project exclude paths
2+
/node_modules/

index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import {watchPages} from "./libs/render-router.js"
2+
watchPages();

libs/cache/routes.js

Whitespace-only changes.

libs/render-router.js

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
import {compileScript, parse} from "vue/compiler-sfc"
2+
import path from "path"
3+
import fs from "fs"
4+
import watch from "watch"
5+
const __dirname = path.resolve();
6+
const pages = path.join(__dirname, "/src/pages");
7+
const files = fs.readdirSync(pages);
8+
const routeDir = path.join(__dirname,"src/router");
9+
const ignoreDirs = /(components|utils)/;
10+
/**
11+
* @desc 生成GUID
12+
* @param format 格式
13+
* @return {string}
14+
*/
15+
export function GUID(format = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") {
16+
let char = ['A', 'B', 'C', 'D', 'E', 'F', 1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
17+
let len = 0;
18+
for (let i in format)
19+
if (format[i] === 'x') len += 1;
20+
let random = () => {
21+
let i = parseInt(Math.random() * 15);
22+
23+
return char[i];
24+
};
25+
return format.replace(/x/g, res => random())
26+
}
27+
28+
/**
29+
* @desc 递归遍历文件夹及文件 并做相应的处理
30+
* 规则:
31+
*
32+
* 1. 页面局部组件 , 若放在单文件夹中 , 需放在在目录下的components文件夹中
33+
*
34+
* @param fls
35+
* @param p
36+
*/
37+
let routes = []; // 路由列表
38+
let routesInfo = {}; // 路由详情
39+
export function loopDir(fls = [],p){
40+
if (fls.length === 0)return;
41+
fls.forEach(item=>{
42+
const fullPath = path.join(p,item); // 完整路径
43+
const isDir = fs.statSync(fullPath).isDirectory(); // 是否为文件夹类型
44+
if (isDir && !ignoreDirs.test(item.toLowerCase())){
45+
const child = fs.readdirSync(fullPath) || [];
46+
loopDir(child,fullPath);
47+
}else {
48+
// 获取后缀
49+
const extname = path.extname(fullPath);
50+
// 只处理vue文件
51+
if (extname === ".vue"){
52+
let routeStr = analysisRouteConfig(fullPath)
53+
routes.push(routeStr)
54+
routesInfo[fullPath] = {
55+
data:routeStr,
56+
index:routes.length - 1,
57+
58+
}
59+
}
60+
}
61+
})
62+
}
63+
64+
/**
65+
* @desc 解析vue文件
66+
* @param filepath 文件路径
67+
*/
68+
export function analysisVue(filepath) {
69+
if (!filepath)return ;
70+
const file = fs.readFileSync(filepath,{encoding:"utf-8"})
71+
const parseVue = parse(file);
72+
let {content,setup} = compileScript(parseVue.descriptor,{
73+
filename:path.basename(filepath),
74+
id:GUID(),
75+
});
76+
// 解析content
77+
let result = (new Function(`${content.replace("export default","return").trim()}`))();
78+
if (setup){
79+
result = result.setup([],{expose:()=>{}});
80+
}
81+
return result._config;
82+
}
83+
84+
/**
85+
* @desc 解析路由配置
86+
* @param filepath 文件路径
87+
*/
88+
export function analysisRouteConfig(filepath){
89+
if (!filepath)return;
90+
let config = analysisVue(filepath);
91+
let abPath = filepath.replace(pages,"/pages").replaceAll("\\","/"); // 相对路径
92+
let routePath = abPath.replace("/pages","").replace(".vue",""); // 路由路径
93+
let routePathArr = routePath.split("/"); // 相对路径转数组
94+
let res = {};
95+
// 没有配置config的情况 or 没有配置route
96+
if (!config || !config.route){
97+
res = {
98+
path:routePath
99+
}
100+
}else if(config.route){
101+
if (config.route.path){ // 有path的情况
102+
res = config.route;
103+
}else if (config.route.name){ // 没有path 有name的情况
104+
routePathArr[routePathArr.length - 1] = encodeURI(config.route.name);
105+
res = Object.assign({
106+
path:routePathArr.join("/")
107+
},config.route);
108+
}else
109+
Object.assign({
110+
path:routePath
111+
},config.route);
112+
}
113+
res["component"] = `$[()=>import('@${abPath}')]$`;
114+
return JSON.stringify(res).replace('"$[', "").replace(']$"', "");
115+
}
116+
117+
/**
118+
* @desc 写入路由
119+
*/
120+
export function writeRouter(){
121+
let config = fs.readFileSync(path.join(__dirname, "libs/template/route.js"),{encoding:"utf-8"});
122+
if (!fs.existsSync(routeDir))
123+
fs.mkdirSync(routeDir);
124+
if (!fs.existsSync(routeDir+"/index.js"))
125+
fs.writeFileSync(routeDir+"/index.js",config,{encoding:"utf-8"});
126+
else {
127+
let index = fs.readFileSync(path.join(routeDir,"index.js"),{encoding:"utf-8"});
128+
if (!/import\s+\w+\s+from\s+"[\w.\/]*config\.js"/.test(index)){
129+
fs.writeFileSync(routeDir+"/index.js","import config from \"./config.js\"\n"+index,{encoding:"utf-8"})
130+
}
131+
}
132+
fs.writeFileSync(routeDir+"/config.js",`export default [\n\t${routes.join(",\n\t")}\n]`,{encoding:"utf-8"});
133+
134+
135+
}
136+
137+
138+
139+
class CURD{
140+
queue = [];
141+
constructor() {
142+
}
143+
/**
144+
* @desc 当文件被更改 , 执行更新操作
145+
* @param filePath
146+
*/
147+
update(filePath) {
148+
let prev = routesInfo[filePath]["data"].replace(/(\n)/g,"");
149+
let cur = analysisRouteConfig(filePath);
150+
let cur_cs = cur.replace(/(\n)/g,"");
151+
const index = routesInfo[filePath]["index"];
152+
if(prev !== cur_cs){
153+
routes[index] = cur;
154+
routesInfo[filePath]["data"] = cur;
155+
writeRouter();
156+
}
157+
}
158+
/**
159+
* @desc 执行删除操作
160+
* @param filePath
161+
* @param cur
162+
*/
163+
delete(filePath,cur){
164+
let keys = Object.keys(routesInfo);
165+
for (let key of keys){
166+
if (key.indexOf(filePath) > -1){
167+
let index = routesInfo[key].index;
168+
routes[index] = null;
169+
this.queue.push(index);
170+
delete routesInfo[key];
171+
}
172+
}
173+
writeRouter();
174+
}
175+
176+
/**
177+
* @desc 执行新增操作
178+
* @param filePath
179+
* @param cur
180+
*/
181+
create(filePath,cur){
182+
const res = this._baseLogic(filePath,cur);
183+
if (!res)return;
184+
res.forEach(item=>{
185+
// 队列长度
186+
const q_len = this.queue.length;
187+
// 判定队列是否为空 , 如果不为空 , 则出队,否则返回数组长度
188+
// 大致意思是 , 如果routes有空值, 则插入,没有控制则push
189+
const index = q_len>0?this.queue.shift():routes.length;
190+
if (routes[index] != null)
191+
loopDir(files,pages);
192+
else if(routesInfo[item]){
193+
// ...
194+
}else{
195+
const routeStr = analysisRouteConfig(item);
196+
routesInfo[item] = {
197+
data: routeStr,
198+
index
199+
}
200+
routes[index] = routeStr;
201+
}
202+
})
203+
204+
writeRouter();
205+
}
206+
207+
/**
208+
* @desc 主要用于判定路径是否是目录,
209+
如果是目录则深度遍历目录下有没有vue文件 ,
210+
如果是vue文件则返回路径
211+
* @param dirPath
212+
* @param cur
213+
* @return {Array|*}
214+
* @private
215+
*/
216+
_dirHasFiles(dirPath,cur){
217+
if (!cur.isDirectory() && path.extname(dirPath) === '.vue')return [dirPath];
218+
let q = [dirPath];
219+
let res = [];
220+
while (q.length){
221+
// 当前文件夹
222+
let c = q.shift();
223+
let f = fs.readdirSync(c);
224+
for (let i of f){
225+
const fullPath = path.join(c,i);
226+
if (fs.statSync(fullPath).isDirectory())q.push(fullPath);
227+
else if(path.extname(fullPath) === '.vue'){
228+
res.push(fullPath);
229+
}
230+
}
231+
}
232+
return res;
233+
}
234+
_baseLogic(filePath,cur){
235+
if(ignoreDirs.test(filePath.toLowerCase()))return null;
236+
const res = this._dirHasFiles(filePath,cur);
237+
if (res.length === 0)return null;
238+
return res;
239+
}
240+
}
241+
242+
/**
243+
* @desc 监听pages目录
244+
*/
245+
export function watchPages(){
246+
let curd = new CURD();
247+
watch.watchTree(pages,{
248+
interval:1,
249+
ignoreDotFiles:true,
250+
ignoreUnreadableDir:true,
251+
ignoreNotPermitted:true,
252+
ignoreDirectoryPattern:/(components|utils)/
253+
},function (f,cur,prev) {
254+
if (typeof f == "object" && prev === null && cur === null) {
255+
renderAll()
256+
// 完成对树的遍历
257+
} else if (prev === null) {
258+
// f 是一个新文件
259+
curd.create(f,cur)
260+
} else if (cur.nlink === 0) {
261+
// f 被移除
262+
curd.delete(f,cur);
263+
} else if (prev != null){
264+
curd.update(f);
265+
}
266+
})
267+
}
268+
// 全部渲染pages
269+
export function renderAll() {
270+
loopDir(files,pages); // 轮询目录 , 生成route配置
271+
writeRouter(); // 文件写入
272+
}

libs/template/route.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import * as VRouter from "vue-router"
2+
const env = import.meta.env.MODE;
3+
import config from "./config.js"
4+
5+
const router = VRouter.createRouter({
6+
routes:config,
7+
history:VRouter.createWebHistory()
8+
});
9+
10+
router.beforeEach((to, from, next)=>{
11+
12+
next();
13+
})
14+
router.afterEach((to)=>{
15+
16+
})
17+
export default router;

0 commit comments

Comments
 (0)