tsc-to
Version:
TypeScript's command-line compiler `tsc` without type-checking. Benefits:
177 lines (152 loc) • 7.26 kB
text/typescript
import __ts from 'typescript';
const ts = __ts as any as typeof __byots;
import __byots from 'byots';
import { TypeScriptService } from '@ts-tools/service';
import fs from 'fs';
import Path from 'path';
export function main() {
const service = new TypeScriptService();
// HACK: trigger tsconfig discovery and parsing
const result = service.transpileFile(`DOES NOT EXIST${ Math.random() }.ts`, {
getCompilerOptions(baseHost, tsconfigOptions) {
// Hacky type assertions because byots compilerOptions and normal compilerOptions are incompatible.
return neuterCompilerOptions({ ...tsconfigOptions as ts.CompilerOptions }) as any;
}
});
const runningService = service.runningServices.get('tsconfig.json')!;
const baseHost = runningService.baseHost;
const languageService = runningService.languageService as any as ts.LanguageService;
const program = languageService.getProgram()!;
const compilerOptions = program.getCompilerOptions();
// Simple memoized mechanism to create directories that don't exist.
const createdDirectories = new Set<string>();
function ensureDirectoryExists(path: string) {
if(createdDirectories.has(path)) return true;
const parent = Path.dirname(path);
if(parent !== path) ensureDirectoryExists(parent);
try {
fs.mkdirSync(path);
} catch(e) {
if(!(e.code === 'EEXIST') || !fs.statSync(path).isDirectory()) {
throw e;
}
}
createdDirectories.add(path);
}
const diagnostics = [
...program.getSyntacticDiagnostics(),
...program.getOptionsDiagnostics()
];
console.log(ts.formatDiagnosticsWithColorAndContext(diagnostics, baseHost));
program.emit(undefined, (path, content) => {
console.log('Emitting', path);
ensureDirectoryExists(Path.dirname(path));
fs.writeFileSync(path, content);
});
return diagnostics.length ? 1: 0;
}
/**
* Copied from the source of ts.transpileModule
* https://github.com/Microsoft/TypeScript/blob/master/src/services/transpile.ts#L31-L58
*
* These modifications to compiler options preserve transpilation behavior but
* prevent wasted time on typechecking and loading other source files.
*/
export function neuterCompilerOptions(options: ts.CompilerOptions) {
options.isolatedModules = true;
// transpileModule does not write anything to disk so there is no need to verify that there are no conflicts between input and output paths.
options.suppressOutputPathCheck = true;
// Filename can be non-ts file.
options.allowNonTsExtensions = true;
// We are not returning a sourceFile for lib file when asked by the program,
// so pass --noLib to avoid reporting a file not found error.
options.noLib = true;
// Clear out other settings that would not be used in transpiling this module
options.lib = undefined;
options.types = undefined;
options.noEmit = undefined;
options.noEmitOnError = undefined;
options.paths = undefined;
options.rootDirs = undefined;
options.declaration = undefined;
options.composite = undefined;
options.declarationDir = undefined;
options.out = undefined;
options.outFile = undefined;
// We are not doing a full typecheck, we are not resolving the whole context,
// so pass --noResolve to avoid reporting missing file errors.
options.noResolve = true;
// I added these 2 to prevent the language service from loading any @types:
options.typeRoots = [];
options.types = [];
// Adding this because declaration is nulled above
options.declarationMap = undefined;
return options;
}
// copied from https://github.com/Microsoft/TypeScript/blob/865b3e786277233585e1586edba52bf837b61b71/src/services/transpile.ts
// function transpileModules(sourceFiles: ReadonlyArray<ts.SourceFile>, options: ts.CompilerOptions) {
// if(true) {
// options.isolatedModules = true;
// // transpileModule does not write anything to disk so there is no need to verify that there are no conflicts between input and output paths.
// options.suppressOutputPathCheck = true;
// // Filename can be non-ts file.
// options.allowNonTsExtensions = true;
// // We are not returning a sourceFile for lib file when asked by the program,
// // so pass --noLib to avoid reporting a file not found error.
// options.noLib = true;
// // Clear out other settings that would not be used in transpiling this module
// options.lib = undefined;
// options.types = undefined;
// options.noEmit = undefined;
// options.noEmitOnError = undefined;
// options.paths = undefined;
// options.rootDirs = undefined;
// options.declaration = undefined;
// options.composite = undefined;
// options.declarationDir = undefined;
// options.out = undefined;
// options.outFile = undefined;
// // We are not doing a full typecheck, we are not resolving the whole context,
// // so pass --noResolve to avoid reporting missing file errors.
// options.noResolve = true;
// }
// const inputFileNames: Array<string> = [];
// const inputs: Record<string, ts.SourceFile> = Object.create(null);
// for(const sourceFile of sourceFiles) {
// const normalized = ts.normalizePath(sourceFile.fileName);
// inputFileNames.push(normalized);
// inputs[normalized] = sourceFile;
// }
// const outputs: Record<string, string> = Object.create(null);
// const compilerHost: ts.CompilerHost = {
// getSourceFile: (fileName) => inputs[fileName],
// writeFile: (name, text) => {
// outputs[name] = text;
// // if (fileExtensionIs(name, ".map")) {
// // Debug.assertEqual(sourceMapText, undefined, "Unexpected multiple source map outputs, file:", name);
// // sourceMapText = text;
// // }
// // else {
// // Debug.assertEqual(outputText, undefined, "Unexpected multiple outputs, file:", name);
// // outputText = text;
// // }
// },
// getDefaultLibFileName: () => "lib.d.ts",
// useCaseSensitiveFileNames: () => false,
// getCanonicalFileName: fileName => fileName,
// getCurrentDirectory: () => process.cwd(),
// getNewLine: () => '\n',
// fileExists: (fileName): boolean => true,/*fileName === inputFileName,*/
// readFile: () => "",
// directoryExists: () => true,
// getDirectories: () => []
// };
// const program = ts.createProgram(inputFileNames, options, compilerHost);
// const diagnostics = program.getSyntacticDiagnostics().concat(program.getOptionsDiagnostics());
// program.emit(/*targetSourceFile*/ undefined, /*writeFile*/ undefined, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ undefined, undefined/*transpileOptions.transformers*/);
// return { outputs, program, diagnostics };
// }
if(require.main === module) {
process.exit(main());
}