feat: init
This commit is contained in:
140
node_modules/rou3/dist/compiler.mjs
generated
vendored
Normal file
140
node_modules/rou3/dist/compiler.mjs
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* Compiles the router instance into a faster route-matching function.
|
||||
*
|
||||
* **IMPORTANT:** `compileRouter` requires eval support with `new Function()` in the runtime for JIT compilation.
|
||||
*
|
||||
* @example
|
||||
* import { createRouter, addRoute } from "rou3";
|
||||
* import { compileRouter } from "rou3/compiler";
|
||||
* const router = createRouter();
|
||||
* // [add some routes]
|
||||
* const findRoute = compileRouter(router);
|
||||
* const matchAll = compileRouter(router, { matchAll: true });
|
||||
* findRoute("GET", "/path/foo/bar");
|
||||
*
|
||||
* @param router - The router context to compile.
|
||||
*/
|
||||
function compileRouter(router, opts) {
|
||||
const ctx = {
|
||||
opts: opts || {},
|
||||
router,
|
||||
data: []
|
||||
};
|
||||
const compiled = compileRouteMatch(ctx);
|
||||
return new Function(...ctx.data.map((_, i) => `$${i}`), `return(m,p)=>{${compiled}}`)(...ctx.data);
|
||||
}
|
||||
/**
|
||||
* Compile the router instance into a compact runnable code.
|
||||
*
|
||||
* **IMPORTANT:** Route data must be serializable to JSON (i.e., no functions or classes) or implement the `toJSON()` method to render custom code or you can pass custom `serialize` function in options.
|
||||
*
|
||||
* @example
|
||||
* import { createRouter, addRoute } from "rou3";
|
||||
* import { compileRouterToString } from "rou3/compiler";
|
||||
* const router = createRouter();
|
||||
* // [add some routes with serializable data]
|
||||
* const compilerCode = compileRouterToString(router, "findRoute");
|
||||
* // "const findRoute=(m, p) => {}"
|
||||
*/
|
||||
function compileRouterToString(router, functionName, opts) {
|
||||
const ctx = {
|
||||
opts: opts || {},
|
||||
router,
|
||||
data: [],
|
||||
compileToString: true
|
||||
};
|
||||
let compiled = `(m,p)=>{${compileRouteMatch(ctx)}}`;
|
||||
if (ctx.data.length > 0) compiled = `/* @__PURE__ */ (() => { ${`const ${ctx.data.map((v, i) => `$${i}=${v}`).join(",")};`}; return ${compiled}})()`;
|
||||
return functionName ? `const ${functionName}=${compiled};` : compiled;
|
||||
}
|
||||
function compileRouteMatch(ctx) {
|
||||
let code = "";
|
||||
const staticNodes = /* @__PURE__ */ new Set();
|
||||
for (const key in ctx.router.static) {
|
||||
const node = ctx.router.static[key];
|
||||
if (node?.methods) {
|
||||
staticNodes.add(node);
|
||||
code += `if(p===${JSON.stringify(key.replace(/\/$/, "") || "/")}){${compileMethodMatch(ctx, node.methods, [], -1)}}`;
|
||||
}
|
||||
}
|
||||
const match = compileNode(ctx, ctx.router.root, [], 0, staticNodes);
|
||||
if (match) code += `let s=p.split("/"),l=s.length-1;${match}`;
|
||||
if (!code) return ctx.opts?.matchAll ? `return [];` : "";
|
||||
return `${ctx.opts?.matchAll ? `let r=[];` : ""}if(p.charCodeAt(p.length-1)===47)p=p.slice(0,-1)||"/";${code}${ctx.opts?.matchAll ? "return r;" : ""}`;
|
||||
}
|
||||
function compileMethodMatch(ctx, methods, params, currentIdx) {
|
||||
let code = "";
|
||||
for (const key in methods) {
|
||||
const matchers = methods[key];
|
||||
if (matchers && matchers?.length > 0) {
|
||||
if (key !== "") code += `if(m==="${key}")${matchers.length > 1 ? "{" : ""}`;
|
||||
const _matchers = matchers.map((m) => compileFinalMatch(ctx, m, currentIdx, params)).sort((a, b) => b.weight - a.weight);
|
||||
for (const matcher of _matchers) code += matcher.code;
|
||||
if (key !== "") code += matchers.length > 1 ? "}" : "";
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
function compileFinalMatch(ctx, data, currentIdx, params) {
|
||||
let ret = `{data:${serializeData(ctx, data.data)}`;
|
||||
const conditions = [];
|
||||
const { paramsMap, paramsRegexp } = data;
|
||||
if (paramsMap && paramsMap.length > 0) {
|
||||
if (!paramsMap[paramsMap.length - 1][2] && currentIdx !== -1) conditions.push(`l>=${currentIdx}`);
|
||||
for (let i = 0; i < paramsRegexp.length; i++) {
|
||||
const regexp = paramsRegexp[i];
|
||||
if (!regexp) continue;
|
||||
conditions.push(`${regexp.toString()}.test(s[${i + 1}])`);
|
||||
}
|
||||
ret += ",params:{";
|
||||
for (let i = 0; i < paramsMap.length; i++) {
|
||||
const map = paramsMap[i];
|
||||
ret += typeof map[1] === "string" ? `${JSON.stringify(map[1])}:${params[i]},` : `...(${map[1].toString()}.exec(${params[i]}))?.groups,`;
|
||||
}
|
||||
ret += "}";
|
||||
}
|
||||
return {
|
||||
code: (conditions.length > 0 ? `if(${conditions.join("&&")})` : "") + (ctx.opts?.matchAll ? `r.unshift(${ret}});` : `return ${ret}};`),
|
||||
weight: conditions.length
|
||||
};
|
||||
}
|
||||
function compileNode(ctx, node, params, startIdx, staticNodes) {
|
||||
let code = "";
|
||||
if (node.methods && !staticNodes.has(node)) {
|
||||
const match = compileMethodMatch(ctx, node.methods, params, node.key === "*" ? startIdx : -1);
|
||||
if (match) {
|
||||
const hasLastOptionalParam = node.key === "*";
|
||||
code += `if(l===${startIdx}${hasLastOptionalParam ? `||l===${startIdx - 1}` : ""}){${match}}`;
|
||||
}
|
||||
}
|
||||
if (node.static) for (const key in node.static) {
|
||||
const match = compileNode(ctx, node.static[key], params, startIdx + 1, staticNodes);
|
||||
if (match) code += `if(s[${startIdx + 1}]===${JSON.stringify(key)}){${match}}`;
|
||||
}
|
||||
if (node.param) {
|
||||
const match = compileNode(ctx, node.param, [...params, `s[${startIdx + 1}]`], startIdx + 1, staticNodes);
|
||||
if (match) code += match;
|
||||
}
|
||||
if (node.wildcard) {
|
||||
const { wildcard } = node;
|
||||
if (wildcard.static || wildcard.param || wildcard.wildcard) throw new Error("Compiler mode does not support patterns after wildcard");
|
||||
if (wildcard.methods) {
|
||||
const match = compileMethodMatch(ctx, wildcard.methods, [...params, `s.slice(${startIdx + 1}).join('/')`], startIdx);
|
||||
if (match) code += match;
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
function serializeData(ctx, value) {
|
||||
if (ctx.compileToString) if (ctx.opts?.serialize) value = ctx.opts.serialize(value);
|
||||
else if (typeof value?.toJSON === "function") value = value.toJSON();
|
||||
else value = JSON.stringify(value);
|
||||
let index = ctx.data.indexOf(value);
|
||||
if (index === -1) {
|
||||
ctx.data.push(value);
|
||||
index = ctx.data.length - 1;
|
||||
}
|
||||
return `$${index}`;
|
||||
}
|
||||
|
||||
export { compileRouter, compileRouterToString };
|
||||
Reference in New Issue
Block a user