@tsbb/typescript
Version:
TSBB is a zero-config CLI that helps you develop, test, and publish modern TypeScript project.
113 lines (112 loc) • 5.6 kB
JavaScript
import ts from 'typescript';
import path from 'node:path';
import { writeFile, getSourceFile, reportDiagnostic, onWatchStatusChange, getRootsFolderName, getSourceFilePath, } from './utils.js';
import { Log } from './log.js';
export * from './utils.js';
export * from './log.js';
export const findConfigFile = () => ts.findConfigFile('.', ts.sys.fileExists, 'tsconfig.json');
export const readConfigFile = (tsConfigPath) => ts.readConfigFile(path.resolve(tsConfigPath), ts.sys.readFile);
export default async function compile(options = {}) {
const { isCopyFiles = true, onWriteFile, onCopyFiles, bail } = options;
const tsConfigPath = findConfigFile();
const log = new Log();
if (!tsConfigPath) {
log.error(`\n \x1b[33;1mYou are trying to compile TypeScript source code using the TypeScript compiler, \n but it cannot find the configuration file named\x1b[0m\x1b[31;1m tsconfig.json\x1b[0m.`);
return;
}
const { config, error } = readConfigFile(tsConfigPath);
if (error) {
return reportDiagnostic(error);
}
log.name();
const parseResult = ts.parseJsonConfigFileContent(config, ts.sys, path.dirname(tsConfigPath));
if (options.emitDeclarationOnly) {
parseResult.options.emitDeclarationOnly = true;
delete parseResult.options.noEmit;
}
if (!parseResult.options.outDir) {
parseResult.options.outDir = 'lib';
}
else if (options.outDir) {
parseResult.options.outDir = options.outDir;
}
const compilerOptions = parseResult.options;
if (parseResult.errors.length) {
return parseResult.errors.forEach(reportDiagnostic);
}
// Test files are not escaped.
parseResult.fileNames = parseResult.fileNames.filter((item) => !/\.(test|spec)\.(js|jsx|ts|tsx)$/i.test(item));
const currentDir = ts.sys.getCurrentDirectory();
const rootDirsRelative = [...new Set(getRootsFolderName(parseResult.fileNames))];
const outputDir = path.resolve(currentDir, compilerOptions.outDir || 'lib');
const rootDirs = [...rootDirsRelative].map((item) => path.resolve(currentDir, item));
if (options.watch) {
const createProgram = ts.createEmitAndSemanticDiagnosticsBuilderProgram;
const system = { ...ts.sys };
const fileNameData = {};
system.readFile = (fileName, encoding) => {
if (/^(?!.*\.d\.ts$).*\.(tsx?)+$/.test(fileName)) {
const sourceFilePath = fileName.indexOf(process.cwd()) > -1 ? path.relative(process.cwd(), fileName) : fileName;
const finalPath = getSourceFilePath(fileName, rootDirsRelative);
const cur = path.resolve(path.join(outputDir, finalPath)).replace(/\.(m?js|jsx?|m?ts|tsx?|c?js)$/, '.d.ts');
fileNameData[cur] = sourceFilePath;
}
return ts.sys.readFile(fileName, encoding);
};
system.writeFile = (pathName, data, writeByteOrderMark) => {
if (options.emitDeclarationOnly && onWriteFile) {
onWriteFile(pathName, data, fileNameData[path.resolve(currentDir, pathName)], writeByteOrderMark);
}
else {
writeFile(pathName, data, writeByteOrderMark);
}
};
// Note that there is another overload for `createWatchCompilerHost` that takes
// a set of root files.
const host = ts.createWatchCompilerHost(tsConfigPath, compilerOptions, system, createProgram, reportDiagnostic);
host.onWatchStatusChange = onWatchStatusChange;
// Start the TypeScript monitor compiler
ts.createWatchProgram(host);
if (isCopyFiles && onCopyFiles) {
await onCopyFiles(rootDirs, { isWatch: options.watch, outputDir, currentDir, rootDirsRelative });
}
}
else {
const compilerHost = ts.createCompilerHost(compilerOptions, true);
const host = { ...compilerHost, getSourceFile };
host.writeFile = (fileNamePath, contents, writeByteOrderMark, onError, sourceFiles = [], data) => {
if (options.emitDeclarationOnly && onWriteFile) {
const sourceFile = sourceFiles?.find((m) => !!m.fileName);
if (!sourceFile || !/\.(d.ts)$/i.test(fileNamePath))
return;
const sourceFilePath = sourceFile.fileName.indexOf(process.cwd()) > -1
? path.relative(process.cwd(), sourceFile.fileName)
: sourceFile.fileName;
onWriteFile(fileNamePath, contents, sourceFilePath, writeByteOrderMark);
return;
}
else {
writeFile(fileNamePath, contents, writeByteOrderMark);
}
};
const program = ts.createProgram(parseResult.fileNames, compilerOptions, host);
const emitResult = program.emit();
const diagnostics = ts.getPreEmitDiagnostics(program);
diagnostics.forEach(reportDiagnostic);
if (bail && diagnostics.length) {
diagnostics.forEach(reportDiagnostic);
process.exitCode = 1;
}
if (isCopyFiles && onCopyFiles) {
await onCopyFiles(rootDirs, { isWatch: options.watch, outputDir, currentDir, rootDirsRelative });
}
if (!options.emitDeclarationOnly && onWriteFile) {
if (emitResult.emitSkipped) {
log.icon('\n🚨').error('\x1b[33;1m Compilation failed!!!\x1b[0m\n');
}
else {
log.icon('\n🎉').error('\x1b[32;1mCompilation successful!\x1b[0m\n');
}
}
}
}