unplugin-remix-router
Version:
[](https://stand-with-palestine.org)
205 lines (196 loc) • 7.52 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
// src/index.ts
import { createUnplugin } from "unplugin";
// src/utils/is-file-exist.ts
import { promises as fs } from "node:fs";
async function isFileExist(filePath) {
try {
await fs.stat(filePath);
return true;
} catch (error) {
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
return false;
else
throw error;
}
}
// src/utils/list-files.ts
import { promises as fs2 } from "node:fs";
import path from "node:path";
import { normalizePath } from "vite";
async function listFiles(directory) {
const topLevelPattern = /\.tsx$/;
const subdirectoryPattern = /^route(\.lazy)?\.tsx$/;
try {
const files = await fs2.readdir(directory);
const matchingFiles = [];
for (const file of files) {
const filePath = path.join(directory, file);
const stat = await fs2.stat(filePath);
if (stat.isDirectory()) {
const subdirectoryFiles = await fs2.readdir(filePath);
const subdirectoryFile = subdirectoryFiles.find((subItem) => subdirectoryPattern.test(subItem));
if (!subdirectoryFile)
continue;
const subFilePath = path.join(filePath, subdirectoryFile);
matchingFiles.push(normalizePath(subFilePath).replace(normalizePath(`${directory}/`), ""));
} else if (topLevelPattern.test(file)) {
matchingFiles.push(normalizePath(filePath).replace(normalizePath(`${directory}/`), ""));
}
}
return matchingFiles;
} catch (error) {
console.error("Error:", error);
throw error;
}
}
// src/utils/build-route-maps.ts
function buildRoutesMap(strings, appDirectory, lazyMode = "suffix", level = 0) {
const result = [];
let intenalImports = "";
const firstSegments = new Set(
strings.map((str) => str.split(".")[level].replace("/route", "")).filter((str) => str !== void 0 && str !== "tsx" && str !== "lazy")
);
if (firstSegments.size === 0)
return { routesMap: result };
const reversedSegments = Array.from(firstSegments).reverse();
for (const segment of reversedSegments) {
const filteredStrings = strings.filter((str) => str.split(".")[level] === segment || str.split(".")[level] === `${segment}/route`);
const routePath = segment.replace(/\(([^)]*)\)\??$/, "$1?").replace(/\$+$/, "*").replace(/^\$/, ":");
if (filteredStrings.length === 0)
continue;
const newNode = {};
if (routePath === "_index")
newNode.index = true;
else if (!routePath.startsWith("_"))
newNode.path = routePath;
const page = filteredStrings.find(
(str) => str.endsWith(`${segment}.tsx`) || str.endsWith(`${segment}/route.tsx`)
);
const lazyPage = filteredStrings.find(
(str) => str.endsWith(`${segment}.lazy.tsx`) || str.endsWith(`${segment}/route.lazy.tsx`)
);
if (lazyMode === "always" && page) {
const absolutePath = `${appDirectory}/routes/${page}`;
newNode.lazy = `ImportStart'${absolutePath}'ImportEnd`;
} else if (lazyPage) {
const absolutePath = `${appDirectory}/routes/${lazyPage}`;
newNode.lazy = `ImportStart'${absolutePath}'ImportEnd`;
} else if (page) {
const random = Math.floor(Math.random() * 1e5 + 1);
const absolutePath = `${appDirectory}/routes/${page}`;
intenalImports += `import * as route${random} from '${absolutePath}'
`;
newNode.spread = `SpreadStartroute${random}SpreadEnd`;
}
const { routesMap, imports } = buildRoutesMap(filteredStrings, appDirectory, lazyMode, level + 1);
if (routesMap.length > 0)
newNode.children = routesMap;
if (imports)
intenalImports += imports;
if (segment.endsWith("_")) {
const slicedSegment = segment.slice(0, -1);
routesMap.forEach((node) => {
result.push(__spreadProps(__spreadValues({}, node), { path: node.index ? slicedSegment : `${slicedSegment}/${node.path}` }));
});
} else {
result.push(newNode);
}
}
return { routesMap: result, imports: intenalImports };
}
// src/index.ts
var server;
function routesCode(imports, routesObject) {
return `
${imports}
function moduleFactory(module) {
const { default: Component, clientLoader: loader, clientAction: action, clientMiddleware: middleware, loader: _loader, action: _action, Component: _Component, middleware: _middleware, unstable_middleware: _unstable_middleware, ...rest } = module;
return { Component, loader, action, middleware, unstable_middleware: middleware, ...rest };
}
export const routes = ${routesObject}
`;
}
function invalidateVirtualModule(server2) {
const { moduleGraph, ws } = server2;
const module = moduleGraph.getModuleById("virtual:routes");
if (module) {
moduleGraph.invalidateModule(module);
if (ws) {
ws.send({
type: "full-reload",
path: "*"
});
}
}
}
var unpluginFactory = (options) => ({
name: "unplugin-remix-router",
async resolveId(source) {
if (source === "virtual:routes")
return source;
},
async load(id) {
const appDirectory = (options == null ? void 0 : options.appDirectory) ? options.appDirectory : "./app";
const lazyMode = (options == null ? void 0 : options.lazy) || "always";
if (id === "virtual:routes") {
const files = await listFiles(`${appDirectory}/routes`);
let { routesMap, imports } = buildRoutesMap(files, appDirectory, lazyMode);
if (await isFileExist(`${appDirectory}/root.lazy.tsx`)) {
const absolutePath = `${appDirectory}/root.lazy.tsx`;
routesMap = [{
path: "/",
lazy: `ImportStart'${absolutePath}'ImportEnd`,
children: routesMap
}];
} else if (await isFileExist(`${appDirectory}/root.tsx`)) {
const absolutePath = `${appDirectory}/root.tsx`;
imports += `import * as root from '${absolutePath}'
`;
routesMap = [{
path: "/",
spread: `SpreadStartrootSpreadEnd`,
children: routesMap
}];
}
const routesObject = JSON.stringify(routesMap).replace(/"ImportStart/g, "() => import(").replace(/ImportEnd"/g, ").then(moduleFactory)").replace(/"spread":"SpreadStart/g, "...moduleFactory(").replace(/SpreadEnd"/g, ")");
return routesCode(imports, routesObject);
}
},
vite: {
configureServer(_server) {
server = _server;
},
watchChange(id, change) {
if (change.event === "update")
return;
invalidateVirtualModule(server);
}
}
});
var unplugin = /* @__PURE__ */ createUnplugin(unpluginFactory);
var index_default = unplugin;
export {
invalidateVirtualModule,
unpluginFactory,
unplugin,
index_default
};