From a7da9b91114ebefd757f568c977871befc8184d0 Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 1 Feb 2024 06:49:10 +0800 Subject: [PATCH] Explicitly support using any types as names --- lib/dep_graph.js | 14 +++++++- lib/index.d.ts | 63 ++++++++++++++++---------------- specs/dep_graph_spec.js | 79 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 31 deletions(-) diff --git a/lib/dep_graph.js b/lib/dep_graph.js index 9444532..7c42511 100644 --- a/lib/dep_graph.js +++ b/lib/dep_graph.js @@ -65,6 +65,18 @@ function createDFS(edges, leavesOnly, result, circular) { }; } +function toString(value) { + if (typeof value === "string") { + return value; + } + + if (typeof value === "function") { + return value.name; + } + + return String(value); +} + /** * Simple Dependency Graph */ @@ -344,7 +356,7 @@ DepGraph.prototype.dependentsOf = DepGraph.prototype.dependantsOf; * Cycle error, including the path of the cycle. */ var DepGraphCycleError = (exports.DepGraphCycleError = function (cyclePath) { - var message = "Dependency Cycle Found: " + cyclePath.join(" -> "); + var message = "Dependency Cycle Found: " + cyclePath.map(toString).join(" -> "); var instance = new Error(message); instance.cyclePath = cyclePath; Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); diff --git a/lib/index.d.ts b/lib/index.d.ts index 6ed8de5..f1b60cb 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -3,7 +3,10 @@ declare module 'dependency-graph' { circular?: boolean; } - export class DepGraph { + /** + * @template K + */ + export class DepGraph { /** * Creates an instance of DepGraph with optional Options. */ @@ -16,49 +19,49 @@ declare module 'dependency-graph' { /** * Add a node in the graph with optional data. If data is not given, name will be used as data. - * @param {string} name + * @param {K} name * @param data */ - addNode(name: string, data?: T): void; + addNode(name: K, data?: T): void; /** * Remove a node from the graph. - * @param {string} name + * @param {K} name */ - removeNode(name: string): void; + removeNode(name: K): void; /** * Check if a node exists in the graph. - * @param {string} name + * @param {K} name */ - hasNode(name: string): boolean; + hasNode(name: K): boolean; /** * Get the data associated with a node (will throw an Error if the node does not exist). - * @param {string} name + * @param {K} name */ - getNodeData(name: string): T; + getNodeData(name: K): T; /** * Set the data for an existing node (will throw an Error if the node does not exist). - * @param {string} name + * @param {K} name * @param data */ - setNodeData(name: string, data?: T): void; + setNodeData(name: K, data?: T): void; /** * Add a dependency between two nodes (will throw an Error if one of the nodes does not exist). - * @param {string} from - * @param {string} to + * @param {K} from + * @param {K} to */ - addDependency(from: string, to: string): void; + addDependency(from: K, to: K): void; /** * Remove a dependency between two nodes. - * @param {string} from - * @param {string} to + * @param {K} from + * @param {K} to */ - removeDependency(from: string, to: string): void; + removeDependency(from: K, to: K): void; /** * Return a clone of the dependency graph (If any custom data is attached @@ -70,35 +73,35 @@ declare module 'dependency-graph' { * Get an array containing the direct dependency nodes of the specified node. * @param name */ - directDependenciesOf(name: string): string[]; + directDependenciesOf(name: K): K[]; /** * Get an array containing the nodes that directly depend on the specified node. * @param name */ - directDependantsOf(name: string): string[]; + directDependantsOf(name: K): K[]; /** * Alias of `directDependantsOf` * * @see directDependantsOf - * @param {string} name + * @param {K} name */ - directDependentsOf(name: string): string[]; + directDependentsOf(name: K): K[]; /** * Get an array containing the nodes that the specified node depends on (transitively). If leavesOnly is true, only nodes that do not depend on any other nodes will be returned in the array. - * @param {string} name + * @param {K} name * @param {boolean} leavesOnly */ - dependenciesOf(name: string, leavesOnly?: boolean): string[]; + dependenciesOf(name: K, leavesOnly?: boolean): K[]; /** * Get an array containing the nodes that depend on the specified node (transitively). If leavesOnly is true, only nodes that do not have any dependants will be returned in the array. - * @param {string} name + * @param {K} name * @param {boolean} leavesOnly */ - dependantsOf(name: string, leavesOnly?: boolean): string[]; + dependantsOf(name: K, leavesOnly?: boolean): K[]; /** * Alias of `dependantsOf` @@ -107,21 +110,21 @@ declare module 'dependency-graph' { * @param name * @param leavesOnly */ - dependentsOf(name: string, leavesOnly?: boolean): string[]; + dependentsOf(name: K, leavesOnly?: boolean): K[]; /** * Get an array of nodes that have no dependants (i.e. nothing depends on them). */ - entryNodes(): string[]; + entryNodes(): K[]; /** * Construct the overall processing order for the dependency graph. If leavesOnly is true, only nodes that do not depend on any other nodes will be returned. * @param {boolean} leavesOnly */ - overallOrder(leavesOnly?: boolean): string[]; + overallOrder(leavesOnly?: boolean): K[]; } - export class DepGraphCycleError extends Error { - cyclePath: string[]; + export class DepGraphCycleError extends Error { + cyclePath: K[]; } } diff --git a/specs/dep_graph_spec.js b/specs/dep_graph_spec.js index ce59c00..008f837 100644 --- a/specs/dep_graph_spec.js +++ b/specs/dep_graph_spec.js @@ -30,6 +30,60 @@ describe("DepGraph", function () { expect(graph.overallOrder()).toEqual(["__proto__", "constructor"]); }); + it("should work with using any types as names", function () { + var graph = new DepGraph(); + + var stringType = ""; + var numberType = 0; + var bigintType = BigInt("9007199254740992"); + var booleanType = false; + var undefinedType = undefined; + var nullType = null; + var symbolType = Symbol(); + var objectType = {}; + var functionType = function x() {}; + + graph.addNode(stringType); + graph.addNode(numberType); + graph.addNode(bigintType); + graph.addNode(booleanType); + graph.addNode(undefinedType); + graph.addNode(nullType); + graph.addNode(symbolType); + graph.addNode(objectType); + graph.addNode(functionType); + + graph.addDependency(stringType, numberType); + graph.addDependency(numberType, bigintType); + graph.addDependency(bigintType, booleanType); + graph.addDependency(booleanType, undefinedType); + graph.addDependency(undefinedType, nullType); + graph.addDependency(nullType, symbolType); + graph.addDependency(symbolType, objectType); + graph.addDependency(objectType, functionType); + + expect(graph.hasNode(stringType)).toBeTrue(); + expect(graph.hasNode(numberType)).toBeTrue(); + expect(graph.hasNode(bigintType)).toBeTrue(); + expect(graph.hasNode(booleanType)).toBeTrue(); + expect(graph.hasNode(undefinedType)).toBeTrue(); + expect(graph.hasNode(nullType)).toBeTrue(); + expect(graph.hasNode(symbolType)).toBeTrue(); + expect(graph.hasNode(objectType)).toBeTrue(); + expect(graph.hasNode(functionType)).toBeTrue(); + expect(graph.overallOrder()).toEqual([ + functionType, + objectType, + symbolType, + nullType, + undefinedType, + booleanType, + bigintType, + numberType, + stringType + ]); + }); + it("should calculate its size", function () { var graph = new DepGraph(); @@ -553,6 +607,31 @@ describe("DepGraphCycleError", function () { expect(err.message).toEqual("Dependency Cycle Found: a -> b -> c -> a"); }); + it("should have a message when using special types as names", function () { + var stringType = "string"; + var numberType = 0; + var bigintType = BigInt("9007199254740992"); + var booleanType = false; + var undefinedType = undefined; + var nullType = null; + var symbolType = Symbol(); + var objectType = {}; + var functionType = function x() {}; + var err = new DepGraphCycleError([ + stringType, + numberType, + bigintType, + booleanType, + undefinedType, + nullType, + symbolType, + objectType, + functionType + ]); + + expect(err.message).toEqual("Dependency Cycle Found: string -> 0 -> 9007199254740992 -> false -> undefined -> null -> Symbol() -> [object Object] -> x"); + }); + it("should be an instanceof DepGraphCycleError", function () { var err = new DepGraphCycleError(["a", "b", "c", "a"]); expect(err instanceof DepGraphCycleError).toBeTrue();