diff --git a/goodrouter-ts/.vscode/launch.json b/goodrouter-ts/.vscode/launch.json index c9e3949..05f589e 100644 --- a/goodrouter-ts/.vscode/launch.json +++ b/goodrouter-ts/.vscode/launch.json @@ -7,10 +7,11 @@ "name": "run file", "program": "${file}", "args": [], - "cwd": "${workspaceRoot}", + "cwd": "${workspaceFolder}", "sourceMaps": true, - "outFiles": ["${workspaceRoot}/out/**/*.js"], - "preLaunchTask": "watch" + "outFiles": ["${workspaceFolder}/out/**/*.js"], + "preLaunchTask": "watch", + "outputCapture": "std" } ] } diff --git a/goodrouter-ts/.vscode/tasks.json b/goodrouter-ts/.vscode/tasks.json index e6d46d1..b5c70c4 100644 --- a/goodrouter-ts/.vscode/tasks.json +++ b/goodrouter-ts/.vscode/tasks.json @@ -2,10 +2,10 @@ "version": "2.0.0", "tasks": [ { - "type": "typescript", + "type": "npm", "label": "watch", - "tsconfig": "tsconfig.json", - "option": "watch", + "script": "watch", + "isBackground": true, "problemMatcher": ["$tsc-watch"], "group": { "kind": "build", diff --git a/goodrouter-ts/package.json b/goodrouter-ts/package.json index d67931d..9d3c4f7 100644 --- a/goodrouter-ts/package.json +++ b/goodrouter-ts/package.json @@ -11,6 +11,7 @@ ], "scripts": { "build": "tsc", + "watch": "tsc --watch", "clean": "rm -rf out", "test": "npm run spec-all", "lint": "prettier --check *", diff --git a/goodrouter-ts/src/main.ts b/goodrouter-ts/src/main.ts index d4ccb1f..9a268b3 100644 --- a/goodrouter-ts/src/main.ts +++ b/goodrouter-ts/src/main.ts @@ -1,2 +1,3 @@ +export * from "./route-node-json.js"; export * from "./router-options.js"; export * from "./router.js"; diff --git a/goodrouter-ts/src/route-node-json.ts b/goodrouter-ts/src/route-node-json.ts new file mode 100644 index 0000000..86da3cc --- /dev/null +++ b/goodrouter-ts/src/route-node-json.ts @@ -0,0 +1,6 @@ +export interface RouteNodeJson { + anchor: string; + hasParameter: boolean; + routeKey: K | null; + children: RouteNodeJson[]; +} diff --git a/goodrouter-ts/src/route-node.spec.ts b/goodrouter-ts/src/route-node.spec.ts index 7117d12..9189dee 100644 --- a/goodrouter-ts/src/route-node.spec.ts +++ b/goodrouter-ts/src/route-node.spec.ts @@ -1,6 +1,7 @@ import { permutations } from "itertools"; import assert from "node:assert/strict"; import test from "node:test"; +import { RouteNodeJson } from "./route-node-json.js"; import { RouteNode } from "./route-node.js"; import { defaultRouterOptions } from "./router-options.js"; import { parseTemplatePairs } from "./template.js"; @@ -60,3 +61,47 @@ test("route-node-sort", () => { assert.deepEqual(nodesActual, nodesExpected); }); + +test("route-node-json", () => { + const node = new RouteNode(); + node.insert(1, [ + ...parseTemplatePairs( + "x/y", + defaultRouterOptions.parameterPlaceholderRE, + ), + ]); + node.insert(2, [ + ...parseTemplatePairs( + "x/z", + defaultRouterOptions.parameterPlaceholderRE, + ), + ]); + const actual = node.toJSON(); + const expected: RouteNodeJson = { + anchor: "", + hasParameter: false, + routeKey: null, + children: [ + { + anchor: "x/", + hasParameter: false, + routeKey: null, + children: [ + { + anchor: "y", + hasParameter: false, + routeKey: 1, + children: [], + }, + { + anchor: "z", + hasParameter: false, + routeKey: 2, + children: [], + }, + ], + }, + ], + }; + assert.deepEqual(actual, expected); +}); diff --git a/goodrouter-ts/src/route-node.ts b/goodrouter-ts/src/route-node.ts index eba75dc..86a5ffd 100644 --- a/goodrouter-ts/src/route-node.ts +++ b/goodrouter-ts/src/route-node.ts @@ -1,3 +1,4 @@ +import { RouteNodeJson } from "./route-node-json.js"; import { findCommonPrefixLength } from "./utils/string.js"; /** @@ -22,14 +23,13 @@ export class RouteNode { * key that identifies the route, if this is a leaf node for the route */ public routeKey: K | null = null, + /** + * @description + * children that represent the rest of the path that needs to be matched + */ + private readonly children = new Array>(), ) {} - /** - * @description - * children that represent the rest of the path that needs to be matched - */ - private readonly children = new Array>(); - private addChild(childNode: RouteNode) { this.children.push(childNode); } @@ -323,4 +323,26 @@ export class RouteNode { return 0; } + + public toJSON(): RouteNodeJson { + const json = { + anchor: this.anchor, + hasParameter: this.hasParameter, + routeKey: this.routeKey, + children: this.children.map((child) => child.toJSON()), + }; + return json; + } + + public static fromJSON( + json: RouteNodeJson, + ): RouteNode { + const node = new RouteNode( + json.anchor, + json.hasParameter, + json.routeKey, + json.children.map((child) => RouteNode.fromJSON(child)), + ); + return node; + } } diff --git a/goodrouter-ts/src/router.ts b/goodrouter-ts/src/router.ts index f4484ba..b881836 100644 --- a/goodrouter-ts/src/router.ts +++ b/goodrouter-ts/src/router.ts @@ -1,3 +1,4 @@ +import { RouteNodeJson } from "./route-node-json.js"; import { RouteNode } from "./route-node.js"; import { defaultRouterOptions, RouterOptions } from "./router-options.js"; import { parseTemplatePairs } from "./template.js"; @@ -154,4 +155,15 @@ export class Router { } return result; } + + public saveToJSON(): RouteNodeJson { + const json = this.rootNode.toJSON(); + return json; + } + + public loadFromJSON(json: RouteNodeJson) { + const node = RouteNode.fromJSON(json); + this.rootNode = node; + return this; + } } diff --git a/goodrouter-ts/tsconfig.json b/goodrouter-ts/tsconfig.json index 91fc8b4..afbfd87 100644 --- a/goodrouter-ts/tsconfig.json +++ b/goodrouter-ts/tsconfig.json @@ -2,7 +2,9 @@ "extends": "@tsconfig/node20", "compilerOptions": { "outDir": "./out", - "rootDir": "./src" + "rootDir": "./src", + "sourceMap": true, + "declaration": true }, "include": ["src/**/*"] }