1import path from 'path'; 2import * as ts from 'typescript'; 3import { Extra } from '../parser-options'; 4 5interface ASTAndProgram { 6 ast: ts.SourceFile; 7 program: ts.Program; 8} 9 10/** 11 * Default compiler options for program generation from single root file 12 */ 13const DEFAULT_COMPILER_OPTIONS: ts.CompilerOptions = { 14 allowNonTsExtensions: true, 15 allowJs: true, 16 checkJs: true, 17 noEmit: true, 18 // extendedDiagnostics: true, 19 /** 20 * Flags required to make no-unused-vars work 21 */ 22 noUnusedLocals: true, 23 noUnusedParameters: true, 24}; 25 26function createDefaultCompilerOptionsFromExtra( 27 extra: Extra, 28): ts.CompilerOptions { 29 if (extra.debugLevel.has('typescript')) { 30 return { 31 ...DEFAULT_COMPILER_OPTIONS, 32 extendedDiagnostics: true, 33 }; 34 } 35 36 return DEFAULT_COMPILER_OPTIONS; 37} 38 39// This narrows the type so we can be sure we're passing canonical names in the correct places 40type CanonicalPath = string & { __brand: unknown }; 41 42// typescript doesn't provide a ts.sys implementation for browser environments 43const useCaseSensitiveFileNames = 44 ts.sys !== undefined ? ts.sys.useCaseSensitiveFileNames : true; 45const correctPathCasing = useCaseSensitiveFileNames 46 ? (filePath: string): string => filePath 47 : (filePath: string): string => filePath.toLowerCase(); 48 49function getCanonicalFileName(filePath: string): CanonicalPath { 50 let normalized = path.normalize(filePath); 51 if (normalized.endsWith(path.sep)) { 52 normalized = normalized.substr(0, normalized.length - 1); 53 } 54 return correctPathCasing(normalized) as CanonicalPath; 55} 56 57function ensureAbsolutePath(p: string, extra: Extra): string { 58 return path.isAbsolute(p) 59 ? p 60 : path.join(extra.tsconfigRootDir || process.cwd(), p); 61} 62 63function getTsconfigPath(tsconfigPath: string, extra: Extra): CanonicalPath { 64 return getCanonicalFileName(ensureAbsolutePath(tsconfigPath, extra)); 65} 66 67function canonicalDirname(p: CanonicalPath): CanonicalPath { 68 return path.dirname(p) as CanonicalPath; 69} 70 71function getScriptKind( 72 extra: Extra, 73 filePath: string = extra.filePath, 74): ts.ScriptKind { 75 const extension = path.extname(filePath).toLowerCase(); 76 // note - we respect the user's extension when it is known we could override it and force it to match their 77 // jsx setting, but that could create weird situations where we throw parse errors when TSC doesn't 78 switch (extension) { 79 case '.ts': 80 return ts.ScriptKind.TS; 81 82 case '.tsx': 83 return ts.ScriptKind.TSX; 84 85 case '.js': 86 return ts.ScriptKind.JS; 87 88 case '.jsx': 89 return ts.ScriptKind.JSX; 90 91 case '.json': 92 return ts.ScriptKind.JSON; 93 94 default: 95 // unknown extension, force typescript to ignore the file extension, and respect the user's setting 96 return extra.jsx ? ts.ScriptKind.TSX : ts.ScriptKind.TS; 97 } 98} 99 100export { 101 ASTAndProgram, 102 canonicalDirname, 103 CanonicalPath, 104 createDefaultCompilerOptionsFromExtra, 105 ensureAbsolutePath, 106 getCanonicalFileName, 107 getScriptKind, 108 getTsconfigPath, 109}; 110