restfuncs-transformer 0.9.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.ts CHANGED
@@ -1,4 +1,89 @@
1
- // Just pass-through the default export of typescript-rtti/dist/transformer
2
- // May be later, we need to extend the transformer for runtime inspection JSDoc information
3
- import transformer from "typescript-rtti/dist/transformer";
4
- export default transformer
1
+ import ts, {CompilerHost, CompilerOptions, Program, SourceFile} from 'typescript';
2
+ import {PluginConfig, ProgramTransformerExtras} from "ts-patch";
3
+ import {AddRemoteMethodsMeta} from "./AddRemoteMethodsMeta";
4
+ import {FileTransformRun, TransformerFactoryOOP} from "./transformerUtil";
5
+
6
+ // From: https://github.com/nonara/ts-patch/discussions/29#discussioncomment-325979
7
+
8
+ export const transformerVersion = {major: 1, feature: 1 }
9
+
10
+ /* ****************************************************************************************************************** */
11
+ // region: Helpers
12
+ /* ****************************************************************************************************************** */
13
+
14
+ /**
15
+ * Patches existing Compiler Host (or creates new one) to allow feeding updated file content from cache
16
+ */
17
+ function getPatchedHost(
18
+ maybeHost: CompilerHost | undefined,
19
+ tsInstance: typeof ts,
20
+ compilerOptions: CompilerOptions
21
+ ): CompilerHost & { fileCache: Map<string, SourceFile> }
22
+ {
23
+ const fileCache = new Map();
24
+ const compilerHost = maybeHost ?? tsInstance.createCompilerHost(compilerOptions, true);
25
+ const originalGetSourceFile = compilerHost.getSourceFile;
26
+
27
+ return Object.assign(compilerHost, {
28
+ getSourceFile(fileName: string, languageVersion: ts.ScriptTarget) {
29
+ fileName = tsInstance.normalizePath(fileName);
30
+ if (fileCache.has(fileName)) return fileCache.get(fileName);
31
+
32
+ const sourceFile = originalGetSourceFile.apply(void 0, Array.from(arguments) as any);
33
+ fileCache.set(fileName, sourceFile);
34
+
35
+ return sourceFile;
36
+ },
37
+ fileCache
38
+ });
39
+ }
40
+
41
+ // endregion
42
+
43
+
44
+ /* ****************************************************************************************************************** */
45
+ // region: Program Transformer
46
+ /* ****************************************************************************************************************** */
47
+
48
+ export default function transformProgram(
49
+ program: Program,
50
+ host: CompilerHost | undefined,
51
+ config: PluginConfig,
52
+ extras?: ProgramTransformerExtras,
53
+ ): Program {
54
+ if(!extras) {
55
+ throw new Error(`Please add the flag "transformProgram": true to the transformer inside tsconfig.json`);
56
+ }
57
+
58
+ const { ts: tsInstance } = extras;
59
+ const compilerOptions = program.getCompilerOptions();
60
+ const compilerHost = getPatchedHost(host, tsInstance, compilerOptions);
61
+ const rootFileNames = program.getRootFileNames().map(tsInstance.normalizePath);
62
+
63
+ const transformerFactoryOOP = new TransformerFactoryOOP(AddRemoteMethodsMeta);
64
+
65
+ /* Transform AST */
66
+ tsInstance.transform(
67
+ /* sourceFiles */ program.getSourceFiles().filter(sourceFile => rootFileNames.includes(sourceFile.fileName)),
68
+ /* transformerFactoryOOP */ [ transformerFactoryOOP.asFunction ],
69
+ compilerOptions
70
+ )
71
+
72
+ transformerFactoryOOP.transformRunsDone.forEach(transformRun => {
73
+ if(transformRun.astWasModified) {
74
+ /* Render modified files and create new SourceFiles for them to use in host's cache */
75
+ const {printFile} = tsInstance.createPrinter();
76
+ const sourceFile = transformRun.sourceFile;
77
+ const updatedSourceFile = tsInstance.createSourceFile(sourceFile.fileName, printFile(sourceFile), sourceFile.languageVersion);
78
+ updatedSourceFile.version = `${sourceFile.version}_restfuncs_${transformerVersion.major}.${transformerVersion.feature}`;
79
+ compilerHost.fileCache.set(sourceFile.fileName, updatedSourceFile);
80
+ }
81
+ })
82
+
83
+
84
+
85
+ /* Re-create Program instance */
86
+ return tsInstance.createProgram(rootFileNames, compilerOptions, compilerHost);
87
+ }
88
+
89
+ // endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "restfuncs-transformer",
3
- "version": "0.9.8",
3
+ "version": "1.0.0",
4
4
  "description": "Enhances your classes with typescript-rtti, to make them usable with Restfuncs",
5
5
  "keywords": ["rpc","rest"],
6
6
  "author": "Boris Gingold <bogeee@bogitech.de>",
@@ -11,8 +11,9 @@
11
11
  },
12
12
  "license": "MIT",
13
13
  "scripts": {
14
- "clean": "tsc --build --clean && rm -rf dist LICENSE",
15
- "build": "tsc"
14
+ "clean": "tsc --build --clean && rimraf dist LICENSE",
15
+ "build": "tsc --build",
16
+ "dev:playgrund": "tsx devPlayground.ts"
16
17
  },
17
18
  "exports": {
18
19
  ".": {
@@ -20,10 +21,13 @@
20
21
  }
21
22
  },
22
23
  "dependencies": {
23
- "typescript-rtti": "^0.9.6"
24
+ "typescript": "5.0 || 5.1 || 5.3"
25
+ },
26
+ "peerDependencies": {
27
+ "restfuncs-server": ">=2"
24
28
  },
25
29
  "devDependencies": {
26
- "typescript": "5.1.x",
27
- "nodemon": "^2.0.15"
30
+ "@types/ts-expose-internals": "npm:ts-expose-internals@5.3.3",
31
+ "rimraf": "=5.0.5"
28
32
  }
29
33
  }
package/readme.md CHANGED
@@ -1,4 +1,43 @@
1
- Enhances your classes with [typescript-rtti](), to make them usable with [restfuncs-server](https://www.npmjs.com/package/restfuncs-server).
2
- Meaning Restfuncs is able to validate the arguments to your remote methods at runtime.
1
+ # restfuncs-transformer
2
+ This transformer prepares `ServerSession` subclasses for Typia and adds jsDoc info for the (upcoming) API browser.
3
3
 
4
- [Here's how to use it](https://github.com/bogeeee/restfuncs#setting-up-the-build-the-annoying-stuff-)
4
+ ## Usage
5
+ [Here's how to use it](https://github.com/bogeeee/restfuncs/tree/3.x/readme.md#setting-up-the-build-here-it-gets-a-bit-nasty-)
6
+
7
+
8
+ ## How the transformer chain works
9
+ Here is, how the restfuncs-transformer, typia transformer and typescript-rtti work together:
10
+ 1. The `restfuncs-transformer` scans for all `@remote` methods and, at the bottom of inside their class, it adds the following code (here for "myMethod"):
11
+ ````typescript
12
+
13
+ /**
14
+ * Code is generated by the restfuncs-transformer during compile-time
15
+ */
16
+ static getRemoteMethodsMeta(): (typeof this.type_remoteMethodsMeta) {
17
+ this.__hello_developer__make_sure_your_class_is_a_subclass_of_ServerSession // Give a friendly error message when this is not the case. Otherwise the following statement "const typia = ..." would fail and leaves the user wondering.
18
+ let typia = this.typiaRuntime; // We need a "typia" defined in the scope, but let restfuncs manage where that dependency comes from
19
+ return {
20
+ transformerVersion: {major: XX, feature: XX },
21
+ instanceMethods: {
22
+ "myMethod": {
23
+ arguments: {
24
+ validateEquals: (args: unknown) => typia.validateEquals<Parameters<typeof this.prototype["myMethod"]>>(args),
25
+ validatePrune: ...
26
+ },
27
+ result: {
28
+ validateEquals: (value: unknown) => typia.validateEquals<Awaited<ReturnType<typeof this.prototype["myMethod"]>>>(value),
29
+ ...
30
+ },
31
+ jsDoc: {
32
+ comment: "text with <strong>markup</strong>",
33
+ params: {a: "text..."},
34
+ tags: [{name:"returns", comment: "text..."}]
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
40
+ ````
41
+
42
+ 2. The [Typia transformer](https://typia.io/) will then replace all `typia.validate<T>` expressions with the actual validation code body. Typia calls this "ahead of time validation". At runtime this is used to validate evil input and serialize/deserialize from json/protobuf in an ultra fast way (future).
43
+ 3. The [typescript-rtti transformer](https://typescript-rtti.org/) adds type-info decorations for all compiled symbols (that's all this `__RΦ` stuff in the .js). At runtime, this allows restfuncs to easily navigate through- and inspect all the type hierarchy. I.e when mapping REST parameter names to the remote methods arguments, for better error diagnosis, or for the upcoming API browser.
@@ -0,0 +1,50 @@
1
+ import ts, { Node, SourceFile, TransformationContext, TransformerFactory } from "typescript";
2
+ type ClassOf<T> = {
3
+ new (...args: any[]): T;
4
+ };
5
+ /**
6
+ * Transformerfactory in a better oop style
7
+ */
8
+ export declare class TransformerFactoryOOP<FT extends FileTransformRun> {
9
+ transformRunsDone: FT[];
10
+ fileTransformRunClass: ClassOf<FileTransformRun>;
11
+ constructor(fileTransformerRunClass: ClassOf<FT>);
12
+ /**
13
+ * Transformer for the typescript api (does not offer such a nice oop style)
14
+ */
15
+ get asFunction(): TransformerFactory<SourceFile>;
16
+ }
17
+ /**
18
+ * Stores the context and result of one run. Don't reuse.
19
+ */
20
+ export declare abstract class FileTransformRun {
21
+ tsInstance: typeof ts;
22
+ sourceFile: SourceFile;
23
+ context: TransformationContext;
24
+ parentNodes: Node[];
25
+ used: boolean;
26
+ constructor(tsInstance: typeof ts, sourceFile: SourceFile, context: TransformationContext);
27
+ getParent: () => ts.Node;
28
+ /**
29
+ * Vistor function
30
+ * The body must call visitChilds, if the subtree should be visited
31
+ * @param node
32
+ */
33
+ abstract visit(node: Node): Node;
34
+ protected visitChilds(node: Node): ts.Node;
35
+ /**
36
+ * @return text of the node (only for string literals, etc)
37
+ * @param node
38
+ */
39
+ getText(node: Node): string;
40
+ getChilds(node: Node): ts.Node[];
41
+ /**
42
+ *
43
+ * @param tsInstance
44
+ * @param node
45
+ * @param escaped don't know why but sometimes the name can only be read by the escapedText property.
46
+ */
47
+ getIdentifierName(node: Node, escaped?: boolean): any;
48
+ }
49
+ export {};
50
+ //# sourceMappingURL=transformerUtil.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transformerUtil.d.ts","sourceRoot":"","sources":["transformerUtil.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAC,IAAI,EAAE,UAAU,EAAc,qBAAqB,EAAE,kBAAkB,EAAC,MAAM,YAAY,CAAC;AAEvG,KAAK,OAAO,CAAC,CAAC,IAAI;IACd,KAAI,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;CACzB,CAAA;AAED;;GAEG;AACH,qBAAa,qBAAqB,CAAC,EAAE,SAAS,gBAAgB;IAC1D,iBAAiB,EAAE,EAAE,EAAE,CAAK;IAC5B,qBAAqB,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;gBAGrC,uBAAuB,EAAE,OAAO,CAAC,EAAE,CAAC;IAIhD;;OAEG;IACH,IAAI,UAAU,IAAI,kBAAkB,CAAC,UAAU,CAAC,CAQ/C;CACJ;AAED;;GAEG;AACH,8BAAsB,gBAAgB;IAClC,UAAU,EAAE,OAAO,EAAE,CAAC;IACtB,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,EAAE,qBAAqB,CAAC;IAC/B,WAAW,EAAE,IAAI,EAAE,CAAK;IACxB,IAAI,UAAS;gBAED,UAAU,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,qBAAqB;IAOzF,SAAS,gBAEP;IAEF;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAGhC,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI;IAWhC;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM;IAK3B,SAAS,CAAC,IAAI,EAAE,IAAI;IAMpB;;;;;OAKG;IACH,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,UAAQ;CAUhD"}
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.FileTransformRun = exports.TransformerFactoryOOP = void 0;
27
+ const typescript_1 = __importStar(require("typescript"));
28
+ /**
29
+ * Transformerfactory in a better oop style
30
+ */
31
+ class TransformerFactoryOOP {
32
+ constructor(fileTransformerRunClass) {
33
+ this.transformRunsDone = [];
34
+ this.fileTransformRunClass = fileTransformerRunClass;
35
+ }
36
+ /**
37
+ * Transformer for the typescript api (does not offer such a nice oop style)
38
+ */
39
+ get asFunction() {
40
+ return (context) => {
41
+ return (sourceFile) => {
42
+ const fileTransformer = new this.fileTransformRunClass(typescript_1.default, sourceFile, context);
43
+ this.transformRunsDone.push(fileTransformer);
44
+ return typescript_1.default.visitEachChild(sourceFile, (node) => fileTransformer.visit(node), context);
45
+ };
46
+ };
47
+ }
48
+ }
49
+ exports.TransformerFactoryOOP = TransformerFactoryOOP;
50
+ /**
51
+ * Stores the context and result of one run. Don't reuse.
52
+ */
53
+ class FileTransformRun {
54
+ constructor(tsInstance, sourceFile, context) {
55
+ this.parentNodes = [];
56
+ this.used = false;
57
+ this.getParent = () => {
58
+ return this.parentNodes[this.parentNodes.length - 1];
59
+ };
60
+ this.tsInstance = tsInstance;
61
+ this.sourceFile = sourceFile;
62
+ this.context = context;
63
+ }
64
+ visitChilds(node) {
65
+ this.parentNodes.push(node);
66
+ try {
67
+ return this.tsInstance.visitEachChild(node, (node) => this.visit(node), this.context);
68
+ }
69
+ finally {
70
+ this.parentNodes.pop();
71
+ }
72
+ }
73
+ /**
74
+ * @return text of the node (only for string literals, etc)
75
+ * @param node
76
+ */
77
+ getText(node) {
78
+ // @ts-ignore
79
+ return node.text;
80
+ }
81
+ getChilds(node) {
82
+ let result = [];
83
+ this.tsInstance.forEachChild(node, child => result.push(child));
84
+ return result;
85
+ }
86
+ /**
87
+ *
88
+ * @param tsInstance
89
+ * @param node
90
+ * @param escaped don't know why but sometimes the name can only be read by the escapedText property.
91
+ */
92
+ getIdentifierName(node, escaped = false) {
93
+ const identifierNode = this.getChilds(node).find(child => child.kind == typescript_1.SyntaxKind.Identifier);
94
+ if (!identifierNode) {
95
+ throw new Error("Identifier node not found");
96
+ }
97
+ if (escaped) {
98
+ return identifierNode.escapedText;
99
+ }
100
+ return identifierNode.getText();
101
+ }
102
+ }
103
+ exports.FileTransformRun = FileTransformRun;
104
+ //# sourceMappingURL=transformerUtil.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transformerUtil.js","sourceRoot":"","sources":["transformerUtil.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yDAAuG;AAMvG;;GAEG;AACH,MAAa,qBAAqB;IAK9B,YAAY,uBAAoC;QAJhD,sBAAiB,GAAS,EAAE,CAAA;QAKxB,IAAI,CAAC,qBAAqB,GAAG,uBAAuB,CAAC;IACzD,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACV,OAAO,CAAC,OAA8B,EAAE,EAAE;YACtC,OAAO,CAAC,UAAsB,EAAc,EAAE;gBAC1C,MAAM,eAAe,GAAG,IAAI,IAAI,CAAC,qBAAqB,CAAC,oBAAE,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;gBAChF,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,eAAqB,CAAC,CAAC;gBACnD,OAAO,oBAAE,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;YACzF,CAAC,CAAA;QACL,CAAC,CAAA;IACL,CAAC;CACJ;AArBD,sDAqBC;AAED;;GAEG;AACH,MAAsB,gBAAgB;IAOlC,YAAY,UAAqB,EAAE,UAAsB,EAAE,OAA8B;QAHzF,gBAAW,GAAW,EAAE,CAAA;QACxB,SAAI,GAAG,KAAK,CAAC;QASb,cAAS,GAAG,GAAG,EAAE;YACb,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACzD,CAAC,CAAC;QARE,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;IAeS,WAAW,CAAC,IAAU;QAC5B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI;YACA,OAAO,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;SACzF;gBAAS;YACN,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC;SAC1B;IACL,CAAC;IAID;;;OAGG;IACH,OAAO,CAAC,IAAU;QACd,aAAa;QACb,OAAO,IAAI,CAAC,IAAI,CAAC;IACrB,CAAC;IAED,SAAS,CAAC,IAAU;QAChB,IAAI,MAAM,GAAW,EAAE,CAAC;QACxB,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAChE,OAAO,MAAM,CAAA;IACjB,CAAC;IAED;;;;;OAKG;IACH,iBAAiB,CAAC,IAAU,EAAE,OAAO,GAAG,KAAK;QACzC,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,IAAI,uBAAU,CAAC,UAAU,CAAC,CAAC;QAC/F,IAAG,CAAC,cAAc,EAAE;YAChB,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;SAC/C;QACD,IAAG,OAAO,EAAE;YACR,OAAQ,cAAsB,CAAC,WAAW,CAAC;SAC9C;QACD,OAAO,cAAc,CAAC,OAAO,EAAE,CAAC;IACpC,CAAC;CACJ;AApED,4CAoEC"}
@@ -0,0 +1,104 @@
1
+ import ts, {Node, SourceFile, SyntaxKind, TransformationContext, TransformerFactory} from "typescript";
2
+
3
+ type ClassOf<T> = {
4
+ new(...args: any[]): T
5
+ }
6
+
7
+ /**
8
+ * Transformerfactory in a better oop style
9
+ */
10
+ export class TransformerFactoryOOP<FT extends FileTransformRun> {
11
+ transformRunsDone: FT[] = []
12
+ fileTransformRunClass: ClassOf<FileTransformRun>;
13
+
14
+
15
+ constructor(fileTransformerRunClass: ClassOf<FT>) {
16
+ this.fileTransformRunClass = fileTransformerRunClass;
17
+ }
18
+
19
+ /**
20
+ * Transformer for the typescript api (does not offer such a nice oop style)
21
+ */
22
+ get asFunction(): TransformerFactory<SourceFile> {
23
+ return (context: TransformationContext) => {
24
+ return (sourceFile: SourceFile): SourceFile => {
25
+ const fileTransformer = new this.fileTransformRunClass(ts, sourceFile, context);
26
+ this.transformRunsDone.push(fileTransformer as FT);
27
+ return ts.visitEachChild(sourceFile, (node) => fileTransformer.visit(node), context);
28
+ }
29
+ }
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Stores the context and result of one run. Don't reuse.
35
+ */
36
+ export abstract class FileTransformRun {
37
+ tsInstance: typeof ts;
38
+ sourceFile: SourceFile
39
+ context: TransformationContext;
40
+ parentNodes: Node[] = []
41
+ used = false;
42
+
43
+ constructor(tsInstance: typeof ts, sourceFile: SourceFile, context: TransformationContext) {
44
+ this.tsInstance = tsInstance;
45
+ this.sourceFile = sourceFile;
46
+ this.context = context;
47
+ }
48
+
49
+
50
+ getParent = () => {
51
+ return this.parentNodes[this.parentNodes.length - 1];
52
+ };
53
+
54
+ /**
55
+ * Vistor function
56
+ * The body must call visitChilds, if the subtree should be visited
57
+ * @param node
58
+ */
59
+ abstract visit(node: Node): Node;
60
+
61
+
62
+ protected visitChilds(node: Node) {
63
+ this.parentNodes.push(node);
64
+ try {
65
+ return this.tsInstance.visitEachChild(node, (node) => this.visit(node), this.context);
66
+ } finally {
67
+ this.parentNodes.pop();
68
+ }
69
+ }
70
+
71
+
72
+
73
+ /**
74
+ * @return text of the node (only for string literals, etc)
75
+ * @param node
76
+ */
77
+ getText(node: Node): string {
78
+ // @ts-ignore
79
+ return node.text;
80
+ }
81
+
82
+ getChilds(node: Node) {
83
+ let result: Node[] = [];
84
+ this.tsInstance.forEachChild(node, child => result.push(child));
85
+ return result
86
+ }
87
+
88
+ /**
89
+ *
90
+ * @param tsInstance
91
+ * @param node
92
+ * @param escaped don't know why but sometimes the name can only be read by the escapedText property.
93
+ */
94
+ getIdentifierName(node: Node, escaped = false) {
95
+ const identifierNode = this.getChilds(node).find(child => child.kind == SyntaxKind.Identifier);
96
+ if(!identifierNode) {
97
+ throw new Error("Identifier node not found")
98
+ }
99
+ if(escaped) {
100
+ return (identifierNode as any).escapedText;
101
+ }
102
+ return identifierNode.getText();
103
+ }
104
+ }