UNPKG

ts-transformer-properties-rename

Version:

A TypeScript custom transformer which renames internal properties of the project

184 lines (183 loc) 7.05 kB
"use strict"; 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; }