Jscodeshift рдФрд░ TypeScript рдореЗрдВ рдкреНрд░рдХрд╛рд░ рдХрд╛ рдЕрдиреБрдорд╛рди

Jscodeshift рдФрд░ TypeScript рдореЗрдВ рдкреНрд░рдХрд╛рд░ рдХрд╛ рдЕрдиреБрдорд╛рди


рд╕рдВрд╕реНрдХрд░рдг 6.0 рд╕реЗ рд╢реБрд░реВ рд╣реЛрдХрд░, jscodeshift рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ (рдЯреАрдПрд╕) рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИред рдХреЛрдбреЗрдореЛрдб (рдкрд░рд┐рд╡рд░реНрддрди) рд▓рд┐рдЦрдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореЗрдВ, рдЖрдкрдХреЛ рдЙрд╕ рдкреНрд░рдХрд╛рд░ рдХреЗ рдЪрд░ рдХрд╛ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛ рд╕рдХрддреА рд╣реИ рдЬрд┐рд╕рдореЗрдВ рд╕реНрдкрд╖реНрдЯ рдПрдиреЛрдЯреЗрд╢рди рдирд╣реАрдВ рд╣реИред рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ, jscodeshift рдмреЙрдХреНрд╕ рд╕реЗ рдирд┐рдореНрди рдкреНрд░рдХрд╛рд░ рдХреЗ рд╕рд╛рдзрди рдкреНрд░рджрд╛рди рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред


рдПрдХ рдЙрджрд╛рд╣рд░рдг рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВред рдорд╛рди рд▓реАрдЬрд┐рдП рдХрд┐ рд╣рдо рдПрдХ рдкрд░рд┐рд╡рд░реНрддрди рд▓рд┐рдЦрдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдЬреЛ рд╡рд░реНрдЧ рдХрд╛рд░реНрдпреЛрдВ рдФрд░ рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕реНрдкрд╖реНрдЯ рд░рд┐рдЯрд░реНрди рдкреНрд░рдХрд╛рд░ рдЬреЛрдбрд╝рддрд╛ рд╣реИред рдпрд╛рдиреА рдкреНрд░рд╡реЗрд╢ рджреНрд╡рд╛рд░ рдкрд░ рд╣реЛрдирд╛:


function foo(x: number) { return x; } 

рд╣рдо рдЖрдЙрдЯрдкреБрдЯ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ:


 function foo(x: number): number { return x; } 

рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ, рд╕рд╛рдорд╛рдиреНрдп рдорд╛рдорд▓реЗ рдореЗрдВ, рдЗрд╕ рддрд░рд╣ рдХреА рд╕рдорд╕реНрдпрд╛ рдХрд╛ рд╕рдорд╛рдзрд╛рди рдмрд╣реБрдд nontrivial рд╣реИред рдпрд╣рд╛рдБ рдХреБрдЫ рдЙрджрд╛рд╣рд░рдг рджрд┐рдП рдЧрдП рд╣реИрдВ:


 function toString(x: number) { return '' + x; } function toInt(str: string) { return parseInt(str); } function toIntArray(strings: string[]) { return strings.map(Number.parseInt); } class Foo1 { constructor(public x = 0) { } getX() { return this.x; } } class Foo2 { x: number; constructor(x = 0) { this.x = x; } getX() { return this.x; } } function foo1(foo: Foo1) { return foo.getX(); } function foo2(foo: Foo2) { return foo.getX(); } 

рд╕реМрднрд╛рдЧреНрдп рд╕реЗ, рдЯреАрдПрд╕ рд╕рдВрдХрд▓рдХ рдХреЗ рдЕрдВрджрд░ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЗрдирдлреЗрдХреНрд╢рди рдХреА рд╕рдорд╕реНрдпрд╛ рд╣рд▓ рд╣реЛ рдЧрдИ рд╣реИред рдХрдВрдкрд╛рдЗрд▓рд░ рдПрдкреАрдЖрдИ рдПрдХ рдкреНрд░рдХрд╛рд░ рдХрд╛ рдЕрдиреБрдорд╛рди рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ рдЬрд┐рд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдЖрдк рдкрд░рд┐рд╡рд░реНрддрди рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред


рд╣рд╛рд▓рд╛рдБрдХрд┐, рдЖрдк рдХреЗрд╡рд▓ jscodeshift рдкрд╛рд░реНрд╕рд░ рдХреЛ рдУрд╡рд░рд░рд╛рдЗрдб рдХрд░рдХреЗ TS рдХрдВрдкрд╛рдЗрд▓рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рддрдереНрдп рдпрд╣ рд╣реИ рдХрд┐ jscodeshift рдХреЛ рдПрд╕реНрдЯреНрд░реА рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдмрд╛рд╣рд░реА рдкрд╛рд░реНрд╕рд░ рд╕реЗ рдПрдХ рд╕рд╛рд░ рд╕рд┐рдВрдЯреИрдХреНрд╕ рдЯреНрд░реА (рдПрдПрд╕рдЯреА) рдХреА рдЙрдореНрдореАрдж рд╣реИред рдФрд░ рдЯреАрдПрд╕ рд╕рдВрдХрд▓рдХ рдПрдПрд╕рдЯреА рдирд╣реАрдВ рд╣реИред


рдмреЗрд╢рдХ, рдХреЛрдИ рднреА jscodeshift рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдмрд┐рдирд╛ TS рд╕рдВрдХрд▓рдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддрд╛ рд╣реИ, рдЦрд░реЛрдВрдЪ рд╕реЗ рдПрдХ рдкрд░рд┐рд╡рд░реНрддрди рд▓рд┐рдЦ рд░рд╣рд╛ рд╣реИред рдпрд╛ TS рдЙрдкрдХрд░рдг рдореЗрдВ рдореМрдЬреВрдж рдХрд┐рд╕реА рдПрдХ рдЙрдкрдХрд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, ts-morph ред рд▓реЗрдХрд┐рди рдХрдИ рд▓реЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП, jscodeshift рдПрдХ рдЕрдзрд┐рдХ рдкрд░рд┐рдЪрд┐рдд рдФрд░ рдЕрднрд┐рд╡реНрдпрдВрдЬрдХ рд╕рдорд╛рдзрд╛рди рд╣реЛрдЧрд╛ред рдЗрд╕рд▓рд┐рдП, рд╣рдо рдЖрдЧреЗ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВрдЧреЗ рдХрд┐ рдЗрд╕ рд╕реАрдорд╛ рдХреЗ рдЖрд╕рдкрд╛рд╕ рдХреИрд╕реЗ рдкрд╣реБрдВрдЪреЗрдВред


рд╡рд┐рдЪрд╛рд░ jscodeshift рдкрд╛рд░реНрд╕рд░ рдПрдПрд╕рдЯреА (рдмрд╛рдж рдореЗрдВ рдПрд╕реНрдЯреНрд░реА) рд╕реЗ рдЯреАрдПрд╕ рд╕рдВрдХрд▓рдХ рдПрдПрд╕рдЯреА (рдЗрд╕рдХреЗ рдмрд╛рдж рдЯреАрдПрд╕рдЯреАрдЖрд░рдЖрдИ) рд╕реЗ рдореИрдкрд┐рдВрдЧ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣реИ, рдФрд░ рдЙрд╕рдХреЗ рдмрд╛рдж рдЯреАрдПрд╕ рдХрдВрдкрд╛рдЗрд▓рд░ рдкреНрд░рдХрд╛рд░ рдХреЗ рдЖрд╡рд┐рд╖реНрдХрд╛рд░ рдЙрдкрдХрд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред рдЕрдЧрд▓рд╛, рд╣рдо рдЗрд╕ рд╡рд┐рдЪрд╛рд░ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рджреЛ рддрд░реАрдХреЛрдВ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВрдЧреЗред


рдкрдВрдХреНрддрд┐ рдФрд░ рд╕реНрддрдВрдн рд╕рдВрдЦреНрдпрд╛рдУрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░реЗрдВ


рдкрд╣рд▓реА рд╡рд┐рдзрд┐ TSTree рд╕реЗ ESTRE рддрдХ рдорд╛рдирдЪрд┐рддреНрд░рдг рдХреЛ рдЦреЛрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдиреЛрдбреНрд╕ рдХреА рдкрдВрдХреНрддрд┐ рдФрд░ рд╕реНрддрдВрдн рд╕рдВрдЦреНрдпрд╛ (рд╕реНрдерд┐рддрд┐) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреА рд╣реИред рдЗрд╕ рддрдереНрдп рдХреЗ рдмрд╛рд╡рдЬреВрдж рдХрд┐ рд╕рд╛рдорд╛рдиреНрдп рд╕реНрдерд┐рддрд┐ рдореЗрдВ рдиреЛрдбреНрд╕ рдХреА рд╕реНрдерд┐рддрд┐ рд╕рдВрдпреЛрдЧ рдирд╣реАрдВ рд╣реЛ рд╕рдХрддреА рд╣реИ, рдкреНрд░рддреНрдпреЗрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдорд╛рдорд▓реЗ рдореЗрдВ рд╡рд╛рдВрдЫрд┐рдд рдкреНрд░рджрд░реНрд╢рди рдвреВрдВрдврдирд╛ рд▓рдЧрднрдЧ рд╣рдореЗрд╢рд╛ рд╕рдВрднрд╡ рд╣реЛрддрд╛ рд╣реИред


рддреЛ, рдЪрд▓рд┐рдП рдПрдХ рдкрд░рд┐рд╡рд░реНрддрди рд▓рд┐рдЦрддреЗ рд╣реИрдВ рдЬреЛ рд╕реНрдкрд╖реНрдЯ рдПрдиреЛрдЯреЗрд╢рди рдЬреЛрдбрд╝рдиреЗ рдХрд╛ рдХрд╛рд░реНрдп рдХрд░реЗрдЧрд╛ред рдореБрдЭреЗ рдЖрдкрдХреЛ рдпрд╛рдж рджрд┐рд▓рд╛рдирд╛ рдЪрд╛рд╣рд┐рдП, рдЖрдЙрдЯрдкреБрдЯ рдкрд░ рд╣рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдорд┐рд▓рдирд╛ рдЪрд╛рд╣рд┐рдП:


 function toString(x: number): number { return '' + x; } function toInt(str: string): number { return parseInt(str); } function toIntArray(strings: string[]): number[] { return strings.map(Number.parseInt); } class Foo1 { constructor(public x = 0) { } getX(): number { return this.x; } } class Foo2 { x: number; constructor(x = 0) { this.x = x; } getX(): number { return this.x; } } function foo1(foo: Foo1): number { return foo.getX(); } function foo2(foo: Foo2): number { return foo.getX(); } 

рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╣рдореЗрдВ рдПрдХ TSTree рдмрдирд╛рдиреЗ рдФрд░ typeChecker рдХрдВрдкрд╛рдЗрд▓рд░ typeChecker :


 const compilerOptions = { target: ts.ScriptTarget.Latest }; const program = ts.createProgram([path], compilerOptions); const sourceFile = program.getSourceFile(path); const typeChecker = program.getTypeChecker(); 

рдЗрд╕рдХреЗ рдмрд╛рдж, рд╢реБрд░реБрдЖрддреА рд╕реНрдерд┐рддрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрд╕реНрдЯреНрд░реА рд╕реЗ рдЯреАрдПрд╕рдЯреНрд░реА рддрдХ рдПрдХ рдореИрдкрд┐рдВрдЧ рдмрдирд╛рдПрдВред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рджреЛ-рд╕реНрддрд░реАрдп Map рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗ (рдкрд╣рд▓рд╛ рд╕реНрддрд░ рдкрдВрдХреНрддрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рд╣реИ, рджреВрд╕рд░рд╛ рд╕реНрддрд░ рд╕реНрддрдВрднреЛрдВ рдХреЗ рд▓рд┐рдП рд╣реИ, рдкрд░рд┐рдгрд╛рдо рдПрдХ TSTree рдиреЛрдб рд╣реИ):


 const locToTSNodeMap = new Map(); const esTreeNodeToTSNode = ({ loc: { start: { line, column } } }) => locToTSNodeMap.has(line) ? locToTSNodeMap.get(line).get(column) : undefined; (function buildLocToTSNodeMap(node) { const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)); const nextLine = line + 1; if (!locToTSNodeMap.has(nextLine)) locToTSNodeMap.set(nextLine, new Map()); locToTSNodeMap.get(nextLine).set(character, node); ts.forEachChild(node, buildLocToTSNodeMap); }(sourceFile)); 

рд▓рд╛рдЗрди рд╕рдВрдЦреНрдпрд╛ рдХреЛ рд╕рдорд╛рдпреЛрдЬрд┐рдд рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИ, рдЬреИрд╕рд╛ рдХрд┐ TSTree рдореЗрдВ, рдкрдВрдХреНрддрд┐ рд╕рдВрдЦреНрдпрд╛рдПрдВ рд╢реВрдиреНрдп рд╕реЗ рд╢реБрд░реВ рд╣реЛрддреА рд╣реИрдВ, рдФрд░ рдПрдХ рд╕реЗ рдПрд╕реНрдЯреНрд░реА рдореЗрдВред


рдЕрдЧрд▓рд╛, рд╣рдореЗрдВ рдХрдХреНрд╖рд╛рдУрдВ рдХреЗ рд╕рднреА рдХрд╛рд░реНрдпреЛрдВ рдФрд░ рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЗ рдЪрд╛рд░реЛрдВ рдУрд░ рдЬрд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рд░рд┐рдЯрд░реНрди рдкреНрд░рдХрд╛рд░ рдХреА рдЬрд╛рдВрдЪ рдХрд░реЗрдВ рдФрд░ рдпрджрд┐ рдпрд╣ null , рддреЛ рдЯрд╛рдЗрдк рдХрд╛ рдПрдиреЛрдЯреЗрд╢рди рдЬреЛрдбрд╝реЗрдВ:


 const ast = j(source); ast .find(j.FunctionDeclaration) .forEach(({ value }) => { if (value.returnType === null) value.returnType = getReturnType(esTreeNodeToTSNode(value)); }); ast .find(j.ClassMethod, { kind: 'method' }) .forEach(({ value }) => { if (value.returnType === null) value.returnType = getReturnType(esTreeNodeToTSNode(value).parent); }); return ast.toSource(); 

рдореБрдЭреЗ рдХреНрд▓рд╛рд╕ рд╡рд┐рдзрд┐ рдиреЛрдб рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЛрдб рдХреЛ рд╕рдорд╛рдпреЛрдЬрд┐рдд рдХрд░рдирд╛ рдкрдбрд╝рд╛, рдХреНрдпреЛрдВрдХрд┐ рдПрд╕реНрдЯреНрд░реА рдореЗрдВ рд╡рд┐рдзрд┐ рдиреЛрдб рдХреА рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд╕реНрдерд┐рддрд┐ рдореЗрдВ TSTree рдореЗрдВ рд╡рд┐рдзрд┐ рдкрд╣рдЪрд╛рдирдХрд░реНрддрд╛ рдХрд╛ рдиреЛрдб рд╣реИ (рдЗрд╕рд▓рд┐рдП рд╣рдо parent рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ)ред


рдЕрдВрдд рдореЗрдВ, рд╣рдо рд░рд┐рдЯрд░реНрди рдкреНрд░рдХрд╛рд░ рдХреЗ рдПрдиреЛрдЯреЗрд╢рди рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЛрдб рд▓рд┐рдЦрддреЗ рд╣реИрдВ:


 function getReturnTypeFromString(typeString) { let ret; j(`function foo(): ${typeString} { }`) .find(j.FunctionDeclaration) .some(({ value: { returnType } }) => ret = returnType); return ret; } function getReturnType(node) { return getReturnTypeFromString( typeChecker.typeToString( typeChecker.getReturnTypeOfSignature( typeChecker.getSignatureFromDeclaration(node) ) ) ); } 

рдкреВрд░реА рд╕реВрдЪреА:


 import * as ts from 'typescript'; export default function transform({ source, path }, { j }) { const compilerOptions = { target: ts.ScriptTarget.Latest }; const program = ts.createProgram([path], compilerOptions); const sourceFile = program.getSourceFile(path); const typeChecker = program.getTypeChecker(); const locToTSNodeMap = new Map(); const esTreeNodeToTSNode = ({ loc: { start: { line, column } } }) => locToTSNodeMap.has(line) ? locToTSNodeMap.get(line).get(column) : undefined; (function buildLocToTSNodeMap(node) { const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)); const nextLine = line + 1; if (!locToTSNodeMap.has(nextLine)) locToTSNodeMap.set(nextLine, new Map()); locToTSNodeMap.get(nextLine).set(character, node); ts.forEachChild(node, buildLocToTSNodeMap); }(sourceFile)); function getReturnTypeFromString(typeString) { let ret; j(`function foo(): ${typeString} { }`) .find(j.FunctionDeclaration) .some(({ value: { returnType } }) => ret = returnType); return ret; } function getReturnType(node) { return getReturnTypeFromString( typeChecker.typeToString( typeChecker.getReturnTypeOfSignature( typeChecker.getSignatureFromDeclaration(node) ) ) ); } const ast = j(source); ast .find(j.FunctionDeclaration) .forEach(({ value }) => { if (value.returnType === null) value.returnType = getReturnType(esTreeNodeToTSNode(value)); }); ast .find(j.ClassMethod, { kind: 'method' }) .forEach(({ value }) => { if (value.returnType === null) value.returnType = getReturnType(esTreeNodeToTSNode(value).parent); }); return ast.toSource(); } export const parser = 'ts'; 

рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ-рдПрд╕реНрд▓рд┐рдВрдЯ рдкрд╛рд░реНрд╕рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛


рдЬреИрд╕рд╛ рдХрд┐ рдКрдкрд░ рджрд┐рдЦрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ, рд╣рд╛рд▓рд╛рдВрдХрд┐ рдиреЛрдбреНрд╕ рдХреЗ рдкрджреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдкреНрд░рджрд░реНрд╢рди рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рдпрд╣ рд╕рдЯреАрдХ рдкрд░рд┐рдгрд╛рдо рдирд╣реАрдВ рджреЗрддрд╛ рд╣реИ рдФрд░ рдХрднреА-рдХрднреА "рдореИрдиреБрдЕрд▓ рдЯреНрдпреВрдирд┐рдВрдЧ" рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдПрдХ рдЕрдзрд┐рдХ рд╕рд╛рдорд╛рдиреНрдп рд╕рдорд╛рдзрд╛рди рдПрд╕реНрдЯреНрд░реА рдиреЛрдбреНрд╕ рдХрд╛ рдПрдХ рд╕реНрдкрд╖реНрдЯ рдорд╛рдирдЪрд┐рддреНрд░рдг TSTree рдХреЛ рд▓рд┐рдЦрдирд╛ рд╣реЛрдЧрд╛ред рдпрд╣ рд╣реИ рдХрд┐ рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ-рдПрд╕реНрд▓рд┐рдВрдЯ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдкрд╛рд░реНрд╕рд░ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред рд╣рдо рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред


рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╣рдореЗрдВ рдЕрдВрддрд░реНрдирд┐рд╣рд┐рдд jscodeshift рдкрд╛рд░реНрд╕рд░ рдХреЛ рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ-рдПрд╕реНрд▓рд┐рдВрдЯ рдкрд╛рд░реНрд╕рд░ рдХреЛ рдУрд╡рд░рд░рд╛рдЗрдб рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рд╕рдмрд╕реЗ рд╕рд░рд▓ рдорд╛рдорд▓реЗ рдореЗрдВ, рдХреЛрдб рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ:


 export const parser = { parse(source) { return typescriptEstree.parse(source); } }; 

рд╣рд╛рд▓рд╛рдБрдХрд┐, рд╣рдореЗрдВ TSTree рдФрд░ typeChecker рдЪреЗрдХрд░ рдХреЛ ESTree рдиреЛрдбреНрд╕ рдХреА рдореИрдкрд┐рдВрдЧ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЛрдб рдХреЛ рдереЛрдбрд╝рд╛ рдЬрдЯрд┐рд▓ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдЗрд╕рдХреЗ рд▓рд┐рдП, рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ-рдПрд╕рд▓рд┐рдВрдЯ parseAndGenerateServices рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред рд╕рдм рдХреБрдЫ рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдореЗрдВ .ts рдлрд╝рд╛рдЗрд▓ рдХреЗ рд▓рд┐рдП рдкрде рдФрд░ рдЙрд╕ рдкрд░ tsconfig.json рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдлрд╝рд╛рдЗрд▓ рдХреЗ рд▓рд┐рдП рдкрде рдкрд╛рд╕ рдХрд░рдирд╛ tsconfig.json ред рдЪреВрдВрдХрд┐ рдРрд╕рд╛ рдХрд░рдиреЗ рдХрд╛ рдХреЛрдИ рд╕реАрдзрд╛ рддрд░реАрдХрд╛ рдирд╣реАрдВ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЖрдкрдХреЛ рд╡реИрд╢реНрд╡рд┐рдХ рдЪрд░ (рдУрд╣!) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред


 const parserState = {}; function parseWithServices(j, source, path, projectPath) { parserState.options = { filePath: path, project: projectPath }; return { ast: j(source), services: parserState.services }; } export const parser = { parse(source) { if (parserState.options !== undefined) { const options = parserState.options; delete parserState.options; const { ast, services } = typescriptEstree.parseAndGenerateServices(source, options); parserState.services = services; return ast; } return typescriptEstree.parse(source); } }; 

рд╣рд░ рдмрд╛рд░ рдЬрдм рд╣рдо рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ-рдПрд╕реНрд▓рд┐рдВрдЯ рдкрд╛рд░реНрд╕рд░ рдЯреВрд▓реНрд╕ рдХрд╛ рдПрдХ рд╡рд┐рд╕реНрддрд╛рд░рд┐рдд рд╕реЗрдЯ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддреЛ рд╣рдо parseWithServices рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХреЙрд▓ рдХрд░рддреЗ рд╣реИрдВ, рдЬрд┐рд╕рдореЗрдВ рд╣рдо рдЖрд╡рд╢реНрдпрдХ рдкреИрд░рд╛рдореАрдЯрд░ рдкрд╛рд╕ рдХрд░рддреЗ рд╣реИрдВ (рдЕрдиреНрдп рдорд╛рдорд▓реЛрдВ рдореЗрдВ, рд╣рдо рдЕрднреА рднреА j рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ):


 const { ast, services: { program, esTreeNodeToTSNodeMap } } = parseWithServices(j, source, path, tsConfigPath); const typeChecker = program.getTypeChecker(); const esTreeNodeToTSNode = ({ original }) => esTreeNodeToTSNodeMap.get(original); 

рдпрд╣ рдХреЗрд╡рд▓ рдХреЛрдб рд▓рд┐рдЦрдиреЗ рдФрд░ рдХрдХреНрд╖рд╛рдУрдВ рдХреЗ рдХрд╛рд░реНрдпреЛрдВ рдФрд░ рддрд░реАрдХреЛрдВ рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрдиреА рд╣реБрдИ рд╣реИ:


 ast .find(j.FunctionDeclaration) .forEach(({ value }) => { if (value.returnType === null) value.returnType = getReturnType(esTreeNodeToTSNode(value)); }); ast .find(j.MethodDefinition, { kind: 'method' }) .forEach(({ value }) => { if (value.value.returnType === null) value.value.returnType = getReturnType(esTreeNodeToTSNode(value)); }); return ast.toSource(); 

рдпрд╣ рдзреНрдпрд╛рди рджрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП рдХрд┐ рд╣рдореЗрдВ рдХреНрд▓рд╛рд╕ рдореЗрдердбреНрд╕ рдХреЛ рдмрд╛рдпрдкрд╛рд╕ MethodDefinition рд▓рд┐рдП ClassMethod рдЪрдпрдирдХрд░реНрддрд╛ рдХреЛ ClassMethod рд╕рд╛рде рдмрджрд▓рдирд╛ рдкрдбрд╝рд╛ (рд╡рд┐рдзрд┐ рдХреЗ рд╡рд╛рдкрд╕реА рдореВрд▓реНрдп рдХрд╛ рдПрдХреНрд╕реЗрд╕ рдХреЛрдб рднреА рдереЛрдбрд╝рд╛ рдмрджрд▓ рдЧрдпрд╛ рд╣реИ)ред рдпрд╣ рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ-рдПрд╕реНрд▓рд┐рдВрдЯ рдкрд╛рд░реНрд╕рд░ рдХреА рдмрд╛рд░реАрдХрд┐рдпреЛрдВ рд╣реИред getReturnType рдлрд╝рдВрдХреНрд╢рди getReturnType рдкрд╣рд▓реЗ рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдЧрдП рд╕рдорд╛рди рд╣реИред


рдкреВрд░реА рд╕реВрдЪреА:


 import * as typescriptEstree from '@typescript-eslint/typescript-estree'; export default function transform({ source, path }, { j }, { tsConfigPath }) { const { ast, services: { program, esTreeNodeToTSNodeMap } } = parseWithServices(j, source, path, tsConfigPath); const typeChecker = program.getTypeChecker(); const esTreeNodeToTSNode = ({ original }) => esTreeNodeToTSNodeMap.get(original); function getReturnTypeFromString(typeString) { let ret; j(`function foo(): ${typeString} { }`) .find(j.FunctionDeclaration) .some(({ value: { returnType } }) => ret = returnType); return ret; } function getReturnType(node) { return getReturnTypeFromString( typeChecker.typeToString( typeChecker.getReturnTypeOfSignature( typeChecker.getSignatureFromDeclaration(node) ) ) ); } ast .find(j.FunctionDeclaration) .forEach(({ value }) => { if (value.returnType === null) value.returnType = getReturnType(esTreeNodeToTSNode(value)); }); ast .find(j.MethodDefinition, { kind: 'method' }) .forEach(({ value }) => { if (value.value.returnType === null) value.value.returnType = getReturnType(esTreeNodeToTSNode(value)); }); return ast.toSource(); } const parserState = {}; function parseWithServices(j, source, path, projectPath) { parserState.options = { filePath: path, project: projectPath }; return { ast: j(source), services: parserState.services }; } export const parser = { parse(source) { if (parserState.options !== undefined) { const options = parserState.options; delete parserState.options; const { ast, services } = typescriptEstree.parseAndGenerateServices(source, options); parserState.services = services; return ast; } return typescriptEstree.parse(source); } }; 

рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХреЗ рдкреЗрд╢реЗрд╡рд░реЛрдВ рдФрд░ рд╡рд┐рдкрдХреНрд╖


рдкрдВрдХреНрддрд┐ рдФрд░ рд╕реНрддрдВрдн рджреГрд╖реНрдЯрд┐рдХреЛрдг


рдкреЗрд╢реЗрд╡рд░реЛрдВ:


  • рдпрд╣ рдирд┐рд░реНрдорд┐рдд рдореЗрдВ рдкрд╛рд░реНрд╕рд░ jscodeshift рдХреЛ рдУрд╡рд░рд░рд╛рдЗрдб рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИред
  • рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдФрд░ рд╕реНрд░реЛрдд рдЧреНрд░рдВрдереЛрдВ рдХреЛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдиреЗ рдХреА рд▓рдЪреАрд▓рд╛рдкрди (рдЖрдк рдореЗрдореЛрд░реА рдореЗрдВ рдлрд╝рд╛рдЗрд▓реЛрдВ рдФрд░ рд▓рд╛рдЗрдиреЛрдВ / рдСрдмреНрдЬреЗрдХреНрдЯ рджреЛрдиреЛрдВ рдХреЛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдиреАрдЪреЗ рджреЗрдЦреЗрдВ)ред

рд╡рд┐рдкрдХреНрд╖:


  • рд╕реНрдерд┐рддрд┐ рд╕реЗ рдиреЛрдбреНрд╕ рдХрд╛ рдкреНрд░рджрд░реНрд╢рди рдЧрд▓рдд рд╣реИ рдФрд░ рдХреБрдЫ рдорд╛рдорд▓реЛрдВ рдореЗрдВ рд╕рдорд╛рдпреЛрдЬрди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

рдкрд╛рд░реНрд╕рд░ рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ-рдПрд╕рд▓рд┐рдВрдЯ


рдкреЗрд╢реЗрд╡рд░реЛрдВ:


  • рдПрдХ рдПрдПрд╕рдЯреА рд╕реЗ рджреВрд╕рд░реЗ рдореЗрдВ рдиреЛрдбреНрд╕ рдХреА рд╕рдЯреАрдХ рдореИрдкрд┐рдВрдЧред

рд╡рд┐рдкрдХреНрд╖:


  • рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ-рдПрд╕реНрд▓рд┐рдВрдЯ рдкрд╛рд░реНрд╕рд░ рдХреА рдПрдПрд╕рдЯреА рд╕рдВрд░рдЪрдирд╛ рдмрд┐рд▓реНрдЯ-рдЗрди рдкрд╛рд░реНрд╕рд░ рдореЗрдВ рдЬреЙрдХреНрд╕реЛрдбреЗрд╢рдлрд╝реНрдЯ рд╕реЗ рдереЛрдбрд╝реА рдЕрд▓рдЧ рд╣реИред
  • рдЯреАрдПрд╕ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдФрд░ рд╕реНрд░реЛрдд рдХреЛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдлрд╝рд╛рдЗрд▓реЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

рдирд┐рд╖реНрдХрд░реНрд╖


рдкрд╣рд▓рд╛ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдореМрдЬреВрджрд╛ рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдХреЛ рдЬреЛрдбрд╝рдирд╛ рдЖрд╕рд╛рди рд╣реИ, рдЬреИрд╕рд╛ рдХрд┐ рдЗрд╕рдХреЗ рд▓рд┐рдП рдкрд╛рд░реНрд╕рд░ рдкреБрдирд░реНрд╡рд┐рддрд░рдг рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдПрдПрд╕рдЯреА рдиреЛрдбреНрд╕ рдХреЗ рдорд╛рдирдЪрд┐рддреНрд░рдг рдХреЛ рд╕рдорд╛рдпреЛрдЬрди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред


рджреВрд╕рд░реЗ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдкрд░ рдирд┐рд░реНрдгрдп рдЕрдЧреНрд░рд┐рдо рдореЗрдВ рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЕрдиреНрдпрдерд╛ рдЖрдкрдХреЛ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдПрдПрд╕рдЯреА рд╕рдВрд░рдЪрдирд╛ рдХреЗ рдХрд╛рд░рдг рдХреЛрдб рдбрд┐рдмрдЧрд┐рдВрдЧ рдореЗрдВ рд╕рдордп рдмрд┐рддрд╛рдирд╛ рдкрдбрд╝ рд╕рдХрддрд╛ рд╣реИред рджреВрд╕рд░реА рдУрд░, рдЖрдкрдХреЗ рдкрд╛рд╕ рдХреБрдЫ рдиреЛрдбреНрд╕ рдХреА рдкреВрд░реА рдореИрдкрд┐рдВрдЧ рд╣реЛрдЧреА (рдФрд░ рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрдд)ред


рдкреБрдирд╢реНрдЪ


рдКрдкрд░ рдЙрд▓реНрд▓реЗрдЦ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ рдХрд┐ рдЯреАрдПрд╕ рдкрд╛рд░реНрд╕рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╕рдордп, рдЖрдк рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдФрд░ рд╕реНрд░реЛрдд рдЧреНрд░рдВрдереЛрдВ рдХреЛ рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреЗ рд░реВрдк рдореЗрдВ рдФрд░ рдореЗрдореЛрд░реА рдореЗрдВ рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рд░реВрдк рдореЗрдВ рджреЛрдиреЛрдВ рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рд░реВрдк рдореЗрдВ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдХрд╛ рд╕реНрдерд╛рдирд╛рдВрддрд░рдг рдФрд░ рдлрд╝рд╛рдЗрд▓ рдХреЗ рд░реВрдк рдореЗрдВ рд╕реНрд░реЛрдд рдкрд╛рда рдХрд╛ рд╕реНрдерд╛рдирд╛рдВрддрд░рдг рдЙрджрд╛рд╣рд░рдг рдореЗрдВ рдорд╛рдирд╛ рдЬрд╛рддрд╛ рдерд╛ред рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдПрдХ рдХреЛрдб рд╣реИ рдЬреЛ рдЖрдкрдХреЛ рдХрд┐рд╕реА рдлрд╝рд╛рдЗрд▓ рд╕реЗ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдкрдврд╝рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ:


 class TsDiagnosticError extends Error { constructor(err) { super(Array.isArray(err) ? err.map(e => e.messageText).join('\n') : err.messageText); this.diagnostic = err; } } function tsGetCompilerOptionsFromConfigFile(tsConfigPath, basePath = '.') { const { config, error } = ts.readConfigFile(tsConfigPath, ts.sys.readFile); if (error) throw new TsDiagnosticError(error); const { options, errors } = ts.parseJsonConfigFileContent(config, tsGetCompilerOptionsFromConfigFile.host, basePath); if (errors.length !== 0) throw new TsDiagnosticError(errors); return options; } tsGetCompilerOptionsFromConfigFile.host = { fileExists: ts.sys.fileExists, readFile: ts.sys.readFile, readDirectory: ts.sys.readDirectory, useCaseSensitiveFileNames: true }; 

рдФрд░ рд▓рд╛рдЗрди рд╕реЗ рдПрдХ TS рдкреНрд░реЛрдЧреНрд░рд╛рдо рдмрдирд╛рдПрдВ:


 function tsCreateStringSourceCompilerHost(mockPath, source, compilerOptions, setParentNodes) { const host = ts.createCompilerHost(compilerOptions, setParentNodes); const getSourceFileOriginal = host.getSourceFile.bind(host); const readFileOriginal = host.readFile.bind(host); const fileExistsOriginal = host.fileExists.bind(host); host.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => { return fileName === mockPath ? ts.createSourceFile(fileName, source, languageVersion) : getSourceFileOriginal(fileName, languageVersion, onError, shouldCreateNewSourceFile); }; host.readFile = (fileName) => { return fileName === mockPath ? source : readFileOriginal(fileName); }; host.fileExists = (fileName) => { return fileName === mockPath ? true : fileExistsOriginal(fileName); }; return host; } function tsCreateStringSourceProgram(source, compilerOptions, mockPath = '_source.ts') { return ts.createProgram([mockPath], compilerOptions, tsCreateStringSourceCompilerHost(mockPath, source, compilerOptions)); } 

рд╕рдВрджрд░реНрдн


Source: https://habr.com/ru/post/hi480304/


All Articles