2024-09-11 02:37:36 +00:00
"use strict" ;
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Copyright ( c ) Microsoft Corporation . All rights reserved .
* Licensed under the MIT License . See License . txt in the project root for license information .
* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
2025-03-01 02:01:53 +00:00
var _ _importDefault = ( this && this . _ _importDefault ) || function ( mod ) {
return ( mod && mod . _ _esModule ) ? mod : { "default" : mod } ;
} ;
2024-09-11 02:37:36 +00:00
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
2025-03-01 02:01:53 +00:00
exports . ESBuildTranspiler = exports . TscTranspiler = void 0 ;
const esbuild _1 = _ _importDefault ( require ( "esbuild" ) ) ;
const typescript _1 = _ _importDefault ( require ( "typescript" ) ) ;
const node _worker _threads _1 = _ _importDefault ( require ( "node:worker_threads" ) ) ;
const vinyl _1 = _ _importDefault ( require ( "vinyl" ) ) ;
2024-09-11 02:37:36 +00:00
const node _os _1 = require ( "node:os" ) ;
function transpile ( tsSrc , options ) {
const isAmd = /\n(import|export)/m . test ( tsSrc ) ;
2025-03-01 02:01:53 +00:00
if ( ! isAmd && options . compilerOptions ? . module === typescript _1 . default . ModuleKind . AMD ) {
2024-09-11 02:37:36 +00:00
// enforce NONE module-system for not-amd cases
2025-03-01 02:01:53 +00:00
options = { ... options , ... { compilerOptions : { ... options . compilerOptions , module : typescript _1 . default . ModuleKind . None } } } ;
2024-09-11 02:37:36 +00:00
}
2025-03-01 02:01:53 +00:00
const out = typescript _1 . default . transpileModule ( tsSrc , options ) ;
2024-09-11 02:37:36 +00:00
return {
jsSrc : out . outputText ,
diag : out . diagnostics ? ? [ ]
} ;
}
2025-03-01 02:01:53 +00:00
if ( ! node _worker _threads _1 . default . isMainThread ) {
2024-09-11 02:37:36 +00:00
// WORKER
2025-03-01 02:01:53 +00:00
node _worker _threads _1 . default . parentPort ? . addListener ( 'message' , ( req ) => {
2024-09-11 02:37:36 +00:00
const res = {
jsSrcs : [ ] ,
diagnostics : [ ]
} ;
for ( const tsSrc of req . tsSrcs ) {
const out = transpile ( tsSrc , req . options ) ;
res . jsSrcs . push ( out . jsSrc ) ;
res . diagnostics . push ( out . diag ) ;
}
2025-03-01 02:01:53 +00:00
node _worker _threads _1 . default . parentPort . postMessage ( res ) ;
2024-09-11 02:37:36 +00:00
} ) ;
}
class OutputFileNameOracle {
getOutputFileName ;
constructor ( cmdLine , configFilePath ) {
this . getOutputFileName = ( file ) => {
try {
// windows: path-sep normalizing
2025-03-01 02:01:53 +00:00
file = typescript _1 . default . normalizePath ( file ) ;
2024-09-11 02:37:36 +00:00
if ( ! cmdLine . options . configFilePath ) {
// this is needed for the INTERNAL getOutputFileNames-call below...
cmdLine . options . configFilePath = configFilePath ;
}
const isDts = file . endsWith ( '.d.ts' ) ;
if ( isDts ) {
file = file . slice ( 0 , - 5 ) + '.ts' ;
cmdLine . fileNames . push ( file ) ;
}
2025-03-01 02:01:53 +00:00
const outfile = typescript _1 . default . getOutputFileNames ( cmdLine , file , true ) [ 0 ] ;
2024-09-11 02:37:36 +00:00
if ( isDts ) {
cmdLine . fileNames . pop ( ) ;
}
return outfile ;
}
catch ( err ) {
console . error ( file , cmdLine . fileNames ) ;
console . error ( err ) ;
2025-03-01 02:01:53 +00:00
throw err ;
2024-09-11 02:37:36 +00:00
}
} ;
}
}
class TranspileWorker {
static pool = 1 ;
id = TranspileWorker . pool ++ ;
2025-03-01 02:01:53 +00:00
_worker = new node _worker _threads _1 . default . Worker ( _ _filename ) ;
2024-09-11 02:37:36 +00:00
_pending ;
_durations = [ ] ;
constructor ( outFileFn ) {
this . _worker . addListener ( 'message' , ( res ) => {
if ( ! this . _pending ) {
console . error ( 'RECEIVING data WITHOUT request' ) ;
return ;
}
const [ resolve , reject , files , options , t1 ] = this . _pending ;
const outFiles = [ ] ;
const diag = [ ] ;
for ( let i = 0 ; i < res . jsSrcs . length ; i ++ ) {
// inputs and outputs are aligned across the arrays
const file = files [ i ] ;
const jsSrc = res . jsSrcs [ i ] ;
const diag = res . diagnostics [ i ] ;
if ( diag . length > 0 ) {
diag . push ( ... diag ) ;
continue ;
}
let SuffixTypes ;
( function ( SuffixTypes ) {
SuffixTypes [ SuffixTypes [ "Dts" ] = 5 ] = "Dts" ;
SuffixTypes [ SuffixTypes [ "Ts" ] = 3 ] = "Ts" ;
SuffixTypes [ SuffixTypes [ "Unknown" ] = 0 ] = "Unknown" ;
} ) ( SuffixTypes || ( SuffixTypes = { } ) ) ;
const suffixLen = file . path . endsWith ( '.d.ts' ) ? 5 /* SuffixTypes.Dts */
: file . path . endsWith ( '.ts' ) ? 3 /* SuffixTypes.Ts */
: 0 /* SuffixTypes.Unknown */ ;
// check if output of a DTS-files isn't just "empty" and iff so
// skip this file
if ( suffixLen === 5 /* SuffixTypes.Dts */ && _isDefaultEmpty ( jsSrc ) ) {
continue ;
}
const outBase = options . compilerOptions ? . outDir ? ? file . base ;
const outPath = outFileFn ( file . path ) ;
2025-03-01 02:01:53 +00:00
outFiles . push ( new vinyl _1 . default ( {
2024-09-11 02:37:36 +00:00
path : outPath ,
base : outBase ,
contents : Buffer . from ( jsSrc ) ,
} ) ) ;
}
this . _pending = undefined ;
this . _durations . push ( Date . now ( ) - t1 ) ;
if ( diag . length > 0 ) {
reject ( diag ) ;
}
else {
resolve ( outFiles ) ;
}
} ) ;
}
terminate ( ) {
// console.log(`Worker#${this.id} ENDS after ${this._durations.length} jobs (total: ${this._durations.reduce((p, c) => p + c, 0)}, avg: ${this._durations.reduce((p, c) => p + c, 0) / this._durations.length})`);
this . _worker . terminate ( ) ;
}
get isBusy ( ) {
return this . _pending !== undefined ;
}
next ( files , options ) {
if ( this . _pending !== undefined ) {
throw new Error ( 'BUSY' ) ;
}
return new Promise ( ( resolve , reject ) => {
this . _pending = [ resolve , reject , files , options , Date . now ( ) ] ;
const req = {
options ,
tsSrcs : files . map ( file => String ( file . contents ) )
} ;
this . _worker . postMessage ( req ) ;
} ) ;
}
}
class TscTranspiler {
_onError ;
_cmdLine ;
static P = Math . floor ( ( 0 , node _os _1 . cpus ) ( ) . length * . 5 ) ;
_outputFileNames ;
onOutfile ;
_workerPool = [ ] ;
_queue = [ ] ;
_allJobs = [ ] ;
constructor ( logFn , _onError , configFilePath , _cmdLine ) {
this . _onError = _onError ;
this . _cmdLine = _cmdLine ;
logFn ( 'Transpile' , ` will use ${ TscTranspiler . P } transpile worker ` ) ;
this . _outputFileNames = new OutputFileNameOracle ( _cmdLine , configFilePath ) ;
}
async join ( ) {
// wait for all penindg jobs
this . _consumeQueue ( ) ;
await Promise . allSettled ( this . _allJobs ) ;
this . _allJobs . length = 0 ;
// terminate all worker
this . _workerPool . forEach ( w => w . terminate ( ) ) ;
this . _workerPool . length = 0 ;
}
transpile ( file ) {
if ( this . _cmdLine . options . noEmit ) {
// not doing ANYTHING here
return ;
}
const newLen = this . _queue . push ( file ) ;
if ( newLen > TscTranspiler . P * * 2 ) {
this . _consumeQueue ( ) ;
}
}
_consumeQueue ( ) {
if ( this . _queue . length === 0 ) {
// no work...
return ;
}
// kinda LAZYily create workers
if ( this . _workerPool . length === 0 ) {
for ( let i = 0 ; i < TscTranspiler . P ; i ++ ) {
this . _workerPool . push ( new TranspileWorker ( file => this . _outputFileNames . getOutputFileName ( file ) ) ) ;
}
}
const freeWorker = this . _workerPool . filter ( w => ! w . isBusy ) ;
if ( freeWorker . length === 0 ) {
// OK, they will pick up work themselves
return ;
}
for ( const worker of freeWorker ) {
if ( this . _queue . length === 0 ) {
break ;
}
const job = new Promise ( resolve => {
const consume = ( ) => {
const files = this . _queue . splice ( 0 , TscTranspiler . P ) ;
if ( files . length === 0 ) {
// DONE
resolve ( undefined ) ;
return ;
}
// work on the NEXT file
// const [inFile, outFn] = req;
worker . next ( files , { compilerOptions : this . _cmdLine . options } ) . then ( outFiles => {
if ( this . onOutfile ) {
outFiles . map ( this . onOutfile , this ) ;
}
consume ( ) ;
} ) . catch ( err => {
this . _onError ( err ) ;
} ) ;
} ;
consume ( ) ;
} ) ;
this . _allJobs . push ( job ) ;
}
}
}
exports . TscTranspiler = TscTranspiler ;
2025-03-01 02:01:53 +00:00
class ESBuildTranspiler {
2024-09-11 02:37:36 +00:00
_logFn ;
_onError ;
_cmdLine ;
_outputFileNames ;
_jobs = [ ] ;
2025-03-01 02:01:53 +00:00
onOutfile ;
_transformOpts ;
2024-09-11 02:37:36 +00:00
constructor ( _logFn , _onError , configFilePath , _cmdLine ) {
this . _logFn = _logFn ;
this . _onError = _onError ;
this . _cmdLine = _cmdLine ;
2025-03-01 02:01:53 +00:00
_logFn ( 'Transpile' , ` will use ESBuild to transpile source files ` ) ;
2024-09-11 02:37:36 +00:00
this . _outputFileNames = new OutputFileNameOracle ( _cmdLine , configFilePath ) ;
2025-03-01 02:01:53 +00:00
const isExtension = configFilePath . includes ( 'extensions' ) ;
this . _transformOpts = {
target : [ 'es2022' ] ,
format : isExtension ? 'cjs' : 'esm' ,
platform : isExtension ? 'node' : undefined ,
loader : 'ts' ,
sourcemap : 'inline' ,
tsconfigRaw : JSON . stringify ( {
compilerOptions : {
... this . _cmdLine . options ,
... {
module : isExtension ? typescript _1 . default . ModuleKind . CommonJS : undefined
}
}
} ) ,
supported : {
'class-static-blocks' : false , // SEE https://github.com/evanw/esbuild/issues/3823,
'dynamic-import' : ! isExtension , // see https://github.com/evanw/esbuild/issues/1281
'class-field' : ! isExtension
}
} ;
2024-09-11 02:37:36 +00:00
}
async join ( ) {
const jobs = this . _jobs . slice ( ) ;
this . _jobs . length = 0 ;
await Promise . allSettled ( jobs ) ;
}
transpile ( file ) {
2025-03-01 02:01:53 +00:00
if ( ! ( file . contents instanceof Buffer ) ) {
throw Error ( 'file.contents must be a Buffer' ) ;
2024-09-11 02:37:36 +00:00
}
const t1 = Date . now ( ) ;
2025-03-01 02:01:53 +00:00
this . _jobs . push ( esbuild _1 . default . transform ( file . contents , {
... this . _transformOpts ,
sourcefile : file . path ,
} ) . then ( result => {
2024-09-11 02:37:36 +00:00
// check if output of a DTS-files isn't just "empty" and iff so
// skip this file
2025-03-01 02:01:53 +00:00
if ( file . path . endsWith ( '.d.ts' ) && _isDefaultEmpty ( result . code ) ) {
2024-09-11 02:37:36 +00:00
return ;
}
const outBase = this . _cmdLine . options . outDir ? ? file . base ;
const outPath = this . _outputFileNames . getOutputFileName ( file . path ) ;
2025-03-01 02:01:53 +00:00
this . onOutfile ( new vinyl _1 . default ( {
2024-09-11 02:37:36 +00:00
path : outPath ,
base : outBase ,
2025-03-01 02:01:53 +00:00
contents : Buffer . from ( result . code ) ,
2024-09-11 02:37:36 +00:00
} ) ) ;
2025-03-01 02:01:53 +00:00
this . _logFn ( 'Transpile' , ` esbuild took ${ Date . now ( ) - t1 } ms for ${ file . path } ` ) ;
2024-09-11 02:37:36 +00:00
} ) . catch ( err => {
this . _onError ( err ) ;
} ) ) ;
}
}
2025-03-01 02:01:53 +00:00
exports . ESBuildTranspiler = ESBuildTranspiler ;
function _isDefaultEmpty ( src ) {
return src
. replace ( '"use strict";' , '' )
. replace ( /\/\/# sourceMappingURL.*^/ , '' )
. replace ( /\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm , '$1' )
. trim ( ) . length === 0 ;
}
2024-09-11 02:37:36 +00:00
//# sourceMappingURL=transpiler.js.map