vite-plugin-convention-routes
Version:
一个类似Umi的约定式路由Vite插件,自动根据文件结构生成路由配置,支持多级动态路由参数、布局组件、元数据和懒加载
583 lines (535 loc) • 24 kB
JavaScript
// src/index.ts
import path from "path";
import fs from "fs";
function vitePluginConventionRoutes(options = {}) {
const {
routesDir = "src/views",
extensions = [".vue"],
generateDeclaration = true,
declarationPath = "src/router/routes.d.ts",
verbose = false,
isLazy = true,
excludes = ["components"],
layoutName = "_layout",
metaConfigPath = "src/router/route-meta.js",
enableInComponentMeta = true
} = options;
let config;
let routesCode = "";
let projectRoot = "";
let needsUpdate = false;
const componentMeta = /* @__PURE__ */ new Map();
let metaConfig = {};
function extractMetaFromComponent(code) {
const metaMatch = code.match(/\/\*\s*route-meta\s*({[^*]*})\s*\*\//);
if (metaMatch && metaMatch[1]) {
try {
return JSON.parse(metaMatch[1]);
} catch (e) {
console.warn("[vite-plugin-convention-routes] \u89E3\u6790\u7EC4\u4EF6\u5143\u6570\u636EJSON\u5931\u8D25:", e);
}
}
const exportMatch = code.match(/export\s+const\s+routeMeta\s*=\s*({[\s\S]*?});/);
if (exportMatch && exportMatch[1]) {
try {
const objectStr = exportMatch[1].trim();
const jsonLike = objectStr.replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:/g, '"$2":').replace(/:\s*'([^']*)'/g, ':"$1"').replace(/:\s*true\b/g, ":true").replace(/:\s*false\b/g, ":false").replace(/:\s*\[(.*?)\]/g, (_, p1) => {
return ":" + JSON.stringify(
p1.split(",").map((item) => item.trim().replace(/^['"]|['"]$/g, ""))
);
});
try {
return JSON.parse(jsonLike);
} catch (jsonError) {
console.warn("[vite-plugin-convention-routes] JSON\u89E3\u6790\u5931\u8D25\uFF0C\u4F7F\u7528\u7B80\u5355\u89E3\u6790:", jsonError);
const result = {};
const props = objectStr.match(/(\w+)\s*:\s*([^,}\r\n]+)/g) || [];
for (const prop of props) {
const [key, valueStr] = prop.split(":").map((p) => p.trim());
if (valueStr === "true") {
result[key] = true;
} else if (valueStr === "false") {
result[key] = false;
} else if (!isNaN(Number(valueStr))) {
result[key] = Number(valueStr);
} else if (valueStr.startsWith("'") && valueStr.endsWith("'")) {
result[key] = valueStr.slice(1, -1);
} else if (valueStr.startsWith('"') && valueStr.endsWith('"')) {
result[key] = valueStr.slice(1, -1);
} else {
result[key] = valueStr;
}
}
return result;
}
} catch (e) {
console.warn("[vite-plugin-convention-routes] \u89E3\u6790\u5BFC\u51FA\u7684\u5143\u6570\u636E\u5BF9\u8C61\u5931\u8D25:", e);
}
}
return null;
}
const applyMetadata = (route, fullPath) => {
route.meta = route.meta || {};
if (metaConfig && metaConfig[fullPath]) {
route.meta = {
...route.meta,
...metaConfig[fullPath]
};
if (verbose) {
console.log("[vite-plugin-convention-routes] \u4ECE\u914D\u7F6E\u6587\u4EF6\u5E94\u7528\u5143\u6570\u636E:", fullPath);
}
}
if (route.component && typeof route.component === "function") {
const compStr = route.component.toString();
const importMatch = compStr.match(/import\(['"]([^'"]+)['"]\)/);
if (importMatch && importMatch[1]) {
const componentPath = importMatch[1];
for (const [id, meta] of componentMeta.entries()) {
if (id.includes(componentPath)) {
route.meta = {
...route.meta,
...meta
};
if (verbose) {
console.log("[vite-plugin-convention-routes] \u4ECE\u7EC4\u4EF6\u5E94\u7528\u5143\u6570\u636E:", fullPath);
}
break;
}
}
}
}
};
return {
name: "vite-plugin-convention-routes",
configResolved(resolvedConfig) {
config = resolvedConfig;
projectRoot = config.root;
if (verbose) {
console.log("[vite-plugin-convention-routes] \u9879\u76EE\u6839\u76EE\u5F55:", projectRoot);
console.log("[vite-plugin-convention-routes] \u8DEF\u7531\u76EE\u5F55:", routesDir);
console.log("[vite-plugin-convention-routes] \u6392\u9664\u76EE\u5F55:", excludes);
console.log("[vite-plugin-convention-routes] \u5E03\u5C40\u6587\u4EF6\u540D:", layoutName);
}
try {
const metaConfigFile = path.resolve(projectRoot, metaConfigPath);
if (fs.existsSync(metaConfigFile)) {
try {
const fileUrl = new URL(`file://${metaConfigFile.replace(/\\/g, "/")}`);
import(
/* @vite-ignore */
fileUrl.href
).then((module) => {
metaConfig = module.default || module;
if (verbose) {
console.log("[vite-plugin-convention-routes] \u52A0\u8F7D\u5143\u6570\u636E\u914D\u7F6E\u6210\u529F:", metaConfigFile);
}
}).catch((error) => {
console.warn("[vite-plugin-convention-routes] \u5BFC\u5165\u5143\u6570\u636E\u914D\u7F6E\u6587\u4EF6\u5931\u8D25:", error);
});
} catch (error) {
console.warn("[vite-plugin-convention-routes] \u5BFC\u5165\u5143\u6570\u636E\u914D\u7F6E\u6587\u4EF6\u5931\u8D25:", error);
}
} else if (verbose) {
console.log("[vite-plugin-convention-routes] \u5143\u6570\u636E\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728:", metaConfigFile);
}
} catch (error) {
console.error("[vite-plugin-convention-routes] \u52A0\u8F7D\u5143\u6570\u636E\u914D\u7F6E\u5931\u8D25:", error);
}
},
transform(code, id) {
if (enableInComponentMeta && id.endsWith(".vue") && id.includes(routesDir)) {
const meta = extractMetaFromComponent(code);
if (meta) {
componentMeta.set(id, meta);
if (verbose) {
console.log("[vite-plugin-convention-routes] \u4ECE\u7EC4\u4EF6\u63D0\u53D6\u5230\u5143\u6570\u636E:", id, meta);
}
}
}
if (id.includes("router/index") && (id.endsWith(".js") || id.endsWith(".ts"))) {
if (verbose) {
console.log("[vite-plugin-convention-routes] \u68C0\u6D4B\u5230\u8DEF\u7531\u6587\u4EF6:", id);
}
const routesImportCode = `
// \u81EA\u52A8\u751F\u6210\u7684\u8DEF\u7531\u914D\u7F6E
[
// \u521D\u59CB\u8DEF\u7531\u914D\u7F6E\u5C06\u5728\u8FD9\u91CC\u751F\u6210
].concat((() => {
// \u5B58\u50A8\u6240\u6709\u751F\u6210\u7684\u8DEF\u7531
const generatedRoutes = [];
// \u7EA6\u5B9A\u5F0F\u8DEF\u7531\u5BFC\u5165
const pages = import.meta.glob('/${routesDir}/**/*.vue', { ${isLazy ? "" : "eager: true, "} });
// \u5B58\u50A8\u5E03\u5C40\u7EC4\u4EF6
const layouts = {};
// \u5B58\u50A8\u8DEF\u7531\u8DEF\u5F84\u4E0E\u7EC4\u4EF6\u7684\u6620\u5C04
const routeMap = {};
// \u68C0\u6D4B\u6839\u76EE\u5F55\u662F\u5426\u6709index.vue\u6587\u4EF6
let hasRootIndex = false;
// \u5B9A\u4E49\u5E94\u7528\u5143\u6570\u636E\u7684\u51FD\u6570
const applyMetadata = (route, fullPath) => {
// \u786E\u4FDDmeta\u5BF9\u8C61\u5B58\u5728
route.meta = route.meta || {};
// \u5E94\u7528\u5143\u6570\u636E
const metaConfig = ${JSON.stringify(metaConfig)};
if (metaConfig && metaConfig[fullPath]) {
route.meta = {
...route.meta,
...metaConfig[fullPath]
};
}
};
// \u7B2C\u4E00\u6B65\uFF1A\u6536\u96C6\u6240\u6709\u8DEF\u7531\u548C\u5E03\u5C40\u7EC4\u4EF6
Object.keys(pages).forEach((path) => {
// \u6392\u9664\u6307\u5B9A\u76EE\u5F55\u4E0B\u7684\u6587\u4EF6
const shouldExclude = ${JSON.stringify(excludes)}.some(excludeDir =>
path.includes(\`/${routesDir}/\${excludeDir}/\`) ||
path.startsWith(\`/${routesDir}/\${excludeDir}.\`)
);
if (shouldExclude) {
if (${verbose}) {
console.log('[vite-plugin-convention-routes] \u6392\u9664\u8DEF\u7531:', path);
}
return;
}
const routePath = path
.replace(new RegExp(\`^/${routesDir}/\`), '')
.replace(/\\.vue$/, '');
// \u68C0\u67E5\u662F\u5426\u4E3A\u6839\u76EE\u5F55\u7684index.vue
if (routePath === 'index') {
hasRootIndex = true;
generatedRoutes.push({
path: '/',
component: ${isLazy ? "pages[path]" : "pages[path].default"},
name: 'home'
});
// \u5E94\u7528\u5143\u6570\u636E\u5230\u6839\u8DEF\u7531
applyMetadata(generatedRoutes[generatedRoutes.length - 1], '/');
if (${verbose}) {
console.log('[vite-plugin-convention-routes] \u6DFB\u52A0\u6839\u8DEF\u7531: / \u7EC4\u4EF6:', pages[path] ? '\u5DF2\u52A0\u8F7D' : '\u672A\u52A0\u8F7D');
}
return;
}
// \u68C0\u67E5\u662F\u5426\u662F\u5E03\u5C40\u7EC4\u4EF6
if (routePath.endsWith(\`/${layoutName}\`)) {
// \u83B7\u53D6\u5E03\u5C40\u7EC4\u4EF6\u6240\u5728\u7684\u76EE\u5F55\u8DEF\u5F84
const layoutDir = routePath.replace(new RegExp(\`/${layoutName}$\`), '');
layouts[layoutDir] = {
component: ${isLazy ? "pages[path]" : "pages[path].default"},
path: layoutDir
};
if (${verbose}) {
console.log('[vite-plugin-convention-routes] \u68C0\u6D4B\u5230\u5E03\u5C40\u7EC4\u4EF6:', path, '\u76EE\u5F55:', layoutDir);
}
return;
}
// \u5904\u7406\u8DEF\u7531\u8DEF\u5F84\uFF0C\u5C06\u6587\u4EF6\u8DEF\u5F84\u8F6C\u6362\u4E3AURL\u8DEF\u5F84
// \u4F8B\u5982: users/[id]/index.vue -> users/:id
const processedPath = routePath
.replace(/index$/, '')
.replace(/\\[(\\w+)\\]/g, ':$1')
.replace(/\\/index$/, '')
.replace(/\\//g, '/');
// \u5FFD\u7565\u5DF2\u7ECF\u5904\u7406\u7684\u9996\u9875\u8DEF\u7531
if (processedPath === '') return;
// \u5B58\u50A8\u8DEF\u7531\u4FE1\u606F\uFF0C\u4FDD\u7559\u539F\u59CB\u8DEF\u5F84\u4EE5\u4FBF\u540E\u7EED\u5904\u7406
routeMap[routePath] = {
path: processedPath === '' ? '/' : \`/\${processedPath}\`,
component: ${isLazy ? "pages[path]" : "pages[path].default"},
name: processedPath.replace(/\\//g, '-').replace(/:/g, '').replace(/^-|-$/g, '') || 'home',
rawPath: routePath, // \u4FDD\u5B58\u539F\u59CB\u8DEF\u5F84
originalFilePath: path // \u4FDD\u5B58\u539F\u59CB\u6587\u4EF6\u8DEF\u5F84\uFF0C\u7528\u4E8E\u786E\u4FDD\u7EC4\u4EF6\u5F15\u7528\u6B63\u786E
};
});
// \u5982\u679C\u6CA1\u6709\u68C0\u6D4B\u5230\u6839\u76EE\u5F55\u7684index.vue\uFF0C\u5219\u6DFB\u52A0\u4E00\u4E2A\u9ED8\u8BA4\u7684\u6839\u8DEF\u7531
if (!hasRootIndex && Object.keys(layouts).length > 0) {
generatedRoutes.push({
path: '/',
redirect: '/' + Object.keys(layouts)[0]
});
}
// \u7B2C\u4E8C\u6B65\uFF1A\u5904\u7406\u5E03\u5C40\u7EC4\u4EF6\u548C\u5D4C\u5957\u8DEF\u7531
Object.keys(routeMap).forEach(routePath => {
const route = {...routeMap[routePath]}; // \u521B\u5EFA\u526F\u672C\uFF0C\u907F\u514D\u4FEE\u6539\u539F\u59CB\u5BF9\u8C61
// \u68C0\u67E5\u8DEF\u7531\u662F\u5426\u5E94\u8BE5\u5D4C\u5957\u5728\u5E03\u5C40\u7EC4\u4EF6\u4E2D
let parentFound = false;
// \u4ECE\u6700\u957F\u7684\u5E03\u5C40\u8DEF\u5F84\u5F00\u59CB\u5339\u914D\uFF0C\u786E\u4FDD\u6700\u6DF1\u5C42\u7684\u5E03\u5C40\u4F18\u5148
Object.keys(layouts)
.sort((a, b) => b.length - a.length)
.forEach(layoutPath => {
// \u5982\u679C\u8DEF\u7531\u8DEF\u5F84\u4EE5\u5E03\u5C40\u8DEF\u5F84\u5F00\u5934\uFF0C\u5E76\u4E14\u4E0D\u662F\u5E03\u5C40\u8DEF\u5F84\u672C\u8EAB
if (routePath.startsWith(layoutPath + '/') && routePath !== layoutPath && !parentFound) {
parentFound = true;
// \u786E\u4FDD\u5E03\u5C40\u8DEF\u7531\u5DF2\u6DFB\u52A0\u5230routes\u6570\u7EC4
let layoutRoute = generatedRoutes.find(r => {
return r.path === '/' + layoutPath ||
(layoutPath === '' && r.path === '/');
});
if (!layoutRoute) {
// \u521B\u5EFA\u5E03\u5C40\u8DEF\u7531
layoutRoute = {
path: layoutPath === '' ? '/' : '/' + layoutPath,
component: layouts[layoutPath].component,
children: []
};
// \u6DFB\u52A0\u5230routes\u6570\u7EC4
generatedRoutes.push(layoutRoute);
// \u5E94\u7528\u5143\u6570\u636E
applyMetadata(layoutRoute, layoutRoute.path);
if (${verbose}) {
console.log('[vite-plugin-convention-routes] \u521B\u5EFA\u5E03\u5C40\u8DEF\u7531:', layoutRoute.path, '\u7EC4\u4EF6:', layouts[layoutPath].component ? '\u5DF2\u52A0\u8F7D' : '\u672A\u52A0\u8F7D');
}
}
// \u786E\u4FDD\u5E03\u5C40\u8DEF\u7531\u6709children\u5C5E\u6027
if (!layoutRoute.children) {
layoutRoute.children = [];
}
// \u5C06\u5F53\u524D\u8DEF\u7531\u6DFB\u52A0\u4E3A\u5E03\u5C40\u8DEF\u7531\u7684\u5B50\u8DEF\u7531
// \u8C03\u6574\u5B50\u8DEF\u7531\u7684\u8DEF\u5F84\uFF0C\u79FB\u9664\u7236\u8DEF\u7531\u90E8\u5206
const childPath = routePath.substring(layoutPath.length + 1);
// \u5904\u7406\u5B50\u8DEF\u7531\u8DEF\u5F84
// 1. \u5C06[param]\u8F6C\u6362\u4E3A:param
// 2. \u79FB\u9664\u672B\u5C3E\u7684index
let processedChildPath = childPath
.replace(/\\[(\\w+)\\]/g, ':$1')
.replace(/\\/index$/, '')
.replace(/index$/, '');
// \u786E\u4FDD\u52A8\u6001\u53C2\u6570\u8DEF\u7531\u6B63\u786E\u5904\u7406
if (processedChildPath.includes('[') && processedChildPath.includes(']')) {
processedChildPath = processedChildPath.replace(/\\[(\\w+)\\]/g, ':$1');
}
// \u5B50\u8DEF\u7531\u8DEF\u5F84\u4E0D\u5E94\u8BE5\u4EE5\u659C\u6760\u5F00\u5934
route.path = processedChildPath === '' ? '' : processedChildPath;
// \u786E\u4FDD\u7EC4\u4EF6\u4FE1\u606F\u88AB\u4FDD\u7559
if (!route.component && routeMap[routePath].component) {
route.component = routeMap[routePath].component;
} else if (!route.component && routeMap[routePath].originalFilePath) {
// \u5982\u679C\u7EC4\u4EF6\u4E22\u5931\uFF0C\u5C1D\u8BD5\u4F7F\u7528\u539F\u59CB\u6587\u4EF6\u8DEF\u5F84\u91CD\u65B0\u83B7\u53D6
route.component = ${isLazy ? "pages[routeMap[routePath].originalFilePath]" : "pages[routeMap[routePath].originalFilePath].default"};
}
// \u66F4\u65B0\u8DEF\u7531\u540D\u79F0\uFF0C\u79FB\u9664\u5192\u53F7
if (route.name) {
route.name = route.name.replace(/:/g, '');
}
// \u5220\u9664rawPath\u5C5E\u6027\uFF0C\u907F\u514DVue Router\u8B66\u544A
delete route.rawPath;
if (${verbose}) {
console.log('[vite-plugin-convention-routes] \u6DFB\u52A0\u5B50\u8DEF\u7531:', route.path, '\u5230\u5E03\u5C40:', layoutRoute.path, '\u7EC4\u4EF6:', route.component ? '\u5DF2\u52A0\u8F7D' : '\u672A\u52A0\u8F7D');
}
// \u5E94\u7528\u5143\u6570\u636E
const fullPath = layoutRoute.path === '/' ? '/' + route.path : layoutRoute.path + '/' + route.path;
applyMetadata(route, fullPath);
// \u6700\u540E\u68C0\u67E5\u786E\u4FDD\u8DEF\u7531\u6709\u7EC4\u4EF6
if (!route.component) {
console.warn(\`[vite-plugin-convention-routes] \u8B66\u544A: \u8DEF\u7531 \${route.path} \u6CA1\u6709\u7EC4\u4EF6\`);
}
layoutRoute.children.push(route);
}
});
// \u5982\u679C\u6CA1\u6709\u627E\u5230\u7236\u5E03\u5C40\uFF0C\u5219\u6DFB\u52A0\u5230\u9876\u5C42\u8DEF\u7531
if (!parentFound) {
// \u5220\u9664rawPath\u5C5E\u6027\uFF0C\u907F\u514DVue Router\u8B66\u544A
delete route.rawPath;
generatedRoutes.push(route);
// \u5E94\u7528\u5143\u6570\u636E\u5230\u8DEF\u7531
applyMetadata(route, route.path);
}
});
// \u6700\u7EC8\u68C0\u67E5\uFF0C\u786E\u4FDD\u6240\u6709\u8DEF\u7531\u90FD\u6709\u7EC4\u4EF6
const checkRouteComponents = (routes) => {
routes.forEach(route => {
// \u5FFD\u7565\u91CD\u5B9A\u5411\u8DEF\u7531
if (route.redirect) return;
// \u68C0\u67E5\u5F53\u524D\u8DEF\u7531\u662F\u5426\u6709\u7EC4\u4EF6
if (!route.component) {
console.warn(\`[vite-plugin-convention-routes] \u8B66\u544A: \u8DEF\u7531 \${route.path} \u6CA1\u6709\u7EC4\u4EF6\`);
}
// \u9012\u5F52\u68C0\u67E5\u5B50\u8DEF\u7531
if (route.children && route.children.length > 0) {
checkRouteComponents(route.children);
}
});
};
// \u6267\u884C\u68C0\u67E5
checkRouteComponents(generatedRoutes);
// \u8F93\u51FA\u751F\u6210\u7684\u8DEF\u7531\u914D\u7F6E\uFF0C\u65B9\u4FBF\u8C03\u8BD5
if (${verbose}) {
// \u521B\u5EFA\u4E00\u4E2A\u4E0D\u5305\u542B\u51FD\u6570\u7684\u8DEF\u7531\u914D\u7F6E\u526F\u672C\u7528\u4E8E\u65E5\u5FD7\u8F93\u51FA
const routesForLog = JSON.parse(JSON.stringify(generatedRoutes, (key, value) => {
// \u5C06\u7EC4\u4EF6\u51FD\u6570\u8F6C\u6362\u4E3A\u6807\u8BB0\uFF0C\u4EE5\u4FBF\u5728\u65E5\u5FD7\u4E2D\u663E\u793A
if (key === 'component' && typeof value === 'function') {
return '() => Component'; // \u7528\u5B57\u7B26\u4E32\u66FF\u4EE3\u51FD\u6570
}
return value;
}));
console.log('[vite-plugin-convention-routes] \u751F\u6210\u7684\u8DEF\u7531\u914D\u7F6E:', JSON.stringify(routesForLog, null, 2));
}
return generatedRoutes;
})())
`;
routesCode = routesImportCode;
const routesRegex = /(const\s+routes\s*(?::\s*RouteRecordRaw\[\])?\s*=\s*)\[\s*\]/;
if (routesRegex.test(code)) {
return code.replace(routesRegex, (_match, p1) => {
return p1 + routesImportCode.trim().replace(/;$/, "");
});
}
return null;
}
return null;
},
async closeBundle() {
if (generateDeclaration && routesCode) {
const declarationContent = `// \u81EA\u52A8\u751F\u6210\u7684\u8DEF\u7531\u7C7B\u578B\u58F0\u660E
import { RouteRecordRaw } from 'vue-router';
declare const routes: RouteRecordRaw[];
export default routes;
`;
if (verbose) {
console.log(
"[vite-plugin-convention-routes] \u751F\u6210\u8DEF\u7531\u58F0\u660E\u6587\u4EF6:",
declarationPath
);
}
try {
const fullPath = path.resolve(projectRoot, declarationPath);
const dirPath = path.dirname(fullPath);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
fs.writeFileSync(fullPath, declarationContent, "utf-8");
if (verbose) {
console.log(
"[vite-plugin-convention-routes] \u8DEF\u7531\u58F0\u660E\u6587\u4EF6\u5DF2\u751F\u6210:",
fullPath
);
}
} catch (error) {
console.error(
"[vite-plugin-convention-routes] \u751F\u6210\u58F0\u660E\u6587\u4EF6\u5931\u8D25:",
error
);
}
}
},
// 提供虚拟模块,可以通过import导入生成的路由
resolveId(id) {
if (id === "virtual:convention-routes") {
return "\0virtual:convention-routes";
}
return null;
},
load(id) {
if (id === "\0virtual:convention-routes") {
return routesCode || "export default [];";
}
return null;
},
// 监听路由文件变化,发送HMR更新而非重启服务器
configureServer(server) {
const routesGlob = `${routesDir}/**/*.vue`;
let hmrTimeout = null;
server.watcher.add(routesGlob);
const handleRouteChange = (file) => {
if (file.includes(routesDir) && extensions.some((ext) => file.endsWith(ext))) {
if (verbose) {
console.log("[vite-plugin-convention-routes] \u68C0\u6D4B\u5230\u8DEF\u7531\u6587\u4EF6\u53D8\u5316:", file);
}
needsUpdate = true;
if (hmrTimeout) {
clearTimeout(hmrTimeout);
}
hmrTimeout = setTimeout(() => {
const routerFiles = ["router/index.js", "router/index.ts", "src/router/index.js", "src/router/index.ts"];
const routerModules = [];
routerFiles.forEach((file2) => {
const mods = server.moduleGraph.getModulesByFile(file2);
if (mods) {
routerModules.push(...Array.from(mods));
}
});
const virtualMods = server.moduleGraph.getModulesByFile("\0virtual:convention-routes");
if (virtualMods) {
routerModules.push(...Array.from(virtualMods));
}
const modulesToInvalidate = new Set(routerModules);
const addImporters = (mod) => {
if (!mod.importers) return;
for (const importer of mod.importers) {
if (!modulesToInvalidate.has(importer)) {
modulesToInvalidate.add(importer);
addImporters(importer);
}
}
};
routerModules.forEach((mod) => {
if (mod) {
addImporters(mod);
}
});
if (verbose) {
console.log(`[vite-plugin-convention-routes] \u627E\u5230 ${modulesToInvalidate.size} \u4E2A\u9700\u8981\u66F4\u65B0\u7684\u6A21\u5757`);
}
if (modulesToInvalidate.size > 0) {
modulesToInvalidate.forEach((mod) => {
server.moduleGraph.invalidateModule(mod);
});
const timestamp = Date.now();
const updates = Array.from(modulesToInvalidate).map((mod) => ({
type: "js-update",
path: mod.url,
acceptedPath: mod.url,
timestamp
}));
if (verbose) {
console.log("[vite-plugin-convention-routes] \u53D1\u9001HMR\u66F4\u65B0:", updates.length);
}
server.ws.send({
type: "update",
updates
});
setTimeout(() => {
if (verbose) {
console.log("[vite-plugin-convention-routes] \u53D1\u9001\u5168\u9875\u9762\u5237\u65B0");
}
server.ws.send({
type: "full-reload",
path: "*"
});
}, 100);
} else {
if (verbose) {
console.log("[vite-plugin-convention-routes] \u672A\u627E\u5230\u76F8\u5173\u6A21\u5757\uFF0C\u53D1\u9001\u5168\u9875\u9762\u5237\u65B0");
}
server.ws.send({
type: "full-reload",
path: "*"
});
}
}, 100);
}
};
server.watcher.on("add", (file) => {
handleRouteChange(file);
});
server.watcher.on("unlink", (file) => {
handleRouteChange(file);
});
server.watcher.on("change", (file) => {
handleRouteChange(file);
});
server.middlewares.use((req, _res, next) => {
if (needsUpdate && req.url && (req.url.includes("router/index") || req.url.includes("virtual:convention-routes"))) {
needsUpdate = false;
if (verbose) {
console.log("[vite-plugin-convention-routes] \u68C0\u6D4B\u5230\u8DEF\u7531\u8BF7\u6C42\uFF0C\u89E6\u53D1\u66F4\u65B0");
}
handleRouteChange("virtual-trigger");
}
next();
});
}
};
}
export {
vitePluginConventionRoutes as default
};