@stacksjs/dtsx
Version:
A modern, fast .d.ts generation tool, powered by Bun.
178 lines (160 loc) • 10.8 kB
JavaScript
// @bun
import{De as I}from"./chunk-dx7m458f.js";import{cpus as K}from"os";import{resolve as J}from"path";import{Worker as M}from"worker_threads";var __dirname="/home/runner/work/dtsx/dtsx/packages/dtsx/src";class L{workers=[];taskQueue=[];config;stats={totalTasks:0,completedTasks:0,failedTasks:0,averageDuration:0,activeWorkers:0,idleWorkers:0};isShuttingDown=!1;idleCheckInterval=null;constructor(q={}){let y=q.maxWorkers??Math.max(1,K().length-1);this.config={maxWorkers:y,taskTimeout:q.taskTimeout??30000,recycleAfter:q.recycleAfter??100,idleTimeout:q.idleTimeout??1e4,initialWorkers:q.initialWorkers??Math.min(2,y)}}async init(){let q=Math.min(this.config.initialWorkers,this.config.maxWorkers);for(let y=0;y<q;y++)this.createWorker();if(this.config.idleTimeout>0){if(this.idleCheckInterval=setInterval(()=>{this.checkIdleWorkers()},5000),this.idleCheckInterval&&typeof this.idleCheckInterval==="object"&&"unref"in this.idleCheckInterval)this.idleCheckInterval.unref()}}createWorker(){let q=`
const { parentPort } = require('worker_threads');
const bun = typeof Bun !== 'undefined' ? Bun : null;
const fs = bun ? null : require('node:fs');
const libsPromise = (async () => {
const { extractDeclarations } = await import('${J(__dirname,"extractor.js")}');
const { processDeclarations } = await import('${J(__dirname,"processor.js")}');
return { extractDeclarations, processDeclarations };
})();
// Dynamic import for ESM compatibility
async function processTask(task) {
const startTime = Date.now();
try {
const { extractDeclarations, processDeclarations } = await libsPromise;
const keepComments = task.config?.keepComments;
const isolatedDeclarations = task.config?.isolatedDeclarations;
const importOrder = task.config?.importOrder;
if (task.type === 'process-batch') {
const filePaths = task.filePaths;
const outPaths = task.outPaths;
const files = filePaths ? null : (task.files || []);
const fileCount = filePaths ? filePaths.length : files.length;
const writeOutput = task.writeOutput === true;
const results = writeOutput ? null : new Array(fileCount);
let errorResults = null;
let sources = task.sources;
if (!sources && fileCount > 0) {
const readPromises = new Array(fileCount);
for (let i = 0; i < fileCount; i++) {
const file = files ? files[i] : null;
const sourceCode = file ? file.sourceCode : undefined;
const filePath = filePaths ? filePaths[i] : file.filePath;
if (sourceCode != null) {
readPromises[i] = Promise.resolve(sourceCode);
}
else if (bun) {
readPromises[i] = bun.file(filePath).text();
}
else {
readPromises[i] = Promise.resolve(fs.readFileSync(filePath, 'utf-8'));
}
}
sources = await Promise.all(readPromises);
}
const ctx = { filePath: '', sourceCode: '', declarations: [] };
const writePromises = writeOutput && bun ? new Array(fileCount) : null;
let success = true;
for (let i = 0; i < fileCount; i++) {
const file = files ? files[i] : null;
const filePath = filePaths ? filePaths[i] : file.filePath;
const outPath = outPaths ? outPaths[i] : (file ? file.outPath : undefined);
try {
const sourceCode = sources ? sources[i] : (file?.sourceCode || '');
const declarations = extractDeclarations(sourceCode, filePath, keepComments, isolatedDeclarations);
ctx.filePath = filePath;
ctx.sourceCode = sourceCode;
ctx.declarations = declarations;
const content = processDeclarations(declarations, ctx, keepComments ?? true, importOrder);
if (writeOutput) {
if (!outPath) {
throw new Error(\`Missing outPath for \${filePath}\`);
}
if (bun) {
writePromises[i] = bun.write(outPath, content);
}
else {
fs.writeFileSync(outPath, content);
}
}
else if (results) {
results[i] = {
filePath,
success: true,
content,
declarations,
};
}
}
catch (error) {
const message = error && error.message ? error.message : String(error);
if (writeOutput) {
if (!errorResults) {
errorResults = [];
}
errorResults.push({
filePath,
success: false,
error: message,
});
}
else if (results) {
results[i] = {
filePath,
success: false,
error: message,
};
}
success = false;
}
}
if (writePromises) {
await Promise.all(writePromises);
}
const errorMessage = errorResults?.length
? \`\${errorResults[0].filePath}: \${errorResults[0].error || 'Worker batch failed'}\`
: undefined;
return {
id: task.id,
success,
filePath: task.filePath,
batchResults: writeOutput ? errorResults || undefined : results,
error: errorMessage,
duration: Date.now() - startTime,
};
}
if (task.type === 'extract') {
const declarations = extractDeclarations(task.sourceCode, task.filePath, keepComments, isolatedDeclarations);
return {
id: task.id,
success: true,
filePath: task.filePath,
declarations,
duration: Date.now() - startTime,
};
}
if (task.type === 'process') {
const declarations = extractDeclarations(task.sourceCode, task.filePath, keepComments, isolatedDeclarations);
const content = processDeclarations(declarations, {
filePath: task.filePath,
sourceCode: task.sourceCode,
declarations,
}, keepComments ?? true, importOrder);
return {
id: task.id,
success: true,
filePath: task.filePath,
content,
declarations,
duration: Date.now() - startTime,
};
}
throw new Error(\`Unknown task type: \${task.type}\`);
}
catch (error) {
return {
id: task.id,
success: false,
filePath: task.filePath,
error: error.message,
duration: Date.now() - startTime,
};
}
}
parentPort.on('message', async (task) => {
const result = await processTask(task);
parentPort.postMessage(result);
});
`,y=new M(q,{eval:!0}),F={worker:y,busy:!1,taskCount:0,lastActive:Date.now(),currentTask:null};return y.on("message",(E)=>{this.handleWorkerResult(F,E)}),y.on("error",(E)=>{I.error("Worker error:",E),this.handleWorkerError(F,E)}),y.on("exit",(E)=>{if(E!==0&&!this.isShuttingDown)I.error(`Worker exited with code ${E}`),this.removeWorker(F)}),this.workers.push(F),this.updateStats(),F}handleWorkerResult(q,y){if(q.busy=!1,q.taskCount++,q.lastActive=Date.now(),q.currentTask&&q.currentTask.id===y.id){clearTimeout(q.currentTask.timeout);let{resolve:E}=q.currentTask;q.currentTask=null,E(y)}if(y.success)this.stats.completedTasks++;else this.stats.failedTasks++;let F=this.stats.averageDuration*(this.stats.completedTasks+this.stats.failedTasks-1)+y.duration;if(this.stats.averageDuration=F/(this.stats.completedTasks+this.stats.failedTasks),this.updateStats(),q.taskCount>=this.config.recycleAfter)this.recycleWorker(q);this.processQueue()}handleWorkerError(q,y){if(q.busy=!1,q.currentTask){clearTimeout(q.currentTask.timeout);let{reject:F}=q.currentTask;q.currentTask=null,F(y)}this.stats.failedTasks++,this.updateStats(),this.recycleWorker(q),this.processQueue()}removeWorker(q){let y=this.workers.indexOf(q);if(y!==-1)this.workers.splice(y,1),q.worker.terminate();this.updateStats()}recycleWorker(q){if(this.removeWorker(q),!this.isShuttingDown&&this.workers.length<this.config.maxWorkers)this.createWorker()}checkIdleWorkers(){let q=Date.now(),y=1;for(let F=this.workers.length-1;F>=0;F--){let E=this.workers[F];if(!E.busy&&q-E.lastActive>this.config.idleTimeout&&this.workers.length>1)this.removeWorker(E)}}processQueue(){while(this.taskQueue.length>0){let q;for(let G=0;G<this.workers.length;G++)if(!this.workers[G].busy){q=this.workers[G];break}if(!q&&this.workers.length<this.config.maxWorkers)q=this.createWorker();if(!q)return;let{task:y,resolve:F,reject:E}=this.taskQueue.shift();q.busy=!0,q.lastActive=Date.now();let H=setTimeout(()=>{if(q.currentTask?.id===y.id)q.currentTask=null;E(Error(`Task ${y.id} timed out`)),this.recycleWorker(q)},this.config.taskTimeout);q.currentTask={id:y.id,resolve:F,reject:E,timeout:H},q.worker.postMessage(y),this.updateStats()}}async submit(q){return this.stats.totalTasks++,new Promise((y,F)=>{this.taskQueue.push({task:q,resolve:y,reject:F}),this.processQueue()})}async processFiles(q,y={}){let F=q.map((E,H)=>({id:`task-${H}-${Date.now()}`,type:"process",filePath:E.path,sourceCode:E.content,config:y}));return Promise.all(F.map((E)=>this.submit(E)))}updateStats(){let q=0;for(let y=0;y<this.workers.length;y++)if(this.workers[y].busy)q++;this.stats.activeWorkers=q,this.stats.idleWorkers=this.workers.length-q}getStats(){return{...this.stats}}async shutdown(){if(this.isShuttingDown=!0,this.idleCheckInterval)clearInterval(this.idleCheckInterval);while(this.taskQueue.length>0||this.workers.some((q)=>q.busy))await new Promise((q)=>setTimeout(q,100));await Promise.all(this.workers.map((q)=>q.worker.terminate())),this.workers=[]}}function N(q={}){return new L(q)}async function U(q,y={},F={}){let E=N(F);await E.init();try{return await E.processFiles(q,y)}finally{await E.shutdown()}}function V(q,y){let F=[];for(let E=0;E<q.length;E+=y)F.push(q.slice(E,E+y));return F}function X(q,y=K().length){if(q<=y)return 1;let E=y*3;return Math.max(1,Math.ceil(q/E))}
export{L as xe,N as ye,U as ze,V as Ae,X as Be};