Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | 8x 8x 8x 8x 8x 8x 26x 26x 1971x 1971x 26x 26x 1971x 1973x 26x 26x 26x 26x 26x 26x 8x 1984x 1984x 1984x 1984x 1984x 8x 1989x 1989x 1989x 1989x 1545x 1545x 1545x 1545x 1989x 1989x 1989x 1989x 3988x 3988x 3988x 1545x 1545x 1545x 1524x 1524x 21x 2443x 1989x 1989x 13x 35x 13x 8x 1984x 1984x | import {JsExpression} from '@jsonjoy.com/codegen/lib/util/JsExpression';
import {printTree} from 'sonic-forest/lib/print/printTree';
import type {Printable} from 'sonic-forest/lib/print/types';
import {type RouteMatcher, RouterCodegenCtx, RouterCodegenOpts} from './codegen';
import {ExactStep, RegexStep, UntilStep} from './steps';
import {RoutingTreeNode} from './tree';
import type {Step} from './types';
export interface RouterOptions {
defaultUntil?: string;
}
export class Router<Data = unknown> implements Printable {
public readonly destinations: Destination[] = [];
constructor(public readonly options: RouterOptions = {}) {}
public add(route: string | string[], data: Data) {
const destination = Destination.from(route, data, this.options.defaultUntil);
this.destinations.push(destination);
}
public addDestination(destination: Destination) {
this.destinations.push(destination);
}
public tree(): RoutingTreeNode {
const tree = new RoutingTreeNode();
for (const destination of this.destinations) {
for (const route of destination.routes) {
tree.add(route, 0, destination);
}
}
return tree;
}
public compile(): RouteMatcher<Data> {
const ctx = new RouterCodegenCtx();
const node = new RouterCodegenOpts(new JsExpression(() => 'str'), '0');
const tree = this.tree();
tree.codegen(ctx, node);
return ctx.codegen.compile() as RouteMatcher<Data>;
}
public toString(tab: string = '') {
return (
`${this.constructor.name}` +
printTree(tab, [
(tab) =>
'Destinations' +
printTree(
tab,
this.destinations.map((d, i) => (tab) => `[${i}]: ` + d.toString(tab + ' ')),
),
() => '',
(tab) => 'RoutingTree' + printTree(tab, [(tab) => this.tree().toString(tab)]),
])
);
}
}
export class Destination implements Printable {
public static from(def: string | string[], data: unknown, defaultUntil?: string): Destination {
const routes =
typeof def === 'string' ? [Route.from(def, defaultUntil)] : def.map((r) => Route.from(r, defaultUntil));
return new Destination(routes, data);
}
public readonly match: Match;
constructor(
public readonly routes: Route[],
public readonly data: unknown,
) {
this.match = new Match(data, []);
}
public toString(tab: string = '') {
return (
`${this.constructor.name} ` +
(this.routes.length === 1
? this.routes[0].toString(tab)
: printTree(
tab,
this.routes.map((r) => (tab) => r.toString(tab)),
))
);
}
}
export class Route implements Printable {
public static from(str: string, defaultUntil = '/'): Route {
const tokens: string[] = [];
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: Step[] = [];
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 UntilStep(name, until || (next ? next[0] : defaultUntil)));
} else {
steps.push(new RegexStep(name, regex, until));
}
} else {
steps.push(new ExactStep(token));
}
}
return new Route(steps);
}
constructor(public readonly steps: Step[]) {}
public toText() {
let str = '';
for (const step in this.steps) str += this.steps[step].toText();
return str;
}
public toString(tab: string = '') {
return this.toText();
}
}
export class Match<Data = unknown> {
constructor(
public readonly data: Data,
public params: string[] | null,
) {}
}
|