ts-transformer-properties-rename
Version:
A TypeScript custom transformer which renames internal properties of the project
184 lines (183 loc) • 7.05 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isNodeNamedDeclaration = isNodeNamedDeclaration;
exports.getActualSymbol = getActualSymbol;
exports.splitTransientSymbol = splitTransientSymbol;
exports.getDeclarationsForSymbol = getDeclarationsForSymbol;
exports.getExportsForSourceFile = getExportsForSourceFile;
exports.isClassMember = isClassMember;
exports.getClassOfMemberSymbol = getClassOfMemberSymbol;
exports.hasPrivateKeyword = hasPrivateKeyword;
exports.hasModifier = hasModifier;
exports.hasDecorators = hasDecorators;
exports.isConstructorParameter = isConstructorParameter;
exports.isSymbolClassMember = isSymbolClassMember;
exports.isPrivateClassMember = isPrivateClassMember;
exports.getNodeJSDocComment = getNodeJSDocComment;
const ts = require("typescript");
const namedDeclarationKinds = [
ts.SyntaxKind.InterfaceDeclaration,
ts.SyntaxKind.ClassDeclaration,
ts.SyntaxKind.EnumDeclaration,
ts.SyntaxKind.TypeAliasDeclaration,
ts.SyntaxKind.ModuleDeclaration,
ts.SyntaxKind.FunctionDeclaration,
ts.SyntaxKind.VariableDeclaration,
ts.SyntaxKind.PropertySignature,
ts.SyntaxKind.Parameter,
];
function isNodeNamedDeclaration(node) {
return namedDeclarationKinds.indexOf(node.kind) !== -1;
}
function getActualSymbol(symbol, typeChecker) {
if (symbol.flags & ts.SymbolFlags.Alias) {
symbol = typeChecker.getAliasedSymbol(symbol);
}
return symbol;
}
function splitTransientSymbol(symbol, typeChecker) {
// actually I think we even don't need to operate/use "Transient" symbols anywhere
// it's kind of aliased symbol, but just merged
// but it's hard to refractor everything to use array of symbols instead of just symbol
// so let's fix it for some places
if ((symbol.flags & ts.SymbolFlags.Transient) === 0) {
return [symbol];
}
// "Transient" symbol is kinda "merged" symbol
// I don't really know is this way to "split" is correct
// but it seems that it works for now ¯\_(ツ)_/¯
const declarations = getDeclarationsForSymbol(symbol);
const result = [];
for (const declaration of declarations) {
if (!isNodeNamedDeclaration(declaration) || declaration.name === undefined) {
continue;
}
const sym = typeChecker.getSymbolAtLocation(declaration.name);
if (sym === undefined) {
continue;
}
result.push(getActualSymbol(sym, typeChecker));
}
return result;
}
function getDeclarationsForSymbol(symbol) {
const result = [];
if (symbol.declarations !== undefined) {
result.push(...symbol.declarations);
}
if (symbol.valueDeclaration !== undefined) {
// push valueDeclaration might be already in declarations array
// so let's check first to avoid duplication nodes
if (!result.includes(symbol.valueDeclaration)) {
result.push(symbol.valueDeclaration);
}
}
return result;
}
function getExportsForSourceFile(typeChecker, sourceFileSymbol) {
if (sourceFileSymbol.exports !== undefined) {
const commonJsExport = sourceFileSymbol.exports.get(ts.InternalSymbolName.ExportEquals);
if (commonJsExport !== undefined) {
return [
getActualSymbol(commonJsExport, typeChecker),
];
}
}
const result = typeChecker.getExportsOfModule(sourceFileSymbol);
if (sourceFileSymbol.exports !== undefined) {
const defaultExportSymbol = sourceFileSymbol.exports.get(ts.InternalSymbolName.Default);
if (defaultExportSymbol !== undefined) {
if (!result.includes(defaultExportSymbol)) {
// it seems that default export is always returned by getExportsOfModule
// but let's add it to be sure add if there is no such export
result.push(defaultExportSymbol);
}
}
}
return result.map((symbol) => getActualSymbol(symbol, typeChecker));
}
function isClassMember(node) {
return ts.isMethodDeclaration(node) || ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node);
}
function getClassOfMemberSymbol(nodeSymbol) {
const classMembers = getClassMemberDeclarations(nodeSymbol);
if (classMembers.length !== 0) {
// we need any member to get class' declaration
const classMember = classMembers[0];
if (isConstructorParameter(classMember)) {
return classMember.parent.parent;
}
// we're sure that it is a class, not interface
return classMember.parent;
}
return null;
}
function hasPrivateKeyword(node) {
return hasModifier(node, ts.SyntaxKind.PrivateKeyword);
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
function getModifiers(node) {
if (isBreakingTypeScriptApi(ts)) {
if (!ts.canHaveModifiers(node)) {
return [];
}
return ts.getModifiers(node) || [];
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line deprecation/deprecation, @typescript-eslint/no-unsafe-return
return node.modifiers || [];
}
function hasModifier(node, modifier) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return getModifiers(node).some(mod => mod.kind === modifier);
}
function getDecorators(node) {
if (isBreakingTypeScriptApi(ts)) {
if (!ts.canHaveDecorators(node)) {
return [];
}
return ts.getDecorators(node) || [];
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line deprecation/deprecation, @typescript-eslint/no-unsafe-return
return node.decorators || [];
}
function hasDecorators(node) {
return getDecorators(node).length !== 0;
}
function isConstructorParameter(node) {
return ts.isParameter(node) &&
ts.isConstructorDeclaration(node.parent) &&
(hasModifier(node, ts.SyntaxKind.PublicKeyword) ||
hasModifier(node, ts.SyntaxKind.ProtectedKeyword) ||
hasModifier(node, ts.SyntaxKind.PrivateKeyword));
}
function getClassMemberDeclarations(symbol) {
if (symbol === undefined) {
return [];
}
const declarations = symbol.getDeclarations();
if (declarations === undefined) {
return [];
}
return declarations.filter((x) => {
return isClassMember(x) || isConstructorParameter(x);
});
}
function isSymbolClassMember(symbol) {
return getClassMemberDeclarations(symbol).length !== 0;
}
function isPrivateClassMember(symbol) {
return getClassMemberDeclarations(symbol).some(hasPrivateKeyword);
}
function getNodeJSDocComment(node) {
const start = node.getStart();
const jsDocStart = node.getStart(undefined, true);
return node.getSourceFile().getFullText().substring(jsDocStart, start).trim();
}
function isBreakingTypeScriptApi(compiler) {
return 'canHaveDecorators' in compiler;
}