1namespace ts { 2 export interface TranspileOptions { 3 compilerOptions?: CompilerOptions; 4 fileName?: string; 5 reportDiagnostics?: boolean; 6 moduleName?: string; 7 renamedDependencies?: MapLike<string>; 8 transformers?: CustomTransformers; 9 } 10 11 export interface TranspileOutput { 12 outputText: string; 13 diagnostics?: Diagnostic[]; 14 sourceMapText?: string; 15 } 16 17 /* 18 * This function will compile source text from 'input' argument using specified compiler options. 19 * If not options are provided - it will use a set of default compiler options. 20 * Extra compiler options that will unconditionally be used by this function are: 21 * - isolatedModules = true 22 * - allowNonTsExtensions = true 23 * - noLib = true 24 * - noResolve = true 25 */ 26 export function transpileModule(input: string, transpileOptions: TranspileOptions): TranspileOutput { 27 const diagnostics: Diagnostic[] = []; 28 29 const options: CompilerOptions = transpileOptions.compilerOptions ? fixupCompilerOptions(transpileOptions.compilerOptions, diagnostics) : {}; 30 31 // mix in default options 32 const defaultOptions = getDefaultCompilerOptions(); 33 for (const key in defaultOptions) { 34 if (hasProperty(defaultOptions, key) && options[key] === undefined) { 35 options[key] = defaultOptions[key]; 36 } 37 } 38 39 for (const option of transpileOptionValueCompilerOptions) { 40 options[option.name] = option.transpileOptionValue; 41 } 42 43 // transpileModule does not write anything to disk so there is no need to verify that there are no conflicts between input and output paths. 44 options.suppressOutputPathCheck = true; 45 46 // Filename can be non-ts file. 47 options.allowNonTsExtensions = true; 48 49 const newLine = getNewLineCharacter(options); 50 // Create a compilerHost object to allow the compiler to read and write files 51 const compilerHost: CompilerHost = { 52 getSourceFile: (fileName) => fileName === normalizePath(inputFileName) ? sourceFile : undefined, 53 writeFile: (name, text) => { 54 if (fileExtensionIs(name, ".map")) { 55 Debug.assertEqual(sourceMapText, undefined, "Unexpected multiple source map outputs, file:", name); 56 sourceMapText = text; 57 } 58 else { 59 Debug.assertEqual(outputText, undefined, "Unexpected multiple outputs, file:", name); 60 outputText = text; 61 } 62 }, 63 getDefaultLibFileName: () => "lib.d.ts", 64 useCaseSensitiveFileNames: () => false, 65 getCanonicalFileName: fileName => fileName, 66 getCurrentDirectory: () => "", 67 getNewLine: () => newLine, 68 fileExists: (fileName): boolean => fileName === inputFileName, 69 readFile: () => "", 70 directoryExists: () => true, 71 getDirectories: () => [] 72 }; 73 74 // if jsx is specified then treat file as .tsx 75 const inputFileName = transpileOptions.fileName || (transpileOptions.compilerOptions && transpileOptions.compilerOptions.jsx ? "module.tsx" : "module.ts"); 76 const scriptKind = ensureScriptKind(inputFileName, /*setScriptKind*/ undefined); 77 const sourceFile = createSourceFile( 78 inputFileName, 79 input, 80 { 81 languageVersion: getEmitScriptTarget(options), 82 impliedNodeFormat: getImpliedNodeFormatForFile(toPath(inputFileName, "", compilerHost.getCanonicalFileName), /*cache*/ undefined, compilerHost, options), 83 setExternalModuleIndicator: getSetExternalModuleIndicator(options) 84 }, 85 /* setParentNodes */ undefined, 86 scriptKind, 87 options 88 ); 89 if (transpileOptions.moduleName) { 90 sourceFile.moduleName = transpileOptions.moduleName; 91 } 92 93 if (transpileOptions.renamedDependencies) { 94 sourceFile.renamedDependencies = new Map(getEntries(transpileOptions.renamedDependencies)); 95 } 96 97 // Output 98 let outputText: string | undefined; 99 let sourceMapText: string | undefined; 100 101 const program = createProgram([inputFileName], options, compilerHost); 102 103 if (transpileOptions.reportDiagnostics) { 104 addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile)); 105 addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics()); 106 } 107 // Emit 108 program.emit(/*targetSourceFile*/ undefined, /*writeFile*/ undefined, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ undefined, transpileOptions.transformers); 109 110 if (outputText === undefined) return Debug.fail("Output generation failed"); 111 112 return { outputText, diagnostics, sourceMapText }; 113 } 114 115 /* 116 * This is a shortcut function for transpileModule - it accepts transpileOptions as parameters and returns only outputText part of the result. 117 */ 118 export function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, diagnostics?: Diagnostic[], moduleName?: string): string { 119 const output = transpileModule(input, { compilerOptions, fileName, reportDiagnostics: !!diagnostics, moduleName }); 120 // addRange correctly handles cases when wither 'from' or 'to' argument is missing 121 addRange(diagnostics, output.diagnostics); 122 return output.outputText; 123 } 124 125 let commandLineOptionsStringToEnum: CommandLineOptionOfCustomType[]; 126 127 /** JS users may pass in string values for enum compiler options (such as ModuleKind), so convert. */ 128 /*@internal*/ 129 export function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]): CompilerOptions { 130 // Lazily create this value to fix module loading errors. 131 commandLineOptionsStringToEnum = commandLineOptionsStringToEnum || 132 filter(optionDeclarations, o => typeof o.type === "object" && !forEachEntry(o.type, v => typeof v !== "number")) as CommandLineOptionOfCustomType[]; 133 134 options = cloneCompilerOptions(options); 135 136 for (const opt of commandLineOptionsStringToEnum) { 137 if (!hasProperty(options, opt.name)) { 138 continue; 139 } 140 141 const value = options[opt.name]; 142 // Value should be a key of opt.type 143 if (isString(value)) { 144 // If value is not a string, this will fail 145 options[opt.name] = parseCustomTypeOption(opt, value, diagnostics); 146 } 147 else { 148 if (!forEachEntry(opt.type, v => v === value)) { 149 // Supplied value isn't a valid enum value. 150 diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt)); 151 } 152 } 153 } 154 155 return options; 156 } 157} 158