@tsbb/typescript
Version:
TSBB is a zero-config CLI that helps you develop, test, and publish modern TypeScript project.
110 lines (109 loc) • 4.21 kB
JavaScript
import ts from 'typescript';
import path, { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { Log } from './log.js';
export const __filename = fileURLToPath(import.meta.url);
export const __dirname = dirname(__filename);
/**
*
* @param paths `[ 'src/index.jsx', 'test/sum.js' ]`
* @returns ['src', 'test']
*/
export const getRootsFolderName = (paths = []) => paths.map((item) => item.replace(/[/\\]/g, path.sep).split(path.sep)[0]);
const getLnCol = (text = '', pos = 0) => {
const lines = text.split('\n');
let lineNum = 1;
let lineStartPos = 1;
for (let i = 0; i < pos; i++) {
if (text[i] === '\n') {
lineNum++;
lineStartPos = i + 1;
}
}
return { ln: lineNum, col: lineStartPos, text: lines[lineNum - 1] };
};
export const getExt = (extname) => {
let ext = path.extname(extname).toLocaleUpperCase().replace(/^\./, '');
ext = ext.padEnd(4, ' ');
if (/^MAP/.test(ext)) {
return `\x1b[34;1m${ext}\x1b[0m`;
}
if (/^JS/.test(ext)) {
return `\x1b[33;1m${ext}\x1b[0m`;
}
if (/^TS/.test(ext)) {
return `\x1b[36;1m${ext}\x1b[0m`;
}
return ext;
};
export const getEmojiIcon = (fileName) => {
let icon = /.d.ts$/i.test(fileName) ? '🐳' : '👉';
icon = /.js.map$/i.test(fileName) ? '🚩' : icon;
return icon;
};
export const writeFile = (fileName, data, writeByteOrderMark) => {
const outputFile = path.join(process.cwd(), fileName);
ts.sys.writeFile(outputFile, data, writeByteOrderMark);
const log = new Log();
log
.name()
.icon(getEmojiIcon(fileName))
.success(`${getExt(fileName)}┈┈▶ \x1b[32;1m${fileName}\x1b[0m`);
};
export const getSourceFile = (fileName, languageVersion, onError) => {
const contents = ts.sys.readFile(fileName, 'utf8');
return ts.createSourceFile(fileName, contents || '', languageVersion);
};
export const reportDiagnostic = (diagnostic) => {
const log = new Log();
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
const fileName = `\x1b[33;1m${diagnostic.file?.fileName}\x1b[0m`;
const lnCol = getLnCol(diagnostic.file?.text, diagnostic.start);
const codeText = lnCol.text ? `\n\n Code: \x1b[33;1m${lnCol.text}\x1b[0m` : '';
log
.name()
.icon('🚨')
.error(`┈┈▶ ${fileName} [Ln:${lnCol.ln} Col:${lnCol.col}]`, codeText, `\n Error: ${diagnostic.code} \x1b[31;1m${message}\x1b[0m`);
};
export const onWatchStatusChange = (diagnostic, newLine, options, errorCount) => {
const log = new Log();
if (typeof errorCount !== 'number') {
log.name().icon('🔆').success(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
return;
}
if (errorCount) {
if (!diagnostic.file?.fileName) {
return;
}
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
const lnCol = getLnCol(diagnostic.file?.text, diagnostic.start);
const fileName = `\x1b[33;1m${diagnostic.file?.fileName}\x1b[0m`;
const codeText = lnCol.text ? `\n\n Code: \x1b[33;1m${lnCol.text}\x1b[0m` : '';
log
.name()
.icon('🚨')
.error(`┈┈▶ ${fileName} [Ln:${lnCol.ln} Col:${lnCol.col}]`, codeText, `\n Error: ${diagnostic.code} \x1b[31;1m${message}\x1b[0m\n`);
}
else {
if (!options.emitDeclarationOnly) {
log.name().icon('🎉').success(diagnostic.messageText);
}
}
};
/**
* @param sourceFilePath `src/demo/index.ts`
* @param rootDirsRelative `['src', 'test']`
* @returns `demo/index.ts`
*/
export const getSourceFilePath = (sourceFilePath, rootDirsRelative) => {
const rootDirsAbsolutePath = rootDirsRelative.map((dir) => path.resolve(dir));
const sourceFileAbsolutePath = path.resolve(sourceFilePath);
let finalPath = sourceFileAbsolutePath;
for (const rootDir of rootDirsAbsolutePath) {
if (sourceFileAbsolutePath.startsWith(rootDir)) {
finalPath = sourceFileAbsolutePath.slice(rootDir.length + 1);
break;
}
}
return finalPath;
};