From 56bf0e6be1977d8da6d3fbc4d451cf847e823aee Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Wed, 19 Aug 2020 03:27:36 +0300 Subject: [PATCH] feat: add a tree structure for generic types --- .../packages/schematics/src/utils/index.ts | 1 + .../packages/schematics/src/utils/tree.ts | 71 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 npm/ng-packs/packages/schematics/src/utils/tree.ts diff --git a/npm/ng-packs/packages/schematics/src/utils/index.ts b/npm/ng-packs/packages/schematics/src/utils/index.ts index cc9b0052fa..5a56c6d2eb 100644 --- a/npm/ng-packs/packages/schematics/src/utils/index.ts +++ b/npm/ng-packs/packages/schematics/src/utils/index.ts @@ -7,4 +7,5 @@ export * from './rule'; export * from './service'; export * from './source'; export * from './text'; +export * from './tree'; export * from './workspace'; diff --git a/npm/ng-packs/packages/schematics/src/utils/tree.ts b/npm/ng-packs/packages/schematics/src/utils/tree.ts new file mode 100644 index 0000000000..d142032255 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/tree.ts @@ -0,0 +1,71 @@ +export class TypeNode { + children: TypeNode[] = []; + + index = 0; + + constructor( + public data: string, + public parent: TypeNode | null, + public mapperFn = (node: TypeNode) => node.data, + ) {} + + toGenerics(): string[] { + const generics = this.children.length ? `<${this.children.map(n => `T${n.index}`)}>` : ''; + return [this.data + generics].concat( + this.children.reduce((acc: string[], node) => acc.concat(node.toGenerics()), []), + ); + } + + toString() { + const self = this.mapperFn(this); + + if (!self) return ''; + + const representation = self + this.children.filter(String || Boolean).join(', '); + + if (!this.parent) return representation; + + const siblings = this.parent.children; + + return ( + (siblings[0] === this ? '<' : '') + + representation + + (siblings[siblings.length - 1] === this ? '>' : '') + ); + } + + valueOf() { + return this.toString(); + } +} + +export function parseGenerics(type: string, mapperFn?: TypeNodeMapperFn) { + const [rootType, ...types] = type.split('<'); + const root = new TypeNode(rootType, null, mapperFn); + + types.reduce((parent, t) => { + const [left, right] = t.split(/>+,?\s*/); + + const leftNode = new TypeNode(left, parent, mapperFn); + leftNode.index = parent.children.length; + parent.children.push(leftNode); + parent = leftNode; + + let { length } = t.match(/>/g) || []; + while (length--) parent = parent.parent!; + + if (right) { + parent = parent.parent!; + const rightNode = new TypeNode(right, parent, mapperFn); + rightNode.index = parent.children.length; + parent.children.push(rightNode); + parent = rightNode; + } + + return parent; + }, root); + + return root; +} + +export type TypeNodeMapperFn = (node: TypeNode) => string;