@jsonjoy.com/jit-router
Version:
High-performance HTTP router with JIT compilation.
123 lines (122 loc) • 4.28 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Match = exports.Route = exports.Destination = exports.Router = void 0;
const JsExpression_1 = require("@jsonjoy.com/codegen/lib/util/JsExpression");
const printTree_1 = require("sonic-forest/lib/print/printTree");
const codegen_1 = require("./codegen");
const steps_1 = require("./steps");
const tree_1 = require("./tree");
class Router {
constructor(options = {}) {
this.options = options;
this.destinations = [];
}
add(route, data) {
const destination = Destination.from(route, data, this.options.defaultUntil);
this.destinations.push(destination);
}
addDestination(destination) {
this.destinations.push(destination);
}
tree() {
const tree = new tree_1.RoutingTreeNode();
for (const destination of this.destinations) {
for (const route of destination.routes) {
tree.add(route, 0, destination);
}
}
return tree;
}
compile() {
const ctx = new codegen_1.RouterCodegenCtx();
const node = new codegen_1.RouterCodegenOpts(new JsExpression_1.JsExpression(() => 'str'), '0');
const tree = this.tree();
tree.codegen(ctx, node);
return ctx.codegen.compile();
}
toString(tab = '') {
return (`${this.constructor.name}` +
(0, printTree_1.printTree)(tab, [
(tab) => 'Destinations' +
(0, printTree_1.printTree)(tab, this.destinations.map((d, i) => (tab) => `[${i}]: ` + d.toString(tab + ' '))),
() => '',
(tab) => 'RoutingTree' + (0, printTree_1.printTree)(tab, [(tab) => this.tree().toString(tab)]),
]));
}
}
exports.Router = Router;
class Destination {
static from(def, data, defaultUntil) {
const routes = typeof def === 'string' ? [Route.from(def, defaultUntil)] : def.map((r) => Route.from(r, defaultUntil));
return new Destination(routes, data);
}
constructor(routes, data) {
this.routes = routes;
this.data = data;
this.match = new Match(data, []);
}
toString(tab = '') {
return (`${this.constructor.name} ` +
(this.routes.length === 1
? this.routes[0].toString(tab)
: (0, printTree_1.printTree)(tab, this.routes.map((r) => (tab) => r.toString(tab)))));
}
}
exports.Destination = Destination;
class Route {
static from(str, defaultUntil = '/') {
const tokens = [];
const matches = str.match(/\{[^\}]*\}/g);
let i = 0;
for (const match of matches ?? []) {
const index = str.indexOf(match, i);
if (index > i)
tokens.push(str.substring(i, index));
tokens.push(match);
i = index + match.length;
}
if (i < str.length)
tokens.push(str.substring(i));
const steps = [];
const length = tokens.length;
for (let i = 0; i < length; i++) {
const token = tokens[i];
const isParameter = token.startsWith('{') && token.endsWith('}');
if (isParameter) {
const content = token.substring(1, token.length - 1);
const [name = '', regex = '', until = ''] = content.split(':');
if (!regex || regex === '*') {
const next = tokens[i + 1];
steps.push(new steps_1.UntilStep(name, until || (next ? next[0] : defaultUntil)));
}
else {
steps.push(new steps_1.RegexStep(name, regex, until));
}
}
else {
steps.push(new steps_1.ExactStep(token));
}
}
return new Route(steps);
}
constructor(steps) {
this.steps = steps;
}
toText() {
let str = '';
for (const step in this.steps)
str += this.steps[step].toText();
return str;
}
toString(tab = '') {
return this.toText();
}
}
exports.Route = Route;
class Match {
constructor(data, params) {
this.data = data;
this.params = params;
}
}
exports.Match = Match;