@@ -19,6 +19,114 @@ function printMessage(message: string | ts.DiagnosticMessageChain, stream: Writa
1919 }
2020}
2121
22+ function validateProjectTsconfig ( compilerOptions : ts . CompilerOptions , outDir : string ) {
23+ const forcedOptions : Record < string , any [ ] > = {
24+ target : [ ts . ScriptTarget . ES2023 , ts . ScriptTarget . ES2020 ] ,
25+ module : [ ts . ModuleKind . ES2022 , ts . ModuleKind . ES2020 ] ,
26+ moduleResolution : [ ts . ModuleResolutionKind . NodeJs ] ,
27+ resolveJsonModule : [ false ] ,
28+ esModuleInterop : [ true ] ,
29+ outDir : [ outDir ] ,
30+ } ;
31+
32+ const optionNames : Record < string , Record < any , string > > = {
33+ target : Object . entries ( ts . ScriptTarget ) . reduce ( ( acc , [ k , v ] ) => ( { ...acc , [ v ] : k } ) , { } ) ,
34+ module : Object . entries ( ts . ModuleKind ) . reduce ( ( acc , [ k , v ] ) => ( { ...acc , [ v ] : k } ) , { } ) ,
35+ moduleResolution : Object . entries ( ts . ModuleResolutionKind ) . reduce (
36+ ( acc , [ k , v ] ) => ( { ...acc , [ v ] : k } ) ,
37+ { }
38+ ) ,
39+ } ;
40+
41+ for ( const [ key , values ] of Object . entries ( forcedOptions ) ) {
42+ const valueNames = values . map ( ( v ) => optionNames [ key ] ?. [ v ] ?? v ) . join ( ", " ) ;
43+ if ( compilerOptions [ key ] && ! values . includes ( compilerOptions [ key ] ) ) {
44+ throw new Error ( `tsconfig.json must have ${ key } set to one of: [ ${ valueNames } ]` ) ;
45+ } else if ( ! compilerOptions [ key ] ) {
46+ compilerOptions [ key ] = values [ 0 ] ;
47+ }
48+ }
49+ }
50+
51+ export async function compileProject (
52+ fs : FSInterface ,
53+ projectPath : string ,
54+ err : Writable ,
55+ out ?: Writable ,
56+ tsLibsPath : string = path . dirname (
57+ fileURLToPath ( import . meta. resolve ?.( "typescript" ) ?? "typescript" )
58+ )
59+ ) : Promise < boolean > {
60+ const outDir = "build" ;
61+ const system = tsvfs . createSystem ( fs , projectPath ) ;
62+
63+ const tsconfig = ts . findConfigFile ( "./" , system . fileExists , "tsconfig.json" ) ;
64+ if ( ! tsconfig ) {
65+ throw new Error ( `Could not find tsconfig.json in directory: ${ projectPath } ` ) ;
66+ }
67+ const configJsonFile = ts . readConfigFile ( tsconfig , system . readFile ) ;
68+ if ( configJsonFile . error ) {
69+ printMessage ( configJsonFile . error . messageText , err ) ;
70+ throw new Error ( "Error reading tsconfig.json" ) ;
71+ }
72+
73+ validateProjectTsconfig ( configJsonFile . config , outDir ) ;
74+ return await compile (
75+ fs ,
76+ projectPath ,
77+ outDir ,
78+ configJsonFile . config ,
79+ system ,
80+ err ,
81+ out ,
82+ false ,
83+ tsLibsPath
84+ ) ;
85+ }
86+
87+ export async function compileLibrary (
88+ fs : FSInterface ,
89+ libraryPath : string ,
90+ err : Writable ,
91+ out ?: Writable ,
92+ transpileOnly : boolean = false ,
93+ tsLibsPath : string = path . dirname (
94+ fileURLToPath ( import . meta. resolve ?.( "typescript" ) ?? "typescript" )
95+ )
96+ ) : Promise < boolean > {
97+ const outDir = "dist" ;
98+ const system = tsvfs . createSystem ( fs , libraryPath ) ;
99+
100+ const configJson = {
101+ compilerOptions : {
102+ target : "ES2023" ,
103+ module : "ES2022" ,
104+ lib : [ "es2023" ] ,
105+ moduleResolution : "node" ,
106+ declaration : true ,
107+ declarationDir : "dist/types" ,
108+ outDir : "dist/js" ,
109+ rootDir : "src" ,
110+ strict : true ,
111+ baseUrl : "." ,
112+ noEmitOnError : ! transpileOnly ,
113+ } ,
114+ include : [ "src" ] ,
115+ } ;
116+
117+ return await compile (
118+ fs ,
119+ libraryPath ,
120+ outDir ,
121+ configJson ,
122+ system ,
123+ err ,
124+ out ,
125+ transpileOnly ,
126+ tsLibsPath
127+ ) ;
128+ }
129+
22130/**
23131 * Compiles TypeScript files with custom FSInterface
24132 * @param fs - The file system interface (Node, zenfs, etc.)
@@ -34,68 +142,28 @@ export async function compile(
34142 fs : FSInterface ,
35143 inputDir : string ,
36144 outDir : string ,
145+ configJson : Record < string , unknown > ,
146+ system : ts . System ,
37147 err : Writable ,
38148 out ?: Writable ,
149+ transpileOnly : boolean = false ,
39150 tsLibsPath : string = path . dirname (
40151 fileURLToPath ( import . meta. resolve ?.( "typescript" ) ?? "typescript" )
41152 )
42153) : Promise < boolean > {
43- const system = tsvfs . createSystem ( fs , inputDir ) ;
44- const tsconfig = ts . findConfigFile ( "./" , system . fileExists , "tsconfig.json" ) ;
45- if ( ! tsconfig ) {
46- throw new Error ( `Could not find tsconfig.json in directory: ${ inputDir } ` ) ;
47- }
48- const config = ts . readConfigFile ( tsconfig , system . readFile ) ;
49- if ( config . error ) {
50- printMessage ( config . error . messageText , err ) ;
51- throw new Error ( "Error reading tsconfig.json" ) ;
52- }
53-
54- // convert enum values to names for better error messages
55- const optionNames : Record < string , Record < any , string > > = {
56- target : Object . entries ( ts . ScriptTarget ) . reduce ( ( acc , [ k , v ] ) => ( { ...acc , [ v ] : k } ) , { } ) ,
57- module : Object . entries ( ts . ModuleKind ) . reduce ( ( acc , [ k , v ] ) => ( { ...acc , [ v ] : k } ) , { } ) ,
58- moduleResolution : Object . entries ( ts . ModuleResolutionKind ) . reduce (
59- ( acc , [ k , v ] ) => ( { ...acc , [ v ] : k } ) ,
60- { }
61- ) ,
62- } ;
63-
64- const forcedOptions : Record < string , any [ ] > = {
65- target : [ ts . ScriptTarget . ES2023 , ts . ScriptTarget . ES2020 ] ,
66- module : [ ts . ModuleKind . ES2022 , ts . ModuleKind . ES2020 ] ,
67- moduleResolution : [ ts . ModuleResolutionKind . NodeJs ] ,
68- resolveJsonModule : [ false ] ,
69- esModuleInterop : [ true ] ,
70- outDir : [ outDir ] ,
71- } ;
72-
73- const {
74- options : compilerOptions ,
75- fileNames,
76- errors,
77- } = ts . parseJsonConfigFileContent ( config . config , system , "./" ) ;
154+ const { options, fileNames, errors } = ts . parseJsonConfigFileContent ( configJson , system , "./" ) ;
78155 if ( errors . length > 0 ) {
79156 errors . forEach ( ( error ) => printMessage ( error . messageText , err ) ) ;
80157 throw new Error ( `Error parsing tsconfig.json - ${ errors . length } error(s) found` ) ;
81158 }
82159
83- for ( const [ key , values ] of Object . entries ( forcedOptions ) ) {
84- const valueNames = values . map ( ( v ) => optionNames [ key ] ?. [ v ] ?? v ) . join ( ", " ) ;
85- if ( compilerOptions [ key ] && ! values . includes ( compilerOptions [ key ] ) ) {
86- throw new Error ( `tsconfig.json must have ${ key } set to one of: [ ${ valueNames } ]` ) ;
87- } else if ( ! compilerOptions [ key ] ) {
88- compilerOptions [ key ] = values [ 0 ] ;
89- }
90- }
91-
92160 out ?. write ( "Compiling files: [" + fileNames . join ( ", " ) + "]\n" ) ;
93161
94- const host = tsvfs . createVirtualCompilerHost ( system , compilerOptions , tsLibsPath ) ;
162+ const host = tsvfs . createVirtualCompilerHost ( system , options , tsLibsPath ) ;
95163
96164 const program = ts . createProgram ( {
97165 rootNames : fileNames ,
98- options : compilerOptions ,
166+ options,
99167 host : host . compilerHost ,
100168 } ) ;
101169 const emitResult = program . emit ( ) ;
@@ -125,5 +193,5 @@ export async function compile(
125193 throw new Error ( "Compilation failed" ) ;
126194 }
127195
128- return ! emitResult . emitSkipped && ! error ;
196+ return ! emitResult . emitSkipped && ( transpileOnly || ! error ) ;
129197}
0 commit comments