1+ import fs from 'node:fs'
12import p from 'node:path'
23import process from 'node:process'
34import type { BunPlugin } from 'bun'
45import ts from 'typescript'
56
6- interface TsOptions {
7+ export interface TsOptions {
78 rootDir : string
89 base : string
910 declaration : boolean
@@ -14,7 +15,7 @@ interface TsOptions {
1415 [ index : string ] : any
1516}
1617
17- interface DtsOptions {
18+ export interface DtsOptions {
1819 /**
1920 * The base directory of the source files. If not provided, it
2021 * will use the current working directory of the process.
@@ -71,91 +72,76 @@ export async function generate(entryPoints: string | string[], options?: DtsOpti
7172 const root = ( options ?. root ?? 'src' ) . replace ( / ^ \. \/ / , '' )
7273
7374 try {
74- const configJson = ts . readConfigFile ( path , ts . sys . readFile ) . config
75+ const configFile = ts . readConfigFile ( path , ts . sys . readFile )
76+ if ( configFile . error ) {
77+ throw new Error ( `Failed to read tsconfig: ${ configFile . error . messageText } ` )
78+ }
79+
7580 const cwd = options ?. cwd ?? process . cwd ( )
7681 const base = options ?. base ?? cwd
77- const rootDir = `${ cwd } /${ root } `
78-
79- // Merge the base tsconfig with the package-specific one
80- const mergedConfig = {
81- ...configJson ,
82- compilerOptions : {
83- ...configJson . compilerOptions ,
84- ...options ?. compiler ,
85- } ,
82+ const rootDir = p . resolve ( cwd , root )
83+
84+ const parsedCommandLine = ts . parseJsonConfigFileContent ( configFile . config , ts . sys , cwd )
85+ if ( parsedCommandLine . errors . length ) {
86+ throw new Error ( `Failed to parse tsconfig: ${ parsedCommandLine . errors . map ( ( e ) => e . messageText ) . join ( ', ' ) } ` )
8687 }
8788
88- const opts : TsOptions = {
89- base,
90- baseUrl : base ,
91- rootDir,
89+ const outDir = p . resolve ( cwd , options ?. outdir || parsedCommandLine . options . outDir || 'dist' )
90+
91+ const compilerOptions : ts . CompilerOptions = {
92+ ...parsedCommandLine . options ,
93+ ...options ?. compiler ,
9294 declaration : true ,
9395 emitDeclarationOnly : true ,
9496 noEmit : false ,
95- isolatedDeclarations : undefined ,
96- ...( options ?. include && { include : options . include } ) ,
97+ declarationMap : true ,
98+ outDir : outDir ,
99+ rootDir : rootDir ,
100+ incremental : false , // Disable incremental compilation
97101 }
98102
99- const parsedConfig = ts . parseJsonConfigFileContent ( mergedConfig , ts . sys , cwd , opts , path )
100- parsedConfig . options . emitDeclarationOnly = true
101-
102- // Use the outdir from options if provided, otherwise use the one from tsconfig
103- const outDir = options ?. outdir || parsedConfig . options . outDir || 'dist'
104-
105- // Ensure outDir is relative to the package root, not the monorepo root
106- parsedConfig . options . outDir = p . resolve ( cwd , outDir )
107-
108- const host = ts . createCompilerHost ( parsedConfig . options )
109-
110- // Custom transformers to modify the output path of declaration files
111- const customTransformers : ts . CustomTransformers = {
112- afterDeclarations : [
113- ( context ) => {
114- return ( sourceFile ) => {
115- if ( 'isDeclarationFile' in sourceFile ) {
116- const originalFileName = sourceFile . fileName
117- const entryPointName = p . basename ( originalFileName , '.ts' )
118- const newFileName = p . join ( parsedConfig . options . outDir || 'dist' , `${ entryPointName } .d.ts` )
119-
120- return ts . factory . updateSourceFile (
121- sourceFile ,
122- sourceFile . statements ,
123- sourceFile . isDeclarationFile ,
124- sourceFile . referencedFiles ,
125- sourceFile . typeReferenceDirectives ,
126- sourceFile . hasNoDefaultLib ,
127- [ { fileName : newFileName , pos : 0 , end : 0 } ] ,
128- )
129- }
130- return sourceFile
131- }
132- } ,
133- ] ,
134- }
103+ const host = ts . createCompilerHost ( compilerOptions )
104+
105+ // console.log('Debug:', {
106+ // cwd,
107+ // rootDir,
108+ // outDir,
109+ // entryPoints: Array.isArray(entryPoints) ? entryPoints : [entryPoints],
110+ // })
135111
136112 const program = ts . createProgram ( {
137113 rootNames : Array . isArray ( entryPoints ) ? entryPoints : [ entryPoints ] ,
138- options : parsedConfig . options ,
114+ options : compilerOptions ,
139115 host,
140116 } )
141117
142- program . emit (
143- undefined ,
144- ( fileName , data ) => {
145- if ( fileName . endsWith ( '.d.ts' ) ) {
146- const outputPath = p . join ( parsedConfig . options . outDir || 'dist' , p . relative ( rootDir , fileName ) )
147- try {
148- ts . sys . writeFile ( outputPath , data )
149- // console.log('Successfully wrote file:', outputPath)
150- } catch ( error ) {
151- console . error ( 'Error writing file:' , outputPath , error )
152- }
118+ const emitResult = program . emit ( undefined , ( fileName , data ) => {
119+ if ( fileName . endsWith ( '.d.ts' ) || fileName . endsWith ( '.d.ts.map' ) ) {
120+ const outputPath = p . join ( outDir , p . relative ( rootDir , fileName ) )
121+ const dir = p . dirname ( outputPath )
122+ if ( ! fs . existsSync ( dir ) ) {
123+ fs . mkdirSync ( dir , { recursive : true } )
153124 }
154- } ,
155- undefined ,
156- true , // Only emit declarations
157- customTransformers ,
158- )
125+ fs . writeFileSync ( outputPath , data )
126+ // console.log(`Generated: ${outputPath}`)
127+ }
128+ } )
129+
130+ const allDiagnostics = ts . getPreEmitDiagnostics ( program ) . concat ( emitResult . diagnostics )
131+
132+ if ( allDiagnostics . length ) {
133+ const formatHost : ts . FormatDiagnosticsHost = {
134+ getCanonicalFileName : ( path ) => path ,
135+ getCurrentDirectory : ts . sys . getCurrentDirectory ,
136+ getNewLine : ( ) => ts . sys . newLine ,
137+ }
138+ const message = ts . formatDiagnosticsWithColorAndContext ( allDiagnostics , formatHost )
139+ console . error ( message )
140+ }
141+
142+ if ( emitResult . emitSkipped ) {
143+ throw new Error ( 'TypeScript compilation failed' )
144+ }
159145 } catch ( error ) {
160146 console . error ( 'Error generating types:' , error )
161147 throw error
0 commit comments