webpack-routes-plugin
Version:
基于文件系统的webpack约定式路由插件 - 自动生成路由配置,告别手写路由文件
180 lines (158 loc) • 4.4 kB
JavaScript
const path = require('path');
/**
* 标准化路由路径
* @param {string} routePath - 原始路由路径
* @returns {string} 标准化后的路由路径
*/
function normalizeRoutePath(routePath) {
if (!routePath || routePath === '/') {
return '/';
}
// 确保以 / 开头
if (!routePath.startsWith('/')) {
routePath = '/' + routePath;
}
// 移除尾部的 /
if (routePath.endsWith('/') && routePath.length > 1) {
routePath = routePath.slice(0, -1);
}
// 处理多个连续的 /
routePath = routePath.replace(/\/+/g, '/');
return routePath;
}
/**
* 获取相对路径
* @param {string} from - 起始路径
* @param {string} to - 目标路径
* @returns {string} 相对路径
*/
function getRelativePath(from, to) {
const fromDir = path.dirname(from);
let relativePath = path.relative(fromDir, to);
// 在Windows系统上,将反斜杠转换为正斜杠
relativePath = relativePath.replace(/\\/g, '/');
// 确保相对路径以 ./ 开头
if (!relativePath.startsWith('./') && !relativePath.startsWith('../')) {
relativePath = './' + relativePath;
}
return relativePath;
}
/**
* 检查是否为有效的页面文件
* @param {string} filename - 文件名
* @param {string[]} extensions - 允许的文件扩展名
* @returns {boolean} 是否为有效的页面文件
*/
function isValidPageFile(filename, extensions) {
const ext = path.extname(filename);
return extensions.includes(ext);
}
/**
* 将文件路径转换为驼峰命名
* @param {string} filepath - 文件路径
* @returns {string} 驼峰命名的字符串
*/
function toCamelCase(filepath) {
return filepath
.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : '')
.replace(/[^a-zA-Z0-9]/g, '');
}
/**
* 获取路由的权重,用于排序
* @param {string} routePath - 路由路径
* @returns {number} 路由权重
*/
function getRouteWeight(routePath) {
let weight = 0;
// 静态路由权重更高
if (!routePath.includes(':')) {
weight += 1000;
}
// 路径段数越多权重越高
const segments = routePath.split('/').filter(Boolean);
weight += segments.length * 100;
// 每个静态段增加权重
segments.forEach(segment => {
if (!segment.startsWith(':')) {
weight += 10;
}
});
return weight;
}
/**
* 检查路径是否匹配排除模式
* @param {string} filepath - 文件路径
* @param {string[]} excludePatterns - 排除模式数组
* @returns {boolean} 是否匹配排除模式
*/
function shouldExclude(filepath, excludePatterns) {
return excludePatterns.some(pattern => {
if (pattern.includes('*')) {
// 支持简单的通配符
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
return regex.test(filepath);
}
return filepath.includes(pattern);
});
}
/**
* 提取动态路由参数
* @param {string} routePath - 路由路径
* @returns {string[]} 参数数组
*/
function extractDynamicParams(routePath) {
const params = [];
const matches = routePath.match(/:([a-zA-Z_$][a-zA-Z0-9_$]*)/g);
if (matches) {
matches.forEach(match => {
params.push(match.substring(1));
});
}
return params;
}
/**
* 生成路由名称
* @param {string} routePath - 路由路径
* @returns {string} 路由名称
*/
function generateRouteName(routePath) {
return routePath
.replace(/^\//, '')
.replace(/\//g, '-')
.replace(/:/g, '')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '')
|| 'home';
}
/**
* 检查是否为布局文件
* @param {string} filename - 文件名
* @returns {boolean} 是否为布局文件
*/
function isLayoutFile(filename) {
const layoutNames = ['_layout', 'layout', '_app'];
const nameWithoutExt = path.basename(filename, path.extname(filename));
return layoutNames.includes(nameWithoutExt);
}
/**
* 检查是否为错误页面
* @param {string} filename - 文件名
* @returns {boolean} 是否为错误页面
*/
function isErrorPage(filename) {
const errorPages = ['404', '500', '_error', 'error'];
const nameWithoutExt = path.basename(filename, path.extname(filename));
return errorPages.includes(nameWithoutExt);
}
module.exports = {
normalizeRoutePath,
getRelativePath,
isValidPageFile,
toCamelCase,
getRouteWeight,
shouldExclude,
extractDynamicParams,
generateRouteName,
isLayoutFile,
isErrorPage
};