UNPKG

@stacksjs/dtsx

Version:

A modern, fast .d.ts generation tool, powered by Bun.

178 lines (160 loc) 10.8 kB
// @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};