esbuild-plugin-tsc
Version:
An esbuild plugin which uses tsc to compile typescript files.
118 lines (95 loc) • 3.24 kB
JavaScript
const fs = require('fs').promises;
const path = require('path');
const typescript = require('typescript');
const stripComments = require('strip-comments');
const { inspect } = require('util');
const theFinder = new RegExp(
/((?<![\(\s]\s*['"])@\w*[\w\d]\s*(?![;])[\((?=\s)])/
);
const findDecorators = (fileContent) =>
theFinder.test(stripComments(fileContent));
const esbuildPluginTsc = ({
tsconfigPath = path.join(process.cwd(), './tsconfig.json'),
force: forceTsc = false,
forceEsm = false,
tsx = true,
} = {}) => ({
name: 'tsc',
setup(build) {
let parsedTsConfig = null;
build.onLoad({ filter: tsx ? /\.m?tsx?$/ : /\.m?ts$/ }, async (args) => {
if (!parsedTsConfig) {
parsedTsConfig = parseTsConfig(tsconfigPath, process.cwd());
if (parsedTsConfig.options.sourceMap) {
parsedTsConfig.options.sourceMap = false;
parsedTsConfig.options.inlineSources = true;
parsedTsConfig.options.inlineSourceMap = true;
}
}
// Just return if we don't need to search the file.
if (
!forceTsc &&
(!parsedTsConfig ||
!parsedTsConfig.options ||
!parsedTsConfig.options.emitDecoratorMetadata)
) {
return;
}
const ts = await fs
.readFile(args.path, 'utf8')
.catch((err) => printDiagnostics({ file: args.path, err }));
// Find the decorator and if there isn't one, return out
const hasDecorator = findDecorators(ts);
if (!hasDecorator) {
return;
}
const extname = path.extname(args.path);
const basename = path.basename(args.path, extname);
let fileName = forceEsm
? `${basename}${extname.replace('.ts', '.mts')}`
: `${basename}${extname}`;
const program = typescript.transpileModule(ts, {
compilerOptions: parsedTsConfig.options,
fileName,
});
return { contents: program.outputText };
});
},
});
function parseTsConfig(tsconfig, cwd = process.cwd()) {
const fileName = typescript.findConfigFile(
cwd,
typescript.sys.fileExists,
tsconfig
);
// if the value was provided, but no file, fail hard
if (tsconfig !== undefined && !fileName)
throw new Error(`failed to open '${fileName}'`);
let loadedConfig = {};
let baseDir = cwd;
let configFileName;
if (fileName) {
const text = typescript.sys.readFile(fileName);
if (text === undefined) throw new Error(`failed to read '${fileName}'`);
const result = typescript.parseConfigFileTextToJson(fileName, text);
if (result.error !== undefined) {
printDiagnostics(result.error);
throw new Error(`failed to parse '${fileName}'`);
}
loadedConfig = result.config;
baseDir = path.dirname(fileName);
configFileName = fileName;
}
const parsedTsConfig = typescript.parseJsonConfigFileContent(
loadedConfig,
typescript.sys,
baseDir
);
if (parsedTsConfig.errors[0]) printDiagnostics(parsedTsConfig.errors);
return parsedTsConfig;
}
function printDiagnostics(...args) {
console.log(inspect(args, false, 10, true));
}
const thisModule = (module.exports = esbuildPluginTsc);
thisModule.esbuildPluginTsc = esbuildPluginTsc;