1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { 17 createCompilerHost, 18 createPrinter, 19 createProgram, 20 createSourceFile, 21 ScriptTarget, 22} from 'typescript'; 23 24import type { 25 CompilerHost, 26 CompilerOptions, 27 Printer, 28 Program, 29 SourceFile, 30 TypeChecker, 31} from 'typescript'; 32 33import path from 'path'; 34 35export class TypeUtils { 36 /** 37 * performing symbol analysis on the original abstract syntax tree can cause sourcemap errors 38 * @param oldAst 39 * 40 */ 41 public static createNewSourceFile(oldAst: SourceFile): SourceFile { 42 let printer: Printer = createPrinter(); 43 let content: string = printer.printFile(oldAst); 44 45 const fileSuffix: string = '.ts'; 46 const { dir, name } = path.parse(oldAst.fileName); 47 const targetName: string = path.join(dir, name) + '__tmp' + fileSuffix; 48 return createSourceFile(targetName, content, ScriptTarget.ES2015, true); 49 } 50 51 public static createChecker(ast: SourceFile): TypeChecker { 52 const host: CompilerHost = createCompilerHost({}); 53 54 const customHost: CompilerHost = { 55 getSourceFile(name, languageVersion): SourceFile | undefined { 56 if (name === ast.fileName) { 57 return ast; 58 } else { 59 return host.getSourceFile(name, languageVersion); 60 } 61 }, 62 // optional 63 getDefaultLibLocation: () => '', 64 getDefaultLibFileName: () => '', 65 writeFile: (filename, data) => { 66 }, 67 getCurrentDirectory: () => '', 68 useCaseSensitiveFileNames: host.useCaseSensitiveFileNames, 69 getCanonicalFileName: host.getCanonicalFileName, 70 getNewLine: host.getNewLine, 71 fileExists: () => true, 72 readFile: (name): string => { 73 return name === ast.fileName ? ast.text : host.readFile(name); 74 }, 75 // must, read program.ts => createCompilerHost 76 directoryExists: undefined, 77 getEnvironmentVariable: undefined, 78 getDirectories: undefined, 79 }; 80 81 let option: CompilerOptions = {}; 82 if (ast.fileName.endsWith('.js')) { 83 option.allowJs = true; 84 } 85 86 let program: Program = createProgram([ast.fileName], option, customHost); 87 return program.getTypeChecker(); 88 } 89} 90