@generouted/react-router
Version:
Generated file-based routes for React Router and Vite
143 lines (129 loc) • 5.88 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/plugin/index.ts
var plugin_exports = {};
__export(plugin_exports, {
default: () => Generouted
});
module.exports = __toCommonJS(plugin_exports);
var import_path2 = __toESM(require("path"), 1);
// src/plugin/generate.ts
var import_child_process = require("child_process");
var import_fs = __toESM(require("fs"), 1);
var import_path = __toESM(require("path"), 1);
var import_vite = require("vite");
// ../../shared/core/src/index.ts
var patterns = {
route: [/^.*\/src\/pages\/|^\/pages\/|\.(jsx|tsx|mdx)$/g, ""],
splat: [/\[\.{3}\w+\]/g, "*"],
param: [/\[([^\]]+)\]/g, ":$1"],
slash: [/^index$|\./g, "/"],
optional: [/^-(:?[\w-]+|\*)/, "$1?"]
};
// src/plugin/generate.ts
var import_fast_glob = __toESM(require("fast-glob"), 1);
// src/plugin/template.ts
var template = `// Generouted, changes to this file will be overriden
/* eslint-disable */
import { components, hooks, utils } from '@generouted/react-router/client'
// types
export const { Link, Navigate } = components<Path, Params>()
export const { useModals, useNavigate, useParams } = hooks<Path, Params, ModalPath>()
export const { redirect } = utils<Path, Params>()
`;
// src/plugin/generate.ts
var generateRouteTypes = async (options) => {
const files = await (0, import_fast_glob.default)(options.source.routes || "./src/pages/**/[\\w[-]*.{jsx,tsx,mdx}", { onlyFiles: true });
const modal = await (0, import_fast_glob.default)(options.source.modals || "./src/pages/**/[+]*.{jsx,tsx,mdx}", { onlyFiles: true });
const filtered = files.filter((key) => !key.includes("/_") && !key.includes("/404"));
const params = [];
const paths = filtered.map((key) => {
const path3 = key.replace(...patterns.route).replace(...patterns.splat).replace(...patterns.param).replace(/\([\w-]+\)\/|\/?_layout/g, "").replace(/\/?index|\./g, "/").replace(/(\w)\/$/g, "$1").split("/").map((segment) => segment.replace(...patterns.optional)).join("/");
if (path3) {
const param = path3.split("/").filter((segment) => segment.startsWith(":"));
if (param.length || path3.includes("*")) {
const dynamic = param.length ? param.map((p) => p.replace(/:(.+)(\?)?/, "$1$2:") + " string") : [];
const splat = path3.includes("*") ? ["'*': string"] : [];
params.push(`'/${path3}': { ${[...dynamic, ...splat].join("; ")} }`);
}
return path3.length > 1 ? `/${path3}` : path3;
}
});
const modals = modal.map(
(path3) => `/${path3.replace(...patterns.route).replace(/\+|\([\w-]+\)\//g, "").replace(/(\/)?index/g, "").replace(/\./g, "/")}`
);
const types = `export type Path =
| "${[...new Set(paths.filter(Boolean))].sort().join('"\n | "')}"`.replace(/"/g, "`") + `
export type Params = {
${params.sort().join("\n ")}
}
` + `export type ModalPath = "${modals.sort().join('" | "') || "never"}"`.replace(/"/g, modals.length ? "`" : "");
const content = template.replace("// types", types);
const count = paths.length + modals.length;
return { content, count };
};
var logger = (0, import_vite.createLogger)("info", { prefix: "[generouted]" });
var latestContent = "";
var generate = async (options) => {
const start = Date.now();
const { content, count } = await generateRouteTypes(options);
logger.info(`scanned ${count} routes in ${Date.now() - start} ms`, { timestamp: true });
if (latestContent === content)
return;
latestContent = content;
await import_fs.default.promises.writeFile(options.output, content);
if (!options.format)
return;
const prettier = import_path.default.resolve("./node_modules/.bin/prettier");
if (import_fs.default.existsSync(prettier))
(0, import_child_process.execSync)(`${prettier} --write --cache ${options.output}`);
};
// src/plugin/options.ts
var defaultOptions = {
source: { routes: "./src/pages/**/[\\w[-]*.{jsx,tsx,mdx}", modals: "./src/pages/**/[+]*.{jsx,tsx,mdx}" },
output: "./src/router.ts",
format: true
};
// src/plugin/index.ts
function Generouted(options) {
const resolvedOptions = { ...defaultOptions, ...options };
return {
name: "generouted/react-router",
enforce: "pre",
configureServer(server) {
const listener = (file = "") => file.includes(import_path2.default.normalize("/src/pages/")) ? generate(resolvedOptions) : null;
server.watcher.on("add", listener);
server.watcher.on("change", listener);
server.watcher.on("unlink", listener);
},
buildStart() {
return generate(resolvedOptions);
}
};
}