UNPKG

@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
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'); } } } }