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 | 6x 6x 6x 6x 6x 6x 7x 7x 81x 81x 7x 7x 81x 82x 7x 7x 7x 7x 7x 7x 6x 93x 93x 93x 93x 93x 6x 94x 94x 94x 94x 109x 109x 109x 109x 94x 94x 94x 94x 252x 252x 252x 109x 109x 109x 98x 98x 11x 143x 94x 94x 12x 31x 12x 6x 93x 93x | 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));
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);
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,
) {}
}
|