@feoe/fs-router
Version:
file system based routing
699 lines (697 loc) • 30.9 kB
JavaScript
import * as __WEBPACK_EXTERNAL_MODULE_unplugin__ from "unplugin";
import * as __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__ from "node:path";
import * as __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__ from "node:fs/promises";
import * as __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__ from "node:fs";
import * as __WEBPACK_EXTERNAL_MODULE_glob__ from "glob";
const NESTED_ROUTE = {
LAYOUT_FILE: 'layout',
LAYOUT_CONFIG_FILE: 'layout.config',
LAYOUT_LOADER_FILE: 'layout.loader',
LAYOUT_DATA_FILE: 'layout.data',
LAYOUT_CLIENT_LOADER: 'layout.data.client',
PAGE_FILE: 'page',
PAGE_CONFIG_FILE: 'page.config',
PAGE_LOADER_FILE: 'page.loader',
PAGE_DATA_FILE: 'page.data',
PAGE_CLIENT_LOADER: 'page.data.client',
SPLATE_FILE: '$',
SPLATE_CONFIG_FILE: '$.config',
SPLATE_LOADER_FILE: '$.loader',
SPLATE_DATA_FILE: '$.data',
SPLATE_CLIENT_DATA: '$.data.client',
LOADING_FILE: 'loading',
ERROR_FILE: 'error',
LOADER_FILE: 'loader'
};
const JS_EXTENSIONS = [
'.js',
'.jsx',
'.ts',
'.tsx'
];
const getPathWithoutExt = (filepath)=>filepath.replace(/\.[^/.]+$/, '');
const hasAction = async (filepath)=>{
const content = await __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__.promises.readFile(filepath, 'utf-8');
return content.includes('export const action') || content.includes('export let action');
};
const replaceWithAlias = (basePath, filePath, alias)=>{
const relativePath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.relative(basePath, filePath);
return __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.join(alias, relativePath);
};
const normalizeToPosixPath = (str)=>str.replace(/\\/g, '/');
function _define_property(obj, key, value) {
if (key in obj) Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
else obj[key] = value;
return obj;
}
const conventionNames = Object.values(NESTED_ROUTE);
class RouteExtractor {
async extract() {
const route = await this.walkDirectory(this.routesDir);
if (!route) return [];
return this.optimizeRoute(route);
}
async walkDirectory(dirname) {
var _finalRoute_children;
if (!await __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__.promises.access(dirname).then(()=>true).catch(()=>false)) return null;
const stats = await __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__.promises.stat(dirname);
if (!stats.isDirectory()) return null;
const alias = this.alias;
const relativeDir = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.relative(this.routesDir, dirname);
const pathSegments = relativeDir.split(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.sep);
const lastSegment = pathSegments[pathSegments.length - 1];
const isRoot = "" === lastSegment;
const isPathlessLayout = lastSegment.startsWith("__");
const isWithoutLayoutPath = lastSegment.includes(".");
let routePath = isRoot || isPathlessLayout ? "/" : `${lastSegment}`;
if (isWithoutLayoutPath) routePath = lastSegment.split(".").join("/");
routePath = this.replaceDynamicPath(routePath);
const route = {
path: null == routePath ? void 0 : routePath.replace(/\$$/, "?"),
children: [],
isRoot,
type: "nested"
};
let pageLoaderFile = "";
let pageRoute = null;
let pageConfigFile = "";
let pageClientData = "";
let pageData = "";
let pageAction = "";
let splatLoaderFile = "";
let splatRoute = null;
let splatConfigFile = "";
let splatClientData = "";
let splatData = "";
let splatAction = "";
const entries = await __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__.promises.readdir(dirname);
for (const entry of entries){
const entryPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.join(dirname, entry);
const entryPathWithAlias = getPathWithoutExt(replaceWithAlias(alias.basename, entryPath, alias.name));
const extname = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.extname(entry);
const entryWithoutExt = entry.slice(0, -extname.length);
const isDirectory = (await __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__.promises.stat(entryPath)).isDirectory();
if (isDirectory) {
const childRoute = await this.walkDirectory(entryPath);
if (childRoute && !Array.isArray(childRoute)) {
var _route_children;
null === (_route_children = route.children) || void 0 === _route_children || _route_children.push(childRoute);
}
}
if (!extname || !!JS_EXTENSIONS.includes(extname) && !!conventionNames.includes(entryWithoutExt)) {
if (entryWithoutExt === NESTED_ROUTE.LAYOUT_LOADER_FILE) {
if (!route.loader) route.loader = entryPathWithAlias;
}
if (entryWithoutExt === NESTED_ROUTE.LAYOUT_CLIENT_LOADER) route.clientData = entryPathWithAlias;
if (entryWithoutExt === NESTED_ROUTE.LAYOUT_DATA_FILE) {
route.data = entryPathWithAlias;
if (await hasAction(entryPath)) route.action = entryPathWithAlias;
}
if (entryWithoutExt === NESTED_ROUTE.LAYOUT_CONFIG_FILE) {
if (!route.config) route.config = entryPathWithAlias;
}
if (entryWithoutExt === NESTED_ROUTE.LAYOUT_FILE) route._component = entryPathWithAlias;
if (entryWithoutExt === NESTED_ROUTE.PAGE_LOADER_FILE) pageLoaderFile = entryPathWithAlias;
if (entryWithoutExt === NESTED_ROUTE.PAGE_CLIENT_LOADER) pageClientData = entryPathWithAlias;
if (entryWithoutExt === NESTED_ROUTE.PAGE_DATA_FILE) {
pageData = entryPathWithAlias;
if (await hasAction(entryPath)) pageAction = entryPathWithAlias;
}
if (entryWithoutExt === NESTED_ROUTE.PAGE_CONFIG_FILE) pageConfigFile = entryPathWithAlias;
if (entryWithoutExt === NESTED_ROUTE.PAGE_FILE) {
var _route_children1;
pageRoute = this.createIndexRoute({
_component: entryPathWithAlias
}, entryPath);
if (pageLoaderFile) pageRoute.loader = pageLoaderFile;
if (pageConfigFile) pageRoute.config = pageConfigFile;
if (pageData) pageRoute.data = pageData;
if (pageClientData) pageRoute.clientData = pageClientData;
if (pageAction) pageRoute.action = pageAction;
null === (_route_children1 = route.children) || void 0 === _route_children1 || _route_children1.unshift(pageRoute);
}
if (entryWithoutExt === NESTED_ROUTE.SPLATE_LOADER_FILE) splatLoaderFile = entryPathWithAlias;
if (entryWithoutExt === NESTED_ROUTE.SPLATE_CLIENT_DATA) splatClientData = entryPathWithAlias;
if (entryWithoutExt === NESTED_ROUTE.SPLATE_CONFIG_FILE) {
if (!route.config) splatConfigFile = replaceWithAlias(alias.basename, entryPath, alias.name);
}
if (entryWithoutExt === NESTED_ROUTE.SPLATE_DATA_FILE) {
splatData = entryPathWithAlias;
if (await hasAction(entryPath)) splatAction = entryPathWithAlias;
}
if (entryWithoutExt === NESTED_ROUTE.SPLATE_FILE) {
var _route_children2;
splatRoute = this.createRoute({
_component: entryPathWithAlias,
path: "*"
}, entryPath);
if (splatLoaderFile) splatRoute.loader = splatLoaderFile;
if (splatClientData) splatRoute.clientData = splatClientData;
if (splatData) splatRoute.data = splatData;
if (splatConfigFile) splatRoute.config = splatConfigFile;
if (splatAction) splatRoute.action = splatAction;
null === (_route_children2 = route.children) || void 0 === _route_children2 || _route_children2.push(splatRoute);
}
if (entryWithoutExt === NESTED_ROUTE.LOADING_FILE) route.loading = entryPathWithAlias;
if (entryWithoutExt === NESTED_ROUTE.ERROR_FILE) route.error = entryPathWithAlias;
}
}
let finalRoute = this.createRoute(route, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.join(dirname, `${NESTED_ROUTE.LAYOUT_FILE}.ts`));
if (isPathlessLayout) delete finalRoute.path;
const childRoutes = finalRoute.children = null === (_finalRoute_children = finalRoute.children) || void 0 === _finalRoute_children ? void 0 : _finalRoute_children.filter((childRoute)=>childRoute);
if (childRoutes && 0 === childRoutes.length && !finalRoute.index && !finalRoute._component) return null;
if (childRoutes && 1 === childRoutes.length && !finalRoute._component) {
const childRoute = childRoutes[0];
if ("*" === childRoute.path) {
const path = `${finalRoute.path || ""}/${childRoute.path || ""}`;
finalRoute = {
...childRoute,
path
};
}
}
if (isRoot && !finalRoute._component) throw new Error("The root layout component is required, make sure the routes/layout.tsx file exists.");
return finalRoute;
}
optimizeRoute(routeTree) {
var _routeTree_children;
if (!(null === (_routeTree_children = routeTree.children) || void 0 === _routeTree_children ? void 0 : _routeTree_children.length)) return [
routeTree
];
if (!routeTree._component && !routeTree.error && !routeTree.loading && !routeTree.config && !routeTree.clientData) {
const newRoutes = routeTree.children.map((child)=>{
const routePath = `${routeTree.path || ""}${child.path ? `/${child.path}` : ""}`;
const newRoute = {
...child,
path: routePath.replace(/\/\//g, "/")
};
if (routePath.length > 0) delete newRoute.index;
else delete newRoute.path;
return newRoute;
});
return Array.from(new Set(newRoutes)).flatMap((route)=>this.optimizeRoute(route));
}
return [
{
...routeTree,
children: routeTree.children.flatMap((child)=>this.optimizeRoute(child))
}
];
}
replaceDynamicPath(routePath) {
return routePath.replace(/\[(.*?)\]/g, ":$1");
}
getRouteId(componentPath) {
const relativePath = normalizeToPosixPath(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.relative(this.routesDir, componentPath));
const pathWithoutExt = getPathWithoutExt(relativePath);
let id = "";
id = this.isMainEntry ? pathWithoutExt : `${this.entryName}_${pathWithoutExt}`;
return id.replace(/\[(.*?)\]/g, "($1)");
}
isValidFile(filename) {
const ext = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.extname(filename);
return this.extensions.includes(ext);
}
getRelativePath(filepath) {
if (!this.alias) return __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.relative(this.routesDir, filepath);
return getPathWithoutExt(replaceWithAlias(this.alias.basename, filepath, this.alias.name));
}
createRoute(routeInfo, componentPath) {
const id = this.getRouteId(componentPath);
return {
...routeInfo,
id,
type: "nested"
};
}
createIndexRoute(routeInfo, componentPath) {
return this.createRoute({
...routeInfo,
index: true,
children: void 0
}, componentPath);
}
createSplatRoute(splatFile, splatLoader, splatConfig, splatClientData, splatData, splatAction) {
return {
path: "*",
_component: splatFile,
loader: splatLoader,
config: splatConfig,
clientData: splatClientData,
data: splatData,
action: splatAction
};
}
constructor(options){
_define_property(this, "routesDir", void 0);
_define_property(this, "extensions", JS_EXTENSIONS);
_define_property(this, "entryName", void 0);
_define_property(this, "isMainEntry", void 0);
_define_property(this, "alias", void 0);
this.routesDir = options.routesDir;
this.entryName = options.entryName || "main";
this.isMainEntry = options.isMainEntry ?? true;
this.alias = options.alias || {
name: "",
basename: ""
};
}
}
function generator_define_property(obj, key, value) {
if (key in obj) Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
else obj[key] = value;
return obj;
}
class RouteCodeGenerator {
generate(routes) {
const routeCode = this.generateRouteCode(routes);
return this.wrapWithImports(routeCode);
}
generateRouteCode(routes) {
const code = `export const routes = [
${routes.map((route)=>this.stringifyRoute(route)).join(",\n ")}
];`;
return JSON.parse(JSON.stringify(code, null, 2));
}
stringifyRoute(route) {
var _route_children;
const element = this.generateElementCode(route);
const errorElement = this.generateErrorElement(route);
const loader = this.generateLoaderCode(route);
const action = this.generateActionCode(route);
const routeObj = {
path: route.path,
index: route.index,
errorElement: errorElement || void 0,
loader: loader || void 0,
element: element || void 0,
action: action || void 0,
children: void 0
};
const childrenStr = (null === (_route_children = route.children) || void 0 === _route_children ? void 0 : _route_children.length) ? `children: [${route.children.map((child)=>this.stringifyRoute(child)).join(",")}]` : "";
const routeEntries = Object.entries(routeObj).filter(([_, value])=>void 0 !== value).map(([key, value])=>{
if ("path" === key) return `${key}: '${value}'`;
if ("element" === key) ;
return `${key}: ${value}`;
});
if (childrenStr) routeEntries.push(childrenStr);
return `{
${routeEntries.join(",\n ")}
}`;
}
generateElementCode(route) {
if (!route._component) return "";
const chunkName = route.id || __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.basename(route._component, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.extname(route._component));
if (route.isRoot) {
this.runtimeImports.add(`import RootLayout from '${route._component}';`);
return "<RootLayout />";
}
if (this.options.splitting) {
const importPath = route._component;
const componentName = `Component_${this.componentDeclarations.size}`;
let loadingComponent = '';
if (route.loading) {
const loadingName = `Loading_${this.loadingImports.size}`;
this.loadingImports.add(`import ${loadingName} from '${route.loading}';`);
loadingComponent = `, { fallback: <${loadingName} /> }`;
}
this.componentDeclarations.add(`const ${componentName} = loadable(() => import(/* webpackChunkName: "${chunkName}" */ '${importPath}')${loadingComponent});`);
return `<${componentName} />`;
}
const componentName = `Component_${this.componentDeclarations.size}`;
this.componentDeclarations.add(`import ${componentName} from '${route._component}';`);
return `<${componentName} />`;
}
generateErrorElement(route) {
if (!route.error) return "";
const errorName = `Error_${this.errorImports.size}`;
this.errorImports.add(`import ${errorName} from '${route.error}';`);
return `<${errorName} />`;
}
generateActionCode(route) {
if (!route.action) return "";
const actionName = `action_${this.loaderImports.size}`;
this.loaderImports.add(`import ${actionName} from '${route.action}';`);
return actionName;
}
generateConfigCode(route) {
if (!route.config) return "";
const configName = `config_${this.configImports.size}`;
this.configImports.add(`import * as ${configName} from '${route.config}';`);
return configName;
}
generateLoaderCode(route) {
const loaders = [];
if (route.data) {
const loaderName = `loader_${this.loaderImports.size}`;
this.loaderImports.add(`import { loader as ${loaderName}${route.action ? `, action as ${loaderName}_action` : ""} } from '${route.data}';`);
loaders.push(loaderName);
}
if (route.clientData) {
const clientDataName = `clientData_${this.loaderImports.size}`;
this.loaderImports.add(`import { loader as ${clientDataName} } from '${route.clientData}';`);
loaders.push(clientDataName);
}
if (route.loader) {
const loaderName = `loader_${this.loaderImports.size}`;
this.loaderImports.add(`import ${loaderName} from '${route.loader}';`);
loaders.push(loaderName);
}
if (0 === loaders.length) return "";
if (loaders.length > 1) return `async (...args) => {
const [${loaders.join(", ")}] = await Promise.all([${loaders.map((l)=>`${l}(...args)`).join(", ")}]);
return { ...${loaders.join(", ...")} };
}`;
return loaders[0];
}
wrapWithImports(routeCode) {
const runtimeImports = [
...Array.from(this.runtimeImports),
...Array.from(this.loaderImports),
...Array.from(this.configImports),
...Array.from(this.loadingImports),
...Array.from(this.errorImports)
];
return `
${runtimeImports.join("\n")}
${this.componentDeclarations.size ? Array.from(this.componentDeclarations).join("\n") : ""}
${routeCode}`;
}
constructor(options){
generator_define_property(this, "options", void 0);
generator_define_property(this, "runtimeImports", new Set([
"import loadable from '@loadable/component';"
]));
generator_define_property(this, "loaderImports", new Set());
generator_define_property(this, "loadingImports", new Set());
generator_define_property(this, "errorImports", new Set());
generator_define_property(this, "configImports", new Set());
generator_define_property(this, "componentDeclarations", new Set());
this.options = {
splitting: true,
...options
};
}
}
function pathParser(path) {
const normalizedPath = path.replace(/^\/+|\/+$/g, "");
const pathWithoutExt = normalizedPath.replace(/\.[jt]sx?$/, "");
const pathSegments = pathWithoutExt.split("/");
const params = [];
const processedSegments = pathSegments.filter((segment)=>!segment.startsWith("(") && !segment.endsWith(")") && !segment.startsWith("__")).map((segment)=>{
const optionalMatch = segment.match(/^\[([.\w]+)\$\]$/);
if (optionalMatch) {
const name = optionalMatch[1];
params.push({
name,
optional: true
});
return `:${name}?`;
}
if ("$" === segment) {
params.push({
name: "*",
optional: false
});
return "*";
}
const optionalBracketMatch = segment.match(/^\[\[([.\w]+)\]\]$/);
if (optionalBracketMatch) {
const name = optionalBracketMatch[1];
if (name.startsWith("...")) {
params.push({
name: "*",
optional: true
});
return "*?";
}
params.push({
name,
optional: true
});
return `:${name}?`;
}
const requiredMatch = segment.match(/^\[([.\w]+)\]$/);
if (requiredMatch) {
const name = requiredMatch[1];
if (name.startsWith("...")) {
params.push({
name: "*",
optional: false
});
return "*";
}
params.push({
name
});
return `:${name}`;
}
return segment;
});
if ("page" === processedSegments[processedSegments.length - 1]) processedSegments.pop();
return {
route: processedSegments.join("/"),
params
};
}
const GlobPattern = "**/page.{jsx,tsx}";
async function generateRouteType(options) {
const { routesTypeFile, routesDirectories = [] } = options;
const allFiles = [];
for (const route of routesDirectories){
const relatedFiles = await (0, __WEBPACK_EXTERNAL_MODULE_glob__.glob)(GlobPattern, {
cwd: route.path
});
allFiles.push(...relatedFiles.map((file)=>route.prefix ? __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(route.prefix, file) : file));
}
const routeTypes = allFiles.map((path)=>{
const { route, params } = pathParser(path);
return {
route: route.startsWith("/") ? route : `/${route}`,
params
};
});
const routeTypesContent = [
'declare module "@feoe/fs-router" {',
" interface RouteTypes {",
...routeTypes.map((routeType)=>` "${routeType.route}": {};`),
" }",
" };",
"export {};"
].join("\n");
__WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].writeFileSync(routesTypeFile, routeTypesContent);
}
async function generator_generator(config) {
const extractor = new RouteExtractor({
routesDir: config.routesDirectory,
alias: config.alias
});
const routes = await extractor.extract();
const generator = new RouteCodeGenerator({
splitting: config.splitting
});
const code = generator.generate(routes);
if (config.enableGeneration) {
await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__.mkdir(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.dirname(config.generatedRoutesPath), {
recursive: true
});
await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__.writeFile(config.generatedRoutesPath, code, "utf-8");
}
if (config.typeGenerateOptions) {
const { routesTypeFile, routesDirectories = [] } = config.typeGenerateOptions;
if (routesDirectories.length > 0) await generateRouteType({
routesTypeFile,
routesDirectories
});
}
return code;
}
const defaultConfig = {
routesDirectory: "src/routes",
generatedRoutesPath: "src/routes.tsx",
routeExtensions: [
".js",
".jsx",
".ts",
".tsx"
],
splitting: true,
alias: {
name: "@",
basename: "src"
},
enableGeneration: true,
defaultErrorBoundary: false,
typeGenerateOptions: {
routesTypeFile: "src/routes-type.ts",
generateRouteParams: true,
generateLoaderTypes: true,
routesDirectories: []
}
};
const getConfig = (options, root)=>{
const config = {
...defaultConfig,
...options
};
config.routesDirectory = (0, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.isAbsolute)(config.routesDirectory) ? config.routesDirectory : (0, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.resolve)(root, config.routesDirectory);
config.generatedRoutesPath = (0, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.isAbsolute)(config.generatedRoutesPath) ? config.generatedRoutesPath : (0, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.resolve)(root, config.generatedRoutesPath);
if (config.typeGenerateOptions) {
const { routesTypeFile } = config.typeGenerateOptions;
config.typeGenerateOptions.routesTypeFile = (0, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.isAbsolute)(routesTypeFile) ? routesTypeFile : (0, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.resolve)(root, routesTypeFile);
}
return config;
};
const PLUGIN_NAME = "unplugin:file-based-router-generator";
const unpluginRouterGeneratorFactory = (userOptions = {})=>{
const ctx = {
root: process.cwd(),
config: getConfig(userOptions, process.cwd()),
watcher: null,
lock: false,
generated: false
};
const getRoutesDirectoryPath = ()=>(0, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.isAbsolute)(ctx.config.routesDirectory) ? ctx.config.routesDirectory : (0, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.join)(ctx.root, ctx.config.routesDirectory);
const generate = async ()=>{
if (ctx.lock) return;
ctx.lock = true;
try {
const content = await generator_generator(ctx.config);
ctx.generated = true;
return content;
} catch (err) {
console.error(`❌ [${PLUGIN_NAME}] Route generation failed:`, err);
return "export const routes = [];";
} finally{
ctx.lock = false;
}
};
const run = async (cb)=>{
if (ctx.config.enableGeneration ?? true) await cb();
};
const handleFile = async (file, event)=>{
const filePath = (0, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.normalize)(file);
if ("update" === event && filePath === (0, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.resolve)(ctx.config.generatedRoutesPath)) return;
const routesDirectoryPath = getRoutesDirectoryPath();
const relative = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].relative(routesDirectoryPath, filePath);
const fileInRoutesDirectory = "" !== relative && !relative.startsWith("..");
if (fileInRoutesDirectory) await run(generate);
};
const setupWatcher = async ()=>{
const { watch } = await import("chokidar");
const routesDirectoryPath = getRoutesDirectoryPath();
const watchOptions = {
ignored: [
/(^|[\/\\])\../,
"node_modules",
"**/*.d.ts",
"**/styles/**",
"**/*.css",
"**/*.less",
"**/*.sass",
"**/*.scss"
],
ignoreInitial: true,
ignorePermissionErrors: true
};
ctx.watcher = watch(routesDirectoryPath, watchOptions);
const debounce = (fn, delay)=>{
let timeout;
return (...args)=>{
clearTimeout(timeout);
timeout = setTimeout(()=>fn(...args), delay);
};
};
const debouncedGenerate = debounce(()=>run(generate), 300);
ctx.watcher.on("add", debouncedGenerate).on("unlink", debouncedGenerate).on("change", debouncedGenerate).on("error", (error)=>{
console.error(`❌ [${PLUGIN_NAME}] Watcher error:`, error);
});
};
return {
name: PLUGIN_NAME,
buildStart () {
ctx.config = getConfig(userOptions, ctx.root);
if (!(0, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.isAbsolute)(ctx.config.routesDirectory)) ctx.config.routesDirectory = (0, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.resolve)(ctx.root, ctx.config.routesDirectory);
},
async watchChange (id, { event }) {
await run(async ()=>{
await handleFile(id, event);
});
},
vite: {
async configResolved (config) {
ctx.root = config.root;
ctx.config = getConfig(userOptions, ctx.root);
await run(generate);
},
configureServer () {
return ()=>{
console.info(`✅ [${PLUGIN_NAME}] Routes generated successfully`);
};
},
handleHotUpdate ({ file }) {
if (file.startsWith(ctx.config.routesDirectory)) return [];
}
},
webpack (compiler) {
if ("production" === compiler.options.mode) {
compiler.hooks.beforeRun.tapPromise(PLUGIN_NAME, async ()=>{
await run(generate);
});
compiler.hooks.done.tap(PLUGIN_NAME, ()=>{
console.info(`✅ ${PLUGIN_NAME}: Routes generated successfully`);
setTimeout(()=>{
process.exit(0);
});
});
} else {
setupWatcher();
let generated = false;
compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async ()=>{
if (!generated) {
generated = true;
return run(generate);
}
});
}
},
rspack (compiler) {
if ("production" === compiler.options.mode) {
compiler.hooks.beforeRun.tapPromise(PLUGIN_NAME, async ()=>{
await run(generate);
});
compiler.hooks.done.tap(PLUGIN_NAME, ()=>{
console.info(`✅ [${PLUGIN_NAME}] Routes generated successfully`);
});
} else {
setupWatcher();
let generated = false;
compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async ()=>{
if (!generated) {
generated = true;
return run(generate);
}
});
}
},
async buildEnd () {
if (ctx.watcher) {
await ctx.watcher.close();
ctx.watcher = null;
}
}
};
};
const FileBasedRouterRspack = (0, __WEBPACK_EXTERNAL_MODULE_unplugin__.createRspackPlugin)(unpluginRouterGeneratorFactory);
const rspack_rslib_entry_ = FileBasedRouterRspack;
export { FileBasedRouterRspack, rspack_rslib_entry_ as default };