1
1
import ts from 'typescript' ;
2
2
import consola from 'consola' ;
3
- import { performance } from 'perf_hooks' ;
4
- import { timeFrom , normalizePath } from '../utils.js' ;
5
- import { createLogger } from './logger.js' ;
6
- import formatAliasToTSPathsConfig from './formatAliasToTSPathsConfig.js' ;
7
- import type { TaskConfig } from '../types.js' ;
3
+ import { normalizePath } from '../utils.js' ;
4
+ import { TaskConfig } from '../types.js' ;
8
5
import { prepareSingleFileReplaceTscAliasPaths } from 'tsc-alias' ;
9
6
import fse from 'fs-extra' ;
10
7
import * as path from 'path' ;
@@ -23,8 +20,8 @@ export interface DtsInputFile extends File {
23
20
dtsPath ?: string ;
24
21
}
25
22
26
- const normalizeDtsInput = ( file : File , rootDir : string , outputDir : string ) : DtsInputFile => {
27
- const { filePath , ext } = file ;
23
+ const normalizeDtsInput = ( filePath : string , rootDir : string , outputDir : string ) : DtsInputFile => {
24
+ const ext = path . extname ( filePath ) as FileExt ;
28
25
// https://www.typescriptlang.org/docs/handbook/esm-node.html#new-file-extensions
29
26
// a.js -> a.d.ts
30
27
// a.cjs -> a.d.cts
@@ -34,59 +31,106 @@ const normalizeDtsInput = (file: File, rootDir: string, outputDir: string): DtsI
34
31
// a.mts -> a.d.mts
35
32
const dtsPath = filePath . replace ( path . join ( rootDir , 'src' ) , outputDir ) . replace ( ext , `.d.${ / ^ \. [ j t ] / . test ( ext ) ? '' : ext [ 1 ] } ts` ) ;
36
33
return {
37
- ...file ,
34
+ filePath,
35
+ ext,
38
36
dtsPath,
39
37
} ;
40
38
} ;
41
39
42
- interface DtsCompileOptions {
40
+ export interface DtsCompileOptions {
43
41
// In watch mode, it only contains the updated file names. In build mode, it contains all file names.
44
- files : File [ ] ;
42
+ files : string [ ] ;
45
43
alias : TaskConfig [ 'alias' ] ;
46
44
rootDir : string ;
47
45
outputDir : string ;
46
+ }
47
+
48
+ function formatAliasToTSPathsConfig ( alias : TaskConfig [ 'alias' ] ) {
49
+ const paths : { [ from : string ] : [ string ] } = { } ;
50
+
51
+ Object . entries ( alias || { } )
52
+ . forEach ( ( [ key , value ] ) => {
53
+ const [ pathKey , pathValue ] = formatPath ( key , value ) ;
54
+ paths [ pathKey ] = [ pathValue ] ;
55
+ } ) ;
48
56
57
+ return paths ;
49
58
}
50
59
51
- export async function dtsCompile ( { files , alias , rootDir , outputDir } : DtsCompileOptions ) : Promise < DtsInputFile [ ] > {
52
- if ( ! files . length ) {
53
- return ;
60
+ function formatPath ( key : string , value : string ) {
61
+ if ( key . endsWith ( '$' ) ) {
62
+ return [ key . replace ( / \$ $ / , '' ) , value ] ;
54
63
}
64
+ // abc -> abc/*
65
+ // abc/ -> abc/*
66
+ return [ addWildcard ( key ) , addWildcard ( value ) ] ;
67
+ }
55
68
56
- const tsConfig = await getTSConfig ( rootDir , outputDir , alias ) ;
69
+ function addWildcard ( str : string ) {
70
+ return `${ str . endsWith ( '/' ) ? str : `${ str } /` } *` ;
71
+ }
57
72
58
- const logger = createLogger ( 'dts' ) ;
73
+ async function getTSConfig (
74
+ rootDir : string ,
75
+ outputDir : string ,
76
+ alias : TaskConfig [ 'alias' ] ,
77
+ ) {
78
+ const defaultTSCompilerOptions : ts . CompilerOptions = {
79
+ allowJs : true ,
80
+ declaration : true ,
81
+ emitDeclarationOnly : true ,
82
+ incremental : true ,
83
+ skipLibCheck : true ,
84
+ paths : formatAliasToTSPathsConfig ( alias ) , // default add alias to paths
85
+ } ;
86
+ const projectTSConfig = await getProjectTSConfig ( rootDir ) ;
87
+ const tsConfig : ts . ParsedCommandLine = merge (
88
+ { options : defaultTSCompilerOptions } ,
89
+ projectTSConfig ,
90
+ {
91
+ options : {
92
+ outDir : outputDir ,
93
+ rootDir : path . join ( rootDir , 'src' ) ,
94
+ } ,
95
+ } ,
96
+ ) ;
59
97
60
- logger . debug ( 'Start Compiling typescript declarations...' ) ;
98
+ return tsConfig ;
99
+ }
61
100
62
- const dtsCompileStart = performance . now ( ) ;
101
+ async function getProjectTSConfig ( rootDir : string ) : Promise < ts . ParsedCommandLine > {
102
+ const tsconfigPath = ts . findConfigFile ( rootDir , ts . sys . fileExists ) ;
103
+ if ( tsconfigPath ) {
104
+ const tsconfigFile = ts . readConfigFile ( tsconfigPath , ts . sys . readFile ) ;
105
+ return ts . parseJsonConfigFileContent (
106
+ tsconfigFile . config ,
107
+ ts . sys ,
108
+ path . dirname ( tsconfigPath ) ,
109
+ ) ;
110
+ }
63
111
64
- const _files = files
65
- . map ( ( file ) => normalizeDtsInput ( file , rootDir , outputDir ) )
66
- . map ( ( { filePath, dtsPath, ...rest } ) => ( {
67
- ...rest ,
68
- // Be compatible with Windows env.
69
- filePath : normalizePath ( filePath ) ,
70
- dtsPath : normalizePath ( dtsPath ) ,
71
- } ) ) ;
112
+ return {
113
+ options : { } ,
114
+ fileNames : [ ] ,
115
+ errors : [ ] ,
116
+ } ;
117
+ }
72
118
73
- const dtsFiles = { } ;
119
+ export async function dtsCompile ( { files, rootDir, outputDir, alias } : DtsCompileOptions ) : Promise < DtsInputFile [ ] > {
120
+ if ( ! files . length ) {
121
+ return [ ] ;
122
+ }
74
123
75
- // Create ts host and custom the writeFile and readFile.
76
- const host = ts . createCompilerHost ( tsConfig . options ) ;
77
- host . writeFile = ( fileName , contents ) => {
78
- dtsFiles [ fileName ] = contents ;
79
- } ;
124
+ const tsConfig = await getTSConfig ( rootDir , outputDir , alias ) ;
80
125
81
- const _readFile = host . readFile ;
82
- // Hijack `readFile` to prevent reading file twice
83
- host . readFile = ( fileName ) => {
84
- const foundItem = files . find ( ( file ) => file . filePath === fileName ) ;
85
- if ( foundItem && foundItem . srcCode ) {
86
- return foundItem . srcCode ;
87
- }
88
- return _readFile ( fileName ) ;
89
- } ;
126
+ const _files = files
127
+ . map ( ( file ) => normalizeDtsInput ( file , rootDir , outputDir ) )
128
+ . map < DtsInputFile > ( ( { filePath, dtsPath, ...rest } ) => ( {
129
+ ...rest ,
130
+ // Be compatible with Windows env.
131
+ filePath : normalizePath ( filePath ) ,
132
+ dtsPath : normalizePath ( dtsPath ) ,
133
+ } ) ) ;
90
134
91
135
// In order to only include the update files instead of all the files in the watch mode.
92
136
function getProgramRootNames ( originalFilenames : string [ ] ) {
@@ -97,7 +141,13 @@ export async function dtsCompile({ files, alias, rootDir, outputDir }: DtsCompil
97
141
return [ ...needCompileFileNames , ...dtsFilenames ] ;
98
142
}
99
143
100
- // Create ts program.
144
+ const dtsFiles = { } ;
145
+ const host = ts . createCompilerHost ( tsConfig . options ) ;
146
+
147
+ host . writeFile = ( fileName , contents ) => {
148
+ dtsFiles [ fileName ] = contents ;
149
+ } ;
150
+
101
151
const programOptions : ts . CreateProgramOptions = {
102
152
rootNames : getProgramRootNames ( tsConfig . fileNames ) ,
103
153
options : tsConfig . options ,
@@ -107,8 +157,6 @@ export async function dtsCompile({ files, alias, rootDir, outputDir }: DtsCompil
107
157
} ;
108
158
const program = ts . createProgram ( programOptions ) ;
109
159
110
- logger . debug ( `Initializing program takes ${ timeFrom ( dtsCompileStart ) } ` ) ;
111
-
112
160
const emitResult = program . emit ( ) ;
113
161
114
162
if ( emitResult . diagnostics && emitResult . diagnostics . length > 0 ) {
@@ -123,9 +171,17 @@ export async function dtsCompile({ files, alias, rootDir, outputDir }: DtsCompil
123
171
} ) ;
124
172
}
125
173
174
+ if ( ! Object . keys ( alias ) . length ) {
175
+ // no alias config
176
+ return _files . map ( ( file ) => ( {
177
+ ...file ,
178
+ dtsContent : dtsFiles [ file . dtsPath ] ,
179
+ } ) ) ;
180
+ }
181
+
126
182
// We use tsc-alias to resolve d.ts alias.
127
183
// Reason: https://github.com/microsoft/TypeScript/issues/30952#issuecomment-1114225407
128
- const tsConfigLocalPath = path . join ( rootDir , 'node_modules/pkg/tsconfig.json' ) ;
184
+ const tsConfigLocalPath = path . join ( rootDir , 'node_modules/.cache/ice- pkg/tsconfig.json' ) ;
129
185
await fse . ensureFile ( tsConfigLocalPath ) ;
130
186
await fse . writeJSON ( tsConfigLocalPath , {
131
187
...tsConfig ,
@@ -142,53 +198,5 @@ export async function dtsCompile({ files, alias, rootDir, outputDir }: DtsCompil
142
198
dtsContent : dtsFiles [ file . dtsPath ] ? runFile ( { fileContents : dtsFiles [ file . dtsPath ] , filePath : file . dtsPath } ) : '' ,
143
199
} ) ) ;
144
200
145
- logger . debug ( `Generating declaration files take ${ timeFrom ( dtsCompileStart ) } ` ) ;
146
-
147
201
return result ;
148
202
}
149
-
150
- async function getTSConfig (
151
- rootDir : string ,
152
- outputDir : string ,
153
- alias : TaskConfig [ 'alias' ] ,
154
- ) {
155
- const defaultTSCompilerOptions : ts . CompilerOptions = {
156
- allowJs : true ,
157
- declaration : true ,
158
- emitDeclarationOnly : true ,
159
- incremental : true ,
160
- skipLibCheck : true ,
161
- paths : formatAliasToTSPathsConfig ( alias ) , // default add alias to paths
162
- } ;
163
- const projectTSConfig = await getProjectTSConfig ( rootDir ) ;
164
- const tsConfig : ts . ParsedCommandLine = merge (
165
- { options : defaultTSCompilerOptions } ,
166
- projectTSConfig ,
167
- {
168
- options : {
169
- outDir : outputDir ,
170
- rootDir : path . join ( rootDir , 'src' ) ,
171
- } ,
172
- } ,
173
- ) ;
174
-
175
- return tsConfig ;
176
- }
177
-
178
- async function getProjectTSConfig ( rootDir : string ) : Promise < ts . ParsedCommandLine > {
179
- const tsconfigPath = ts . findConfigFile ( rootDir , ts . sys . fileExists ) ;
180
- if ( tsconfigPath ) {
181
- const tsconfigFile = ts . readConfigFile ( tsconfigPath , ts . sys . readFile ) ;
182
- return ts . parseJsonConfigFileContent (
183
- tsconfigFile . config ,
184
- ts . sys ,
185
- path . dirname ( tsconfigPath ) ,
186
- ) ;
187
- }
188
-
189
- return {
190
- options : { } ,
191
- fileNames : [ ] ,
192
- errors : [ ] ,
193
- } ;
194
- }
0 commit comments