@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.
- package/README.md +0 -0
- package/package.json +50 -0
- package/src/config.ts +1 -0
- package/src/const-enum/evaluator.ts +165 -0
- package/src/const-enum/index.ts +181 -0
- package/src/const-enum/registry.ts +169 -0
- package/src/const-enum/utils.ts +13 -0
- package/src/mangler/exports/tracker.ts +292 -0
- package/src/mangler/index.ts +642 -0
- package/src/mangler/types.ts +51 -0
- package/src/mangler/typescript-helpers.ts +204 -0
- package/src/mangler/utils/ast-utils.ts +7 -0
- package/src/mangler/utils/symbol-utils.ts +213 -0
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
import { ExportsSymbolTree } from "./exports/tracker";
|
|
4
|
+
import {
|
|
5
|
+
getDeclarationsForSymbol,
|
|
6
|
+
isClassMember,
|
|
7
|
+
isConstructorParameter,
|
|
8
|
+
isPrivateClassMember,
|
|
9
|
+
isSymbolClassMember,
|
|
10
|
+
getClassOfMemberSymbol,
|
|
11
|
+
hasDecorators,
|
|
12
|
+
hasModifier,
|
|
13
|
+
splitTransientSymbol,
|
|
14
|
+
isNodeNamedDeclaration,
|
|
15
|
+
} from "./utils/symbol-utils";
|
|
16
|
+
import { getNodeJSDocComment } from "./utils/ast-utils";
|
|
17
|
+
import { RenameOptions, defaultOptions, VisibilityType } from "./types";
|
|
18
|
+
import { LOGS } from "../config";
|
|
19
|
+
|
|
20
|
+
export function propertiesRenameTransformer(
|
|
21
|
+
program: ts.Program,
|
|
22
|
+
config?: Partial<RenameOptions>,
|
|
23
|
+
): ts.TransformerFactory<ts.SourceFile> {
|
|
24
|
+
return createTransformerFactory(program, config);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function createTransformerFactory(
|
|
28
|
+
program: ts.Program,
|
|
29
|
+
options?: Partial<RenameOptions>,
|
|
30
|
+
): ts.TransformerFactory<ts.SourceFile> {
|
|
31
|
+
const fullOptions: RenameOptions = { ...defaultOptions, ...options };
|
|
32
|
+
const typeChecker = program.getTypeChecker();
|
|
33
|
+
const exportsSymbolTree = new ExportsSymbolTree(program, fullOptions.entrySourceFiles);
|
|
34
|
+
|
|
35
|
+
const cache = new Map<ts.Symbol, VisibilityType>();
|
|
36
|
+
|
|
37
|
+
function putToCache(nodeSymbol: ts.Symbol, val: VisibilityType): VisibilityType {
|
|
38
|
+
cache.set(nodeSymbol, val);
|
|
39
|
+
return val;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (context: ts.TransformationContext) => {
|
|
43
|
+
function transformNodeAndChildren(node: ts.SourceFile, ctx: ts.TransformationContext): ts.SourceFile;
|
|
44
|
+
function transformNodeAndChildren(node: ts.Node, ctx: ts.TransformationContext): ts.Node;
|
|
45
|
+
function transformNodeAndChildren(node: ts.Node, ctx: ts.TransformationContext): ts.Node {
|
|
46
|
+
return ts.visitEachChild(
|
|
47
|
+
transformNode(node),
|
|
48
|
+
(childNode: ts.Node) => transformNodeAndChildren(childNode, ctx),
|
|
49
|
+
ctx,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function transformNode(node: ts.Node): ts.Node {
|
|
54
|
+
// const a = { node }
|
|
55
|
+
if (ts.isShorthandPropertyAssignment(node)) {
|
|
56
|
+
return handleShorthandPropertyAssignment(node);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// const { node } = obj;
|
|
60
|
+
if (ts.isBindingElement(node) && node.propertyName === undefined) {
|
|
61
|
+
if (node.parent && ts.isObjectBindingPattern(node.parent)) {
|
|
62
|
+
return handleShorthandObjectBindingElement(node);
|
|
63
|
+
} else {
|
|
64
|
+
console.warn("!!!", node);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// is not supported:
|
|
69
|
+
// const { node = default_Value } = obj;
|
|
70
|
+
|
|
71
|
+
// 'node' in obj
|
|
72
|
+
if (
|
|
73
|
+
node.parent &&
|
|
74
|
+
ts.isStringLiteral(node) &&
|
|
75
|
+
ts.isBinaryExpression(node.parent) &&
|
|
76
|
+
node.parent.operatorToken.kind === ts.SyntaxKind.InKeyword
|
|
77
|
+
) {
|
|
78
|
+
return handleInKeyword(node);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (ts.isIdentifier(node) && node.parent) {
|
|
82
|
+
// obj.node
|
|
83
|
+
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
|
84
|
+
return handlePropertyAccessIdentifier(node);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// private node
|
|
88
|
+
// public node()
|
|
89
|
+
if (isClassMember(node.parent) && node.parent.name === node) {
|
|
90
|
+
return handleClassMember(node);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// enum Enum { node }
|
|
94
|
+
if (ts.isEnumMember(node.parent) && node.parent.name === node) {
|
|
95
|
+
return handleEnumMember(node);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// const a = { node: 123 }
|
|
99
|
+
if (ts.isPropertyAssignment(node.parent) && node.parent.name === node) {
|
|
100
|
+
return handlePropertyAssignment(node);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// const { node: localName } = obj;
|
|
104
|
+
if (ts.isBindingElement(node.parent) && node.parent.propertyName === node) {
|
|
105
|
+
return handleBindingElement(node);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// <Comp node={...} />
|
|
109
|
+
if (ts.isJsxAttribute(node.parent) && node.parent.name === node) {
|
|
110
|
+
return handleJsxAttributeName(node);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// constructor(public node: string) { // <--- this
|
|
114
|
+
// console.log(node); // <--- and this
|
|
115
|
+
// }
|
|
116
|
+
if (
|
|
117
|
+
(isConstructorParameter(node.parent) && node.parent.name === node) ||
|
|
118
|
+
isConstructorParameterReference(node)
|
|
119
|
+
) {
|
|
120
|
+
return handleCtorParameter(node);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// obj['fooBar']
|
|
125
|
+
if (
|
|
126
|
+
node.parent &&
|
|
127
|
+
ts.isStringLiteral(node) &&
|
|
128
|
+
ts.isElementAccessExpression(node.parent) &&
|
|
129
|
+
node.parent.argumentExpression === node
|
|
130
|
+
) {
|
|
131
|
+
return handleElementAccessExpression(node);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return node;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function handleInKeyword(node: ts.StringLiteral): ts.StringLiteral {
|
|
138
|
+
const parent = node.parent as ts.BinaryExpression;
|
|
139
|
+
const nodeType = typeChecker.getTypeAtLocation(node);
|
|
140
|
+
if (!nodeType.isStringLiteral()) {
|
|
141
|
+
throw new Error(`Can't get type for left expression in ${node.parent.getText()}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const propertyName = nodeType.value;
|
|
145
|
+
if (isTypePropertyExternal(typeChecker.getTypeAtLocation(parent.right), propertyName)) {
|
|
146
|
+
return node;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return createNewNode(propertyName, VisibilityType.Internal, context.factory.createStringLiteral);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// obj.node
|
|
153
|
+
function handlePropertyAccessIdentifier(node: ts.Identifier): ts.Identifier {
|
|
154
|
+
return createNewIdentifier(node);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// obj['fooBar']
|
|
158
|
+
function handleElementAccessExpression(node: ts.StringLiteral): ts.StringLiteral {
|
|
159
|
+
const visibilityType = getNodeVisibilityType(node);
|
|
160
|
+
if (visibilityType === VisibilityType.External) {
|
|
161
|
+
return node;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return createNewNodeFromProperty(node, visibilityType, context.factory.createStringLiteral);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// private node
|
|
168
|
+
// public node()
|
|
169
|
+
function handleClassMember(node: ts.Identifier): ts.Identifier {
|
|
170
|
+
return createNewIdentifier(node);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// enum Enum { node }
|
|
174
|
+
function handleEnumMember(node: ts.Identifier): ts.Identifier {
|
|
175
|
+
return createNewIdentifier(node);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// const { node: localName } = obj;
|
|
179
|
+
function handleBindingElement(node: ts.Identifier): ts.Identifier {
|
|
180
|
+
return createNewIdentifier(node);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// <Comp node={...} />
|
|
184
|
+
function handleJsxAttributeName(node: ts.Identifier): ts.Identifier {
|
|
185
|
+
return createNewIdentifier(node);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// const a = { node: 123 }
|
|
189
|
+
function handlePropertyAssignment(node: ts.Identifier): ts.Identifier {
|
|
190
|
+
return createNewIdentifier(node);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// const a = { node }
|
|
194
|
+
function handleShorthandPropertyAssignment(
|
|
195
|
+
node: ts.ShorthandPropertyAssignment,
|
|
196
|
+
): ts.PropertyAssignment | ts.ShorthandPropertyAssignment {
|
|
197
|
+
const visibilityType = getNodeVisibilityType(node.name);
|
|
198
|
+
if (visibilityType === VisibilityType.External) {
|
|
199
|
+
return node;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return createNewNodeFromProperty(node.name, visibilityType, (newName: string) => {
|
|
203
|
+
return context.factory.createPropertyAssignment(newName, node.name);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// const { node } = obj;
|
|
208
|
+
function handleShorthandObjectBindingElement(node: ts.BindingElement): ts.BindingElement {
|
|
209
|
+
if (!ts.isIdentifier(node.name)) {
|
|
210
|
+
return node;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const visibilityType = getNodeVisibilityType(node);
|
|
214
|
+
if (visibilityType === VisibilityType.External) {
|
|
215
|
+
return node;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return createNewNodeFromProperty(node.name, visibilityType, (newName: string) => {
|
|
219
|
+
return context.factory.createBindingElement(node.dotDotDotToken, newName, node.name, node.initializer);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// constructor(public node: string) { // <--- this
|
|
224
|
+
// console.log(node); // <--- and this
|
|
225
|
+
// }
|
|
226
|
+
function handleCtorParameter(node: ts.Identifier): ts.Identifier {
|
|
227
|
+
return createNewIdentifier(node);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function createNewIdentifier(oldIdentifier: ts.Identifier): ts.Identifier {
|
|
231
|
+
const visibilityType = getNodeVisibilityType(oldIdentifier);
|
|
232
|
+
if (visibilityType === VisibilityType.External) {
|
|
233
|
+
return oldIdentifier;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return createNewNodeFromProperty(oldIdentifier, visibilityType, context.factory.createIdentifier);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function createNewNodeFromProperty<T extends ts.Node>(
|
|
240
|
+
oldProperty: ts.PropertyName,
|
|
241
|
+
type: VisibilityType,
|
|
242
|
+
createNode: (newName: string) => T,
|
|
243
|
+
): T {
|
|
244
|
+
const symbol = typeChecker.getSymbolAtLocation(oldProperty);
|
|
245
|
+
if (symbol === undefined) {
|
|
246
|
+
throw new Error(`Cannot get symbol for node "${oldProperty.getText()}"`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const oldPropertyName = ts.unescapeLeadingUnderscores(symbol.escapedName);
|
|
250
|
+
|
|
251
|
+
return createNewNode(oldPropertyName, type, createNode);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function createNewNode<T extends ts.Node>(
|
|
255
|
+
oldPropertyName: string,
|
|
256
|
+
type: VisibilityType,
|
|
257
|
+
createNode: (newName: string) => T,
|
|
258
|
+
): T {
|
|
259
|
+
const newPropertyName = getNewName(oldPropertyName, type);
|
|
260
|
+
return createNode(newPropertyName);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function getNewName(originalName: string, type: VisibilityType): string {
|
|
264
|
+
return `${
|
|
265
|
+
type === VisibilityType.Private ? fullOptions.privatePrefix : fullOptions.internalPrefix
|
|
266
|
+
}${originalName}`;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function getActualSymbol(symbol: ts.Symbol): ts.Symbol {
|
|
270
|
+
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
271
|
+
symbol = typeChecker.getAliasedSymbol(symbol);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return symbol;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function getNodeSymbol(node: ts.Expression | ts.NamedDeclaration | ts.DeclarationName): ts.Symbol | null {
|
|
278
|
+
const symbol = typeChecker.getSymbolAtLocation(node);
|
|
279
|
+
if (symbol === undefined) {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return getActualSymbol(symbol);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function isTypePropertyExternal(type: ts.Type, typePropertyName: string): boolean {
|
|
287
|
+
// if a type is unknown or any - they should be interpret as a public ones
|
|
288
|
+
if (type.flags & ts.TypeFlags.Unknown || type.flags & ts.TypeFlags.Any) {
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (type.flags & ts.TypeFlags.IndexedAccess) {
|
|
293
|
+
return isTypePropertyExternal(typeChecker.getApparentType(type), typePropertyName);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const symbol = type.getSymbol();
|
|
297
|
+
const propertySymbol = typeChecker.getPropertyOfType(type, typePropertyName);
|
|
298
|
+
|
|
299
|
+
if (type.flags & ts.TypeFlags.Object) {
|
|
300
|
+
const objectType = type as ts.ObjectType;
|
|
301
|
+
|
|
302
|
+
// treat any tuple property as "external"
|
|
303
|
+
if (objectType.objectFlags & ts.ObjectFlags.Tuple) {
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (objectType.objectFlags & ts.ObjectFlags.Reference) {
|
|
308
|
+
const target = (objectType as ts.TypeReference).target;
|
|
309
|
+
if (target !== objectType && isTypePropertyExternal(target, typePropertyName)) {
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// in case when we can't get where a property come from in mapped types
|
|
315
|
+
// let's check the whole type explicitly
|
|
316
|
+
// thus in case of when property doesn't have a declaration let's treat any property of a mapped type as "external" if its parent type is external
|
|
317
|
+
// e.g. Readonly<Foo>.field will look for `field` in _Foo_ type (not in Readonly<Foo>), but { [K in 'foo' | 'bar']: any } won't
|
|
318
|
+
// perhaps it would be awesome to handle exactly property we have, but ¯\_(ツ)_/¯
|
|
319
|
+
const propertyHasDeclarations =
|
|
320
|
+
propertySymbol !== undefined ? getDeclarationsForSymbol(propertySymbol).length !== 0 : false;
|
|
321
|
+
if (
|
|
322
|
+
objectType.objectFlags & ts.ObjectFlags.Mapped &&
|
|
323
|
+
symbol !== undefined &&
|
|
324
|
+
!propertyHasDeclarations &&
|
|
325
|
+
getSymbolVisibilityType(symbol) === VisibilityType.External
|
|
326
|
+
) {
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (type.isUnionOrIntersection()) {
|
|
332
|
+
const hasExternalSubType = type.types.some((t: ts.Type) => isTypePropertyExternal(t, typePropertyName));
|
|
333
|
+
if (hasExternalSubType) {
|
|
334
|
+
return hasExternalSubType;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (symbol !== undefined) {
|
|
339
|
+
const declarations = getDeclarationsForSymbol(symbol);
|
|
340
|
+
for (const declaration of declarations) {
|
|
341
|
+
if (
|
|
342
|
+
(ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) &&
|
|
343
|
+
declaration.heritageClauses !== undefined
|
|
344
|
+
) {
|
|
345
|
+
const hasHeritageClausesExternals = declaration.heritageClauses.some((clause: ts.HeritageClause) => {
|
|
346
|
+
return clause.types.some((expr: ts.ExpressionWithTypeArguments) => {
|
|
347
|
+
return isTypePropertyExternal(typeChecker.getTypeAtLocation(expr), typePropertyName);
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
if (hasHeritageClausesExternals) {
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (propertySymbol === undefined) {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return [propertySymbol, ...splitTransientSymbol(propertySymbol, typeChecker)].some(
|
|
363
|
+
(sym: ts.Symbol) => getSymbolVisibilityType(sym) === VisibilityType.External,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function getNodeVisibilityType(
|
|
368
|
+
node: ts.Expression | ts.Identifier | ts.StringLiteral | ts.BindingElement,
|
|
369
|
+
): VisibilityType {
|
|
370
|
+
if (ts.isPropertyAssignment(node.parent) || ts.isShorthandPropertyAssignment(node.parent)) {
|
|
371
|
+
let expressionToGetTypeFrom: ts.Expression = node.parent.parent;
|
|
372
|
+
let lastKnownExpression: ts.AsExpression | ts.ObjectLiteralExpression = node.parent.parent;
|
|
373
|
+
let currentNode: ts.Node = node.parent.parent.parent;
|
|
374
|
+
|
|
375
|
+
while (ts.isParenthesizedExpression(currentNode) || ts.isAsExpression(currentNode)) {
|
|
376
|
+
if (ts.isAsExpression(currentNode)) {
|
|
377
|
+
expressionToGetTypeFrom = lastKnownExpression;
|
|
378
|
+
lastKnownExpression = currentNode;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
currentNode = currentNode.parent;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// to get correct contextual type we need to provide the previous last expression rather than the last one
|
|
385
|
+
const type = typeChecker.getContextualType(expressionToGetTypeFrom);
|
|
386
|
+
if (type !== undefined && isTypePropertyExternal(type, node.getText())) {
|
|
387
|
+
return VisibilityType.External;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (ts.isPropertyAccessExpression(node.parent) || ts.isElementAccessExpression(node.parent)) {
|
|
392
|
+
if (ts.isIdentifier(node.parent.expression)) {
|
|
393
|
+
const expressionSymbol = typeChecker.getSymbolAtLocation(node.parent.expression);
|
|
394
|
+
if (LOGS) {
|
|
395
|
+
console.log(
|
|
396
|
+
`[transformer] PropertyAccess: expression=${node.parent.expression.getText()}, property=${node.getText()}`,
|
|
397
|
+
);
|
|
398
|
+
console.log(`[transformer] Expression symbol: ${expressionSymbol?.name}`);
|
|
399
|
+
}
|
|
400
|
+
if (expressionSymbol !== undefined) {
|
|
401
|
+
// import * as foo from '...';
|
|
402
|
+
// foo.node;
|
|
403
|
+
// foo['node'];
|
|
404
|
+
// or
|
|
405
|
+
// namespace Foo { ... }
|
|
406
|
+
// Foo.node
|
|
407
|
+
// Foo['node'];
|
|
408
|
+
const declarations = getDeclarationsForSymbol(expressionSymbol);
|
|
409
|
+
if (LOGS) {
|
|
410
|
+
console.log(`[transformer] Declarations: ${declarations.map((d) => ts.SyntaxKind[d.kind]).join(", ")}`);
|
|
411
|
+
}
|
|
412
|
+
const isModuleOrStarImport = declarations.some(
|
|
413
|
+
(decl: ts.Declaration) => ts.isNamespaceImport(decl) || ts.isModuleDeclaration(decl),
|
|
414
|
+
);
|
|
415
|
+
if (LOGS) {
|
|
416
|
+
console.log(`[transformer] isModuleOrStarImport: ${isModuleOrStarImport}`);
|
|
417
|
+
}
|
|
418
|
+
if (isModuleOrStarImport) {
|
|
419
|
+
// treat accessing to import-star or namespace as external one
|
|
420
|
+
// because we can't rename them yet
|
|
421
|
+
if (LOGS) {
|
|
422
|
+
console.log(`[transformer] Returning External (module/star import)`);
|
|
423
|
+
}
|
|
424
|
+
return VisibilityType.External;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// import { foo } from '...';
|
|
428
|
+
// where foo is a namespace re-export (export * as foo from './bar')
|
|
429
|
+
// In this case, symbol for 'foo' is an alias to a module
|
|
430
|
+
// Check if the symbol's type is a namespace module or regular module
|
|
431
|
+
const hasImportSpecifier = declarations.some((decl) => ts.isImportSpecifier(decl));
|
|
432
|
+
if (LOGS) {
|
|
433
|
+
console.log(`[transformer] hasImportSpecifier: ${hasImportSpecifier}`);
|
|
434
|
+
}
|
|
435
|
+
if (hasImportSpecifier) {
|
|
436
|
+
const expressionType = typeChecker.getTypeAtLocation(node.parent.expression);
|
|
437
|
+
if (LOGS) {
|
|
438
|
+
console.log(`[transformer] Expression type: ${expressionType?.flags}`);
|
|
439
|
+
}
|
|
440
|
+
if (expressionType) {
|
|
441
|
+
const typeSymbol = expressionType.getSymbol();
|
|
442
|
+
if (LOGS) {
|
|
443
|
+
console.log(`[transformer] Type symbol: ${typeSymbol?.name}, flags: ${typeSymbol?.flags}`);
|
|
444
|
+
}
|
|
445
|
+
// Check if it's a module (NamespaceModule or regular Module)
|
|
446
|
+
// NamespaceModule: 0x8000000, Module: 0x200
|
|
447
|
+
const isModule =
|
|
448
|
+
typeSymbol &&
|
|
449
|
+
((typeSymbol.flags & ts.SymbolFlags.NamespaceModule) !== 0 ||
|
|
450
|
+
(typeSymbol.flags & ts.SymbolFlags.Module) !== 0);
|
|
451
|
+
if (isModule) {
|
|
452
|
+
// The expression's type is a module, so accessing its properties
|
|
453
|
+
// means accessing exports from that module, which are external
|
|
454
|
+
if (LOGS) {
|
|
455
|
+
console.log(`[transformer] Returning External (module)`);
|
|
456
|
+
}
|
|
457
|
+
return VisibilityType.External;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const expressionType = typeChecker.getTypeAtLocation(node.parent.expression);
|
|
465
|
+
const propertyText = node.getText();
|
|
466
|
+
if (LOGS) {
|
|
467
|
+
console.log(`[transformer] Checking isTypePropertyExternal for ${propertyText}`);
|
|
468
|
+
}
|
|
469
|
+
if (isTypePropertyExternal(expressionType, propertyText)) {
|
|
470
|
+
if (LOGS) {
|
|
471
|
+
console.log(`[transformer] Returning External (isTypePropertyExternal)`);
|
|
472
|
+
}
|
|
473
|
+
return VisibilityType.External;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// shorthand binding element
|
|
478
|
+
// const { node } = obj;
|
|
479
|
+
if (
|
|
480
|
+
ts.isBindingElement(node) &&
|
|
481
|
+
isTypePropertyExternal(typeChecker.getTypeAtLocation(node.parent), node.getText())
|
|
482
|
+
) {
|
|
483
|
+
return VisibilityType.External;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// full binding element
|
|
487
|
+
// const { node: propName } = obj;
|
|
488
|
+
if (
|
|
489
|
+
ts.isBindingElement(node.parent) &&
|
|
490
|
+
isTypePropertyExternal(typeChecker.getTypeAtLocation(node.parent.parent), node.getText())
|
|
491
|
+
) {
|
|
492
|
+
return VisibilityType.External;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// <Comp node={...} />
|
|
496
|
+
if (ts.isJsxAttribute(node.parent)) {
|
|
497
|
+
const jsxTagSymbol = typeChecker.getSymbolAtLocation(node.parent.parent.parent.tagName);
|
|
498
|
+
if (jsxTagSymbol !== undefined && jsxTagSymbol.valueDeclaration !== undefined) {
|
|
499
|
+
const jsxPropsType = typeChecker.getTypeOfSymbolAtLocation(jsxTagSymbol, jsxTagSymbol.valueDeclaration);
|
|
500
|
+
if (isTypePropertyExternal(jsxPropsType, node.getText())) {
|
|
501
|
+
return VisibilityType.External;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const nodeSymbol = getNodeSymbol(node);
|
|
507
|
+
const classOfMember = nodeSymbol !== null ? getClassOfMemberSymbol(nodeSymbol) : null;
|
|
508
|
+
if (
|
|
509
|
+
classOfMember !== null &&
|
|
510
|
+
isTypePropertyExternal(typeChecker.getTypeAtLocation(classOfMember), node.getText())
|
|
511
|
+
) {
|
|
512
|
+
return VisibilityType.External;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const symbol = ts.isBindingElement(node) ? getShorthandObjectBindingElementSymbol(node) : nodeSymbol;
|
|
516
|
+
if (symbol === null) {
|
|
517
|
+
return VisibilityType.External;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return getSymbolVisibilityType(symbol);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function getSymbolVisibilityType(nodeSymbol: ts.Symbol): VisibilityType {
|
|
524
|
+
nodeSymbol = getActualSymbol(nodeSymbol);
|
|
525
|
+
|
|
526
|
+
const cachedValue = cache.get(nodeSymbol);
|
|
527
|
+
if (cachedValue !== undefined) {
|
|
528
|
+
return cachedValue;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const symbolDeclarations = getDeclarationsForSymbol(nodeSymbol);
|
|
532
|
+
if (symbolDeclarations.some(isDeclarationFromExternals)) {
|
|
533
|
+
return putToCache(nodeSymbol, VisibilityType.External);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (symbolDeclarations.some((decl: ts.Declaration) => hasModifier(decl, ts.SyntaxKind.DeclareKeyword))) {
|
|
537
|
+
return putToCache(nodeSymbol, VisibilityType.External);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (nodeSymbol.escapedName === "prototype") {
|
|
541
|
+
// accessing to prototype
|
|
542
|
+
return putToCache(nodeSymbol, VisibilityType.External);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (fullOptions.publicJSDocTag.length !== 0 || fullOptions.ignoreDecorated) {
|
|
546
|
+
for (const declaration of symbolDeclarations) {
|
|
547
|
+
let currentNode: ts.Node = declaration;
|
|
548
|
+
while (!ts.isSourceFile(currentNode)) {
|
|
549
|
+
if (
|
|
550
|
+
fullOptions.publicJSDocTag.length !== 0 &&
|
|
551
|
+
getNodeJSDocComment(currentNode).includes(`@${fullOptions.publicJSDocTag}`)
|
|
552
|
+
) {
|
|
553
|
+
return putToCache(nodeSymbol, VisibilityType.External);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (fullOptions.ignoreDecorated && hasDecorators(currentNode)) {
|
|
557
|
+
return putToCache(nodeSymbol, VisibilityType.External);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// TODO: mute enum renames
|
|
561
|
+
if (ts.isEnumDeclaration(currentNode)) {
|
|
562
|
+
return putToCache(nodeSymbol, VisibilityType.External);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
currentNode = currentNode.parent;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (isPrivateClassMember(nodeSymbol)) {
|
|
571
|
+
return putToCache(nodeSymbol, VisibilityType.Private);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (exportsSymbolTree.isSymbolAccessibleFromExports(nodeSymbol)) {
|
|
575
|
+
return putToCache(nodeSymbol, VisibilityType.External);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
for (const declaration of symbolDeclarations) {
|
|
579
|
+
if (!isNodeNamedDeclaration(declaration.parent) || declaration.parent.name === undefined) {
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const parentSymbol = getNodeSymbol(declaration.parent.name);
|
|
584
|
+
if (parentSymbol === null) {
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (getSymbolVisibilityType(parentSymbol) === VisibilityType.External) {
|
|
589
|
+
return putToCache(nodeSymbol, VisibilityType.External);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
return putToCache(nodeSymbol, VisibilityType.Internal);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function getShorthandObjectBindingElementSymbol(element: ts.BindingElement): ts.Symbol | null {
|
|
597
|
+
if (element.propertyName !== undefined) {
|
|
598
|
+
throw new Error(
|
|
599
|
+
`Cannot handle binding element with property name: ${element.getText()} in ${
|
|
600
|
+
element.getSourceFile().fileName
|
|
601
|
+
}`,
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (!ts.isIdentifier(element.name)) {
|
|
606
|
+
return null;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// if no property name is set (const { a } = foo)
|
|
610
|
+
// then node.propertyName is undefined and we need to find this property ourselves
|
|
611
|
+
// so let's use go-to-definition algorithm from TSServer
|
|
612
|
+
// see https://github.com/microsoft/TypeScript/blob/672b0e3e16ad18b422dbe0cec5a98fce49881b76/src/services/goToDefinition.ts#L58-L77
|
|
613
|
+
const type = typeChecker.getTypeAtLocation(element.parent as ts.Node);
|
|
614
|
+
if (type.isUnion()) {
|
|
615
|
+
return null;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return type.getProperty(ts.idText(element.name)) || null;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function isDeclarationFromExternals(declaration: ts.Declaration): boolean {
|
|
622
|
+
const sourceFile = declaration.getSourceFile();
|
|
623
|
+
|
|
624
|
+
// all declarations from declaration source files are external by default
|
|
625
|
+
return (
|
|
626
|
+
sourceFile.isDeclarationFile ||
|
|
627
|
+
program.isSourceFileDefaultLibrary(sourceFile) ||
|
|
628
|
+
/[\\/]node_modules[\\/]/.test(sourceFile.fileName)
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function isConstructorParameterReference(node: ts.Node): node is ts.Identifier {
|
|
633
|
+
if (!ts.isIdentifier(node)) {
|
|
634
|
+
return false;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
return isSymbolClassMember(typeChecker.getSymbolAtLocation(node));
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return (sourceFile: ts.SourceFile) => transformNodeAndChildren(sourceFile, context);
|
|
641
|
+
};
|
|
642
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface RenameOptions {
|
|
2
|
+
/**
|
|
3
|
+
* An array of entry source files which will used to detect exported and internal fields.
|
|
4
|
+
* Basically it should be entry point(s) of the library/project.
|
|
5
|
+
* @example ['./src/index.ts']
|
|
6
|
+
*/
|
|
7
|
+
entrySourceFiles: string[];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Prefix of generated names for private fields
|
|
11
|
+
* @example '_private_' // default
|
|
12
|
+
* @example '$p$'
|
|
13
|
+
*/
|
|
14
|
+
privatePrefix: string;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Prefix of generated names for internal fields
|
|
18
|
+
* @example '_internal_' // default
|
|
19
|
+
* @example '$i$'
|
|
20
|
+
*/
|
|
21
|
+
internalPrefix: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Comment which will treat a class/interface/type/property/etc and all its children as "public".
|
|
25
|
+
* Set it to empty string to disable using JSDoc comment to detecting "visibility level".
|
|
26
|
+
* @example 'public' // default
|
|
27
|
+
* @example 'external'
|
|
28
|
+
* @example ''
|
|
29
|
+
*/
|
|
30
|
+
publicJSDocTag: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Whether fields that were decorated should be renamed.
|
|
34
|
+
* A field is treated as "decorated" if itself or any its parent (on type level) has a decorator.
|
|
35
|
+
*/
|
|
36
|
+
ignoreDecorated: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const enum VisibilityType {
|
|
40
|
+
Internal = 0,
|
|
41
|
+
Private = 1,
|
|
42
|
+
External = 2,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const defaultOptions: RenameOptions = {
|
|
46
|
+
entrySourceFiles: [],
|
|
47
|
+
privatePrefix: "$p$",
|
|
48
|
+
internalPrefix: "$i$",
|
|
49
|
+
publicJSDocTag: "public",
|
|
50
|
+
ignoreDecorated: false,
|
|
51
|
+
};
|