Typescript 使用ts.createWatchProgram键入自定义转换器脚本

Typescript 使用ts.createWatchProgram键入自定义转换器脚本,typescript,Typescript,TypeScript有几个高级API来实现监视/编译,例如: 它们中的任何一个可以与自定义变压器一起使用吗? 一条评论提到了通过变压器的能力,但它不能用于观察者 基本上,我需要通过API在--watch模式下运行TypeScript编译器,但需要传入我的自定义转换器。有任何线索吗?更新:请参阅带有createSolutionBuilderWithWatchHost的 可以通过截取createProgram并重写其emit方法来完成 这是一个带有录音的POC脚本。那里的自定义转换使用AS

TypeScript有几个高级API来实现监视/编译,例如:

它们中的任何一个可以与自定义变压器一起使用吗?

一条评论提到了通过变压器的能力,但它不能用于观察者


基本上,我需要通过API在
--watch
模式下运行TypeScript编译器,但需要传入我的自定义转换器。有任何线索吗?

更新:请参阅带有
createSolutionBuilderWithWatchHost的

可以通过截取
createProgram
并重写其
emit
方法来完成

这是一个带有录音的POC脚本。那里的自定义转换使用AST stats/类型装饰每个顶级语句

答复 GitHub gist

var ts=require('typescript');
var diagrept=ts.createBuilderStatusReporter(ts.sys,ts.sys.writeoutput&&ts.sys.writeoutput());
var host=ts.createWatchCompilerHost(
[\uu文件名],
{allowJs:true,checkJs:true,outFile:u filename+'.out.js'},
ts.sys,
创建和修补程序,
无效0,
报告观察);
var buildStart=Date.now();
ts.createWatchProgram(主机);
函数报告监视(诊断、换行、选项、错误计数){
ts.sys.write(类型错误计数=='number'?
'\x1b[90m'+((Date.now()-buildStart)/1000)+'s')。切片(0,10)+'\x1b[0m':
“观察诊断”);
diag报告(diag);
if(typeof errorCount!=='number')buildStart=Date.now();
}
var-prog;
函数createAndPatchProgram(){
prog=ts.createEmitAndSemanticDiagnosticsBuilderProgram.apply(ts,参数);
程序发射=程序发射;
prog.emit=overrideEmit;
返回程序;
}
函数重写限制(targetSourceFile、writeFile、cancellationToken、emitOnlyDtsFiles、customTransformers){
把这个还给我(
targetSourceFile,
writeFile,
取消令牌,
emit onlydts文件,
{after:[TransformInjectStatementNumber]}
);
}
var校验器;
/**@param{import('typescript').TransformationContext}context*/
函数TransformInjectStatementNumber(上下文){
checker=prog.getProgram().getTypeChecker();
返回转换文件;
函数转换文件(sourceFile){
返回ts.updateSourceFileNode(
源文件,
map(decorateStatementWithComplexityAndType));
}
}
函数decorateStatementWithComplexityAndType(语句){
var nodeCount=0;
var类型;
T.forEachChild(声明、访问声明儿童);
返回ts.addLeadingComment(
语句,ts.SyntaxKind.SingleLineCommentTrivia,
'复杂性:'+nodeCount+
(!type?“”:“:”+checker.typeToString(type));
函数visitStatementChild(子函数){
nodeCount++;
如果(!type)type=checker.getTypeAtLocation(子级);
如果(type.getFlags()==ts.TypeFlags.Any)type=null;
t.forEachChild(child,visitStatementChild);
}
}

更好的方法,更符合建议

手动调用solutionBuilder.getNextInvalidatedProject().emit(…)
时,可以传入转换器。调用该API会将生成标记为完成,这意味着它不会以通常的非自定义方式再次发出

您可以在初始
build()
之前调用它,也可以从
WatchStatusReporter
回调调用它

通过这种方式,您可以注入自定义变压器,但仍保留内置的监视逻辑。请参阅下面的概念验证脚本:

以下是完整的代码,另请参见和

/@ts检查
var ts=require('typescript');
var tsconfig_json=json.stringify({
编译器选项:{
outFile:_filename+'.out.js',
是的,
是的,
目标:“es3”
},
文件:[[uuuu文件名]
},空,2);
变量s={
删除:3
};
/**@type{import('typescript').System}*/
var sysOverride={};
对于(ts.sys中的var k){sysOverride[k]=ts.sys[k];}
sysOverride.readFile=函数(文件){
if(ts.sys.resolvePath(文件)==ts.sys.resolvePath(\uu dirname+'/tsconfig.json')){
//log('readFile(',file')->重写的tsconfig_json');
返回tsconfig_json;
}
否则{
var result=ts.sys.readFile(文件);
//if(!/node_modules/.test(文件))
//console.log('readFile(',file')->'+(typeof result==='string'?'“+result.length+'”:typeof result));
返回结果;
}
};
sysOverride.writeFile=函数(文件、内容){
log('sys.writeFile(',file',[',content.length',]');
ts.sys.writeFile(文件、内容);
};
var host=ts.createSolutionBuilderWithWatchHost(
系统覆盖,
无效0,
reportDiag,
reportDiag,
报告观察);
var buildStart=Date.now();
var solutionBuilder=ts.createSolutionBuilderWithWatch(
主办
[uuu dirname],
{增量:false},{});
initiateFirstBuild();
函数initiateFirstBuild(){
var firstBuild=solutionBuilder.getNextInvalidatedProject();
如果(首次构建){
buildStart=Date.now();
startBuild(firstBuild);
}
solutionBuilder.build();
}
/**
*@param{import('typescript').InvalidatedProject}proj
*@param{import('typescript').Diagnostic=}watchDiag
*/
功能启动构建(项目、watchDiag){
ts.sys.write(
'\x1b[93m'+(ts.InvalidatedProjectKind[proj.kind]+''。切片(0,10)+'\x1b[0m'+
(watchDiag?“”:“\n”);
if(watchDiag)reportDiag(watchDiag);
buildStart=Date.now();
if(proj&&proj.kind==ts.InvalidatedProjectKind.Build){
progSource=proj;
项目发射(
无效0,
无效0,
无效0,
无效0,
{after:[TransformInjectStatementNumber]});
}
}
函数完成构建(watchDiag){
ts.sys.write('\x1b[90m'+((Date.now()-buildStart)/1000)+'s')。slice(0,10)+'\x1b[0m');
if(watchDiag)reportDiag(watchDiag);
}
/**@type{import('typescript').FormatDiagnosticsHost}*/
var诊断主机;
/**@param{import('typescript')。诊断号