@eliasku/ts-transformers 0.0.1

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.
@@ -0,0 +1,204 @@
1
+ import ts from "typescript";
2
+
3
+ const namedDeclarationKinds = [
4
+ ts.SyntaxKind.InterfaceDeclaration,
5
+ ts.SyntaxKind.ClassDeclaration,
6
+ ts.SyntaxKind.EnumDeclaration,
7
+ ts.SyntaxKind.TypeAliasDeclaration,
8
+ ts.SyntaxKind.ModuleDeclaration,
9
+ ts.SyntaxKind.FunctionDeclaration,
10
+ ts.SyntaxKind.VariableDeclaration,
11
+ ts.SyntaxKind.PropertySignature,
12
+ ts.SyntaxKind.Parameter,
13
+ ];
14
+
15
+ export const isNodeNamedDeclaration = (node: ts.Node): node is ts.NamedDeclaration =>
16
+ namedDeclarationKinds.indexOf(node.kind) !== -1;
17
+
18
+ export function getActualSymbol(symbol: ts.Symbol, typeChecker: ts.TypeChecker): ts.Symbol {
19
+ if (symbol.flags & ts.SymbolFlags.Alias) {
20
+ symbol = typeChecker.getAliasedSymbol(symbol);
21
+ }
22
+
23
+ return symbol;
24
+ }
25
+
26
+ export function splitTransientSymbol(symbol: ts.Symbol, typeChecker: ts.TypeChecker): ts.Symbol[] {
27
+ // actually I think we even don't need to operate/use "Transient" symbols anywhere
28
+ // it's kind of aliased symbol, but just merged
29
+ // but it's hard to refractor everything to use array of symbols instead of just symbol
30
+ // so let's fix it for some places
31
+ if ((symbol.flags & ts.SymbolFlags.Transient) === 0) {
32
+ return [symbol];
33
+ }
34
+
35
+ // "Transient" symbol is kinda "merged" symbol
36
+ // I don't really know is this way to "split" is correct
37
+ // but it seems that it works for now ¯\_(ツ)_/¯
38
+ const declarations = getDeclarationsForSymbol(symbol);
39
+ const result: ts.Symbol[] = [];
40
+ for (const declaration of declarations) {
41
+ if (!isNodeNamedDeclaration(declaration) || declaration.name === undefined) {
42
+ continue;
43
+ }
44
+
45
+ const sym = typeChecker.getSymbolAtLocation(declaration.name);
46
+ if (sym === undefined) {
47
+ continue;
48
+ }
49
+
50
+ result.push(getActualSymbol(sym, typeChecker));
51
+ }
52
+
53
+ return result;
54
+ }
55
+
56
+ export function getDeclarationsForSymbol(symbol: ts.Symbol): ts.Declaration[] {
57
+ const result: ts.Declaration[] = [];
58
+
59
+ if (symbol.declarations !== undefined) {
60
+ result.push(...symbol.declarations);
61
+ }
62
+
63
+ if (symbol.valueDeclaration !== undefined) {
64
+ // push valueDeclaration might be already in declarations array
65
+ // so let's check first to avoid duplication nodes
66
+ if (!result.includes(symbol.valueDeclaration)) {
67
+ result.push(symbol.valueDeclaration);
68
+ }
69
+ }
70
+
71
+ return result;
72
+ }
73
+
74
+ export function getExportsForSourceFile(typeChecker: ts.TypeChecker, sourceFileSymbol: ts.Symbol): ts.Symbol[] {
75
+ if (sourceFileSymbol.exports !== undefined) {
76
+ const commonJsExport = sourceFileSymbol.exports.get(ts.InternalSymbolName.ExportEquals);
77
+ if (commonJsExport !== undefined) {
78
+ return [getActualSymbol(commonJsExport, typeChecker)];
79
+ }
80
+ }
81
+
82
+ const result: ts.Symbol[] = typeChecker.getExportsOfModule(sourceFileSymbol);
83
+
84
+ if (sourceFileSymbol.exports !== undefined) {
85
+ const defaultExportSymbol = sourceFileSymbol.exports.get(ts.InternalSymbolName.Default);
86
+ if (defaultExportSymbol !== undefined) {
87
+ if (!result.includes(defaultExportSymbol)) {
88
+ // it seems that default export is always returned by getExportsOfModule
89
+ // but let's add it to be sure add if there is no such export
90
+ result.push(defaultExportSymbol);
91
+ }
92
+ }
93
+ }
94
+
95
+ return result.map((symbol: ts.Symbol) => getActualSymbol(symbol, typeChecker));
96
+ }
97
+
98
+ export type ClassMember =
99
+ | ts.MethodDeclaration
100
+ | ts.PropertyDeclaration
101
+ | ts.GetAccessorDeclaration
102
+ | ts.SetAccessorDeclaration;
103
+
104
+ export const isClassMember = (node: ts.Node): node is ClassMember =>
105
+ ts.isMethodDeclaration(node) || ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node);
106
+
107
+ export function getClassOfMemberSymbol(
108
+ nodeSymbol: ts.Symbol,
109
+ ): ts.ClassLikeDeclaration | ts.ObjectLiteralExpression | ts.TypeLiteralNode | ts.InterfaceDeclaration | null {
110
+ const classMembers = getClassMemberDeclarations(nodeSymbol);
111
+ if (classMembers.length !== 0) {
112
+ // we need any member to get class' declaration
113
+ const classMember = classMembers[0];
114
+ if (isConstructorParameter(classMember)) {
115
+ return (classMember.parent as ts.ConstructorDeclaration).parent;
116
+ }
117
+
118
+ // we're sure that it is a class, not interface
119
+ return classMember.parent as ts.ClassLikeDeclaration | ts.ObjectLiteralExpression;
120
+ }
121
+
122
+ return null;
123
+ }
124
+
125
+ export const hasPrivateKeyword = (node: ClassMember | ts.ParameterDeclaration) =>
126
+ hasModifier(node, ts.SyntaxKind.PrivateKeyword);
127
+
128
+ function getModifiers(node: ts.Node): readonly ts.Modifier[] {
129
+ if (isBreakingTypeScriptApi(ts)) {
130
+ if (!ts.canHaveModifiers(node)) {
131
+ return [];
132
+ }
133
+
134
+ return ts.getModifiers(node) || [];
135
+ }
136
+
137
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
138
+ // @ts-ignore
139
+ return node.modifiers || [];
140
+ }
141
+
142
+ export const hasModifier = (node: ts.Node, modifier: ts.SyntaxKind) =>
143
+ getModifiers(node).some((mod) => mod.kind === modifier);
144
+
145
+ function getDecorators(node: ts.Node): readonly unknown[] {
146
+ if (isBreakingTypeScriptApi(ts)) {
147
+ if (!ts.canHaveDecorators(node)) {
148
+ return [];
149
+ }
150
+
151
+ return ts.getDecorators(node) || [];
152
+ }
153
+
154
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
155
+ // @ts-ignore
156
+ return node.decorators || [];
157
+ }
158
+
159
+ export const hasDecorators = (node: ts.Node) => getDecorators(node).length !== 0;
160
+
161
+ export const isConstructorParameter = (node: ts.Node): node is ts.ParameterDeclaration =>
162
+ ts.isParameter(node) &&
163
+ ts.isConstructorDeclaration(node.parent as ts.Node) &&
164
+ (hasModifier(node, ts.SyntaxKind.PublicKeyword) ||
165
+ hasModifier(node, ts.SyntaxKind.ProtectedKeyword) ||
166
+ hasModifier(node, ts.SyntaxKind.PrivateKeyword) ||
167
+ hasModifier(node, ts.SyntaxKind.ReadonlyKeyword));
168
+
169
+ function getClassMemberDeclarations(symbol: ts.Symbol | undefined): (ClassMember | ts.ParameterDeclaration)[] {
170
+ if (symbol === undefined) {
171
+ return [];
172
+ }
173
+
174
+ const declarations = symbol.getDeclarations();
175
+ if (declarations === undefined) {
176
+ return [];
177
+ }
178
+
179
+ return declarations.filter((x: ts.Declaration): x is ClassMember | ts.ParameterDeclaration => {
180
+ return isClassMember(x) || isConstructorParameter(x);
181
+ });
182
+ }
183
+
184
+ export const isSymbolClassMember = (symbol: ts.Symbol | undefined) => getClassMemberDeclarations(symbol).length !== 0;
185
+
186
+ export const isPrivateClassMember = (symbol: ts.Symbol | undefined) =>
187
+ getClassMemberDeclarations(symbol).some(hasPrivateKeyword);
188
+
189
+ export function getNodeJSDocComment(node: ts.Node): string {
190
+ const start = node.getStart();
191
+ const jsDocStart = node.getStart(undefined, true);
192
+ return node.getSourceFile().getFullText().substring(jsDocStart, start).trim();
193
+ }
194
+
195
+ // decorators and modifiers-related api added in ts 4.8
196
+ interface BreakingTypeScriptApi {
197
+ canHaveDecorators(node: ts.Node): boolean;
198
+ getDecorators(node: ts.Node): readonly ts.Decorator[] | undefined;
199
+ canHaveModifiers(node: ts.Node): boolean;
200
+ getModifiers(node: ts.Node): readonly ts.Modifier[] | undefined;
201
+ }
202
+
203
+ const isBreakingTypeScriptApi = (compiler: object): compiler is BreakingTypeScriptApi =>
204
+ "canHaveDecorators" in compiler;
@@ -0,0 +1,7 @@
1
+ import ts from "typescript";
2
+
3
+ export function getNodeJSDocComment(node: ts.Node): string {
4
+ const start = node.getStart();
5
+ const jsDocStart = node.getStart(undefined, true);
6
+ return node.getSourceFile().getFullText().substring(jsDocStart, start).trim();
7
+ }
@@ -0,0 +1,213 @@
1
+ import ts from "typescript";
2
+
3
+ export function getActualSymbol(symbol: ts.Symbol, typeChecker: ts.TypeChecker): ts.Symbol {
4
+ if (symbol.flags & ts.SymbolFlags.Alias) {
5
+ symbol = typeChecker.getAliasedSymbol(symbol);
6
+ }
7
+
8
+ return symbol;
9
+ }
10
+
11
+ export function splitTransientSymbol(symbol: ts.Symbol, typeChecker: ts.TypeChecker): ts.Symbol[] {
12
+ // actually I think we even don't need to operate/use "Transient" symbols anywhere
13
+ // it's kind of aliased symbol, but just merged
14
+ // but it's hard to refractor everything to use array of symbols instead of just symbol
15
+ // so let's fix it for some places
16
+ if ((symbol.flags & ts.SymbolFlags.Transient) === 0) {
17
+ return [symbol];
18
+ }
19
+
20
+ // "Transient" symbol is kinda "merged" symbol
21
+ // I don't really know is this way to "split" is correct
22
+ // but it seems that it works for now
23
+ const declarations = getDeclarationsForSymbol(symbol);
24
+ const result: ts.Symbol[] = [];
25
+ for (const declaration of declarations) {
26
+ if (!isNodeNamedDeclaration(declaration) || declaration.name === undefined) {
27
+ continue;
28
+ }
29
+
30
+ const sym = typeChecker.getSymbolAtLocation(declaration.name);
31
+ if (sym === undefined) {
32
+ continue;
33
+ }
34
+
35
+ result.push(getActualSymbol(sym, typeChecker));
36
+ }
37
+
38
+ return result;
39
+ }
40
+
41
+ export function getDeclarationsForSymbol(symbol: ts.Symbol): ts.Declaration[] {
42
+ const result: ts.Declaration[] = [];
43
+
44
+ if (symbol.declarations !== undefined) {
45
+ result.push(...symbol.declarations);
46
+ }
47
+
48
+ if (symbol.valueDeclaration !== undefined) {
49
+ // push valueDeclaration might be already in declarations array
50
+ // so let's check first to avoid duplication nodes
51
+ if (!result.includes(symbol.valueDeclaration)) {
52
+ result.push(symbol.valueDeclaration);
53
+ }
54
+ }
55
+
56
+ return result;
57
+ }
58
+
59
+ export function getExportsForSourceFile(typeChecker: ts.TypeChecker, sourceFileSymbol: ts.Symbol): ts.Symbol[] {
60
+ if (sourceFileSymbol.exports !== undefined) {
61
+ const commonJsExport = sourceFileSymbol.exports.get(ts.InternalSymbolName.ExportEquals);
62
+ if (commonJsExport !== undefined) {
63
+ return [getActualSymbol(commonJsExport, typeChecker)];
64
+ }
65
+ }
66
+
67
+ const result: ts.Symbol[] = typeChecker.getExportsOfModule(sourceFileSymbol);
68
+
69
+ if (sourceFileSymbol.exports !== undefined) {
70
+ const defaultExportSymbol = sourceFileSymbol.exports.get(ts.InternalSymbolName.Default);
71
+ if (defaultExportSymbol !== undefined) {
72
+ if (!result.includes(defaultExportSymbol)) {
73
+ // it seems that default export is always returned by getExportsOfModule
74
+ // but let's add it to be sure add if there is no such export
75
+ result.push(defaultExportSymbol);
76
+ }
77
+ }
78
+ }
79
+
80
+ return result.map((symbol: ts.Symbol) => getActualSymbol(symbol, typeChecker));
81
+ }
82
+
83
+ const namedDeclarationKinds = [
84
+ ts.SyntaxKind.InterfaceDeclaration,
85
+ ts.SyntaxKind.ClassDeclaration,
86
+ ts.SyntaxKind.EnumDeclaration,
87
+ ts.SyntaxKind.TypeAliasDeclaration,
88
+ ts.SyntaxKind.ModuleDeclaration,
89
+ ts.SyntaxKind.FunctionDeclaration,
90
+ ts.SyntaxKind.VariableDeclaration,
91
+ ts.SyntaxKind.PropertySignature,
92
+ ts.SyntaxKind.Parameter,
93
+ ];
94
+
95
+ export function isNodeNamedDeclaration(node: ts.Node): node is ts.NamedDeclaration {
96
+ return namedDeclarationKinds.indexOf(node.kind) !== -1;
97
+ }
98
+
99
+ export type ClassMember =
100
+ | ts.MethodDeclaration
101
+ | ts.PropertyDeclaration
102
+ | ts.GetAccessorDeclaration
103
+ | ts.SetAccessorDeclaration;
104
+
105
+ export function isClassMember(node: ts.Node): node is ClassMember {
106
+ return (
107
+ ts.isMethodDeclaration(node) || ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node)
108
+ );
109
+ }
110
+
111
+ export function getClassOfMemberSymbol(
112
+ nodeSymbol: ts.Symbol,
113
+ ): ts.ClassLikeDeclaration | ts.ObjectLiteralExpression | ts.TypeLiteralNode | ts.InterfaceDeclaration | null {
114
+ const classMembers = getClassMemberDeclarations(nodeSymbol);
115
+ if (classMembers.length !== 0) {
116
+ // we need any member to get class' declaration
117
+ const classMember = classMembers[0];
118
+ if (isConstructorParameter(classMember)) {
119
+ return (classMember.parent as ts.ConstructorDeclaration).parent;
120
+ }
121
+
122
+ // we're sure that it is a class, not interface
123
+ return classMember.parent as ts.ClassLikeDeclaration | ts.ObjectLiteralExpression;
124
+ }
125
+
126
+ return null;
127
+ }
128
+
129
+ export function hasPrivateKeyword(node: ClassMember | ts.ParameterDeclaration): boolean {
130
+ return hasModifier(node, ts.SyntaxKind.PrivateKeyword);
131
+ }
132
+
133
+ function getModifiers(node: ts.Node): readonly ts.Modifier[] {
134
+ if (isBreakingTypeScriptApi(ts)) {
135
+ if (!ts.canHaveModifiers(node)) {
136
+ return [];
137
+ }
138
+
139
+ return ts.getModifiers(node) || [];
140
+ }
141
+
142
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
143
+ // @ts-ignore
144
+ return node.modifiers || [];
145
+ }
146
+
147
+ export function hasModifier(node: ts.Node, modifier: ts.SyntaxKind): boolean {
148
+ return getModifiers(node).some((mod) => mod.kind === modifier);
149
+ }
150
+
151
+ function getDecorators(node: ts.Node): readonly unknown[] {
152
+ if (isBreakingTypeScriptApi(ts)) {
153
+ if (!ts.canHaveDecorators(node)) {
154
+ return [];
155
+ }
156
+
157
+ return ts.getDecorators(node) || [];
158
+ }
159
+
160
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
161
+ // @ts-ignore
162
+ return node.decorators || [];
163
+ }
164
+
165
+ export function hasDecorators(node: ts.Node): boolean {
166
+ return getDecorators(node).length !== 0;
167
+ }
168
+
169
+ export function isConstructorParameter(node: ts.Node): node is ts.ParameterDeclaration {
170
+ return (
171
+ ts.isParameter(node) &&
172
+ ts.isConstructorDeclaration(node.parent as ts.Node) &&
173
+ (hasModifier(node, ts.SyntaxKind.PublicKeyword) ||
174
+ hasModifier(node, ts.SyntaxKind.ProtectedKeyword) ||
175
+ hasModifier(node, ts.SyntaxKind.PrivateKeyword) ||
176
+ hasModifier(node, ts.SyntaxKind.ReadonlyKeyword))
177
+ );
178
+ }
179
+
180
+ function getClassMemberDeclarations(symbol: ts.Symbol | undefined): (ClassMember | ts.ParameterDeclaration)[] {
181
+ if (symbol === undefined) {
182
+ return [];
183
+ }
184
+
185
+ const declarations = symbol.getDeclarations();
186
+ if (declarations === undefined) {
187
+ return [];
188
+ }
189
+
190
+ return declarations.filter((x: ts.Declaration): x is ClassMember | ts.ParameterDeclaration => {
191
+ return isClassMember(x) || isConstructorParameter(x);
192
+ });
193
+ }
194
+
195
+ export function isSymbolClassMember(symbol: ts.Symbol | undefined): boolean {
196
+ return getClassMemberDeclarations(symbol).length !== 0;
197
+ }
198
+
199
+ export function isPrivateClassMember(symbol: ts.Symbol | undefined): boolean {
200
+ return getClassMemberDeclarations(symbol).some(hasPrivateKeyword);
201
+ }
202
+
203
+ // decorators and modifiers-related api added in ts 4.8
204
+ interface BreakingTypeScriptApi {
205
+ canHaveDecorators(node: ts.Node): boolean;
206
+ getDecorators(node: ts.Node): readonly ts.Decorator[] | undefined;
207
+ canHaveModifiers(node: ts.Node): boolean;
208
+ getModifiers(node: ts.Node): readonly ts.Modifier[] | undefined;
209
+ }
210
+
211
+ function isBreakingTypeScriptApi(compiler: object): compiler is BreakingTypeScriptApi {
212
+ return "canHaveDecorators" in compiler;
213
+ }