5
5
6
6
"use strict" ;
7
7
8
- const path = require ( "path" ) ;
9
8
const { aliasResolveHandler } = require ( "./AliasUtils" ) ;
10
9
const { modulesResolveHandler } = require ( "./ModulesUtils" ) ;
10
+ const TsconfigPathsUtils = require ( "./TsconfigPathsUtils" ) ;
11
11
12
12
/** @typedef {import("./Resolver") } Resolver */
13
13
/** @typedef {import("./Resolver").ResolveStepHook } ResolveStepHook */
14
14
/** @typedef {import("./AliasUtils").AliasOption } AliasOption */
15
15
/** @typedef {import("./Resolver").ResolveRequest } ResolveRequest */
16
16
/** @typedef {import("./Resolver").ResolveContext } ResolveContext */
17
-
18
- /**
19
- * @typedef {object } TsconfigPathsPluginOptions
20
- * @property {string= } configFile - Path to tsconfig.json file
21
- * @property {string= } baseUrl - Override baseUrl from tsconfig.json
22
- * @property {string= } context - Context directory for resolving relative paths
23
- */
17
+ /** @typedef {import("./Resolver").TsconfigFileData } TsconfigFileData */
24
18
25
19
const DEFAULT_CONFIG_FILE = "tsconfig.json" ;
26
20
@@ -46,14 +40,17 @@ module.exports = class TsconfigPathsPlugin {
46
40
"TsconfigPathsPlugin" ,
47
41
async ( request , resolveContext , callback ) => {
48
42
try {
49
- const aliases = await this . beforeResolve (
43
+ const tsconfigFileData = await this . loadTsconfigFile (
50
44
resolver ,
51
45
request ,
52
46
resolveContext ,
53
47
) ;
48
+
49
+ if ( ! tsconfigFileData ) return callback ( ) ;
50
+
54
51
aliasResolveHandler (
55
52
resolver ,
56
- aliases . filter ( ( option ) => option . name !== "*" ) ,
53
+ tsconfigFileData . alias ,
57
54
aliasTarget ,
58
55
request ,
59
56
resolveContext ,
@@ -71,18 +68,17 @@ module.exports = class TsconfigPathsPlugin {
71
68
"TsconfigPathsPlugin" ,
72
69
async ( request , resolveContext , callback ) => {
73
70
try {
74
- const aliases = await this . beforeResolve (
71
+ const tsconfigFileData = await this . loadTsconfigFile (
75
72
resolver ,
76
73
request ,
77
74
resolveContext ,
78
75
) ;
79
- const directories = aliases
80
- . filter ( ( option ) => option . name === "*" )
81
- . map ( ( option ) => /** @type {string } */ ( option . alias ) ) ;
76
+
77
+ if ( ! tsconfigFileData ) return callback ( ) ;
82
78
83
79
modulesResolveHandler (
84
80
resolver ,
85
- directories ,
81
+ tsconfigFileData . modules ,
86
82
moduleTarget ,
87
83
request ,
88
84
resolveContext ,
@@ -100,178 +96,27 @@ module.exports = class TsconfigPathsPlugin {
100
96
* @param {Resolver } resolver the resolver
101
97
* @param {ResolveRequest } request the request
102
98
* @param {ResolveContext } resolveContext the resolve context
103
- * @returns {Promise<Array<AliasOption>> } the pre-processed request
104
- */
105
- async beforeResolve ( resolver , request , resolveContext ) {
106
- const tsconfigFileData =
107
- request . tsconfigFileData || ( await this . loadAndConvertPaths ( resolver ) ) ;
108
- if ( ! tsconfigFileData ) return [ ] ;
109
- const { aliases, fileDependencies } = tsconfigFileData ;
110
- for ( const fileDependency of fileDependencies ) {
111
- if ( resolveContext . fileDependencies ) {
112
- resolveContext . fileDependencies . add ( fileDependency ) ;
113
- }
114
- }
115
- request . tsconfigFileData = tsconfigFileData ;
116
- return aliases ;
117
- }
118
-
119
- /**
120
- * Load tsconfig.json (and referenced tsconfigs) and convert paths to AliasPlugin format
121
- * @param {Resolver } resolver the resolver
122
- * @returns {Promise<{aliases: AliasOption[], fileDependencies: Set<string>} | null> } Array of alias options for AliasPlugin
99
+ * @returns {Promise<TsconfigFileData | null> } the pre-processed request
123
100
*/
124
- async loadAndConvertPaths ( resolver ) {
125
- try {
126
- const fs = resolver . fileSystem ;
127
- const configPath = path . isAbsolute ( this . configFile )
128
- ? this . configFile
129
- : resolver . join ( process . cwd ( ) , this . configFile ) ;
130
-
131
- const mainOptions = await this . readTsconfigCompilerOptions (
132
- resolver ,
133
- configPath ,
134
- ) ;
135
- if ( ! mainOptions ) return null ;
136
-
137
- /** @type {AliasOption[] } */
138
- const aliases = [ ] ;
139
- /** @type {Set<string> } */
140
- const fileDependencies = new Set ( ) ;
141
- const result = {
142
- aliases,
143
- fileDependencies,
144
- } ;
145
- aliases . push (
146
- ...this . convertPathsToAliases (
147
- resolver ,
148
- mainOptions . paths ,
149
- mainOptions . baseUrl ,
150
- ) ,
101
+ async loadTsconfigFile ( resolver , request , resolveContext ) {
102
+ if ( typeof request . tsconfigFileData === "undefined" ) {
103
+ request . tsconfigFileData = await TsconfigPathsUtils . loadTsconfigFile (
104
+ resolver . fileSystem ,
105
+ this . configFile ,
151
106
) ;
152
-
153
- // Collect references from the main tsconfig.json
154
- const tsconfigJson = await new Promise ( ( resolve , reject ) => {
155
- fs . readFile ( configPath , "utf8" , ( err , data ) => {
156
- if ( err ) reject ( err ) ;
157
- resolve ( JSON . parse ( /** @type {string } */ ( data ) ) ) ;
158
- } ) ;
159
- } ) ;
160
- fileDependencies . add ( configPath ) ;
161
-
162
- const references = Array . isArray ( tsconfigJson . references )
163
- ? tsconfigJson . references
164
- : [ ] ;
165
-
166
- for ( const ref of references ) {
167
- /** @type {string } */
168
- // TypeScript allows string or object with path
169
- const refPathLike = typeof ref === "string" ? ref : ref && ref . path ;
170
- if ( ! refPathLike ) continue ;
171
- let refPath = path . isAbsolute ( refPathLike )
172
- ? refPathLike
173
- : resolver . join ( path . dirname ( configPath ) , refPathLike ) ;
174
- // If reference points to a directory, append tsconfig.json
175
- try {
176
- const stat = await new Promise ( ( resolve , reject ) => {
177
- fs . stat ( refPath , ( err , stat ) => {
178
- if ( err ) reject ( err ) ;
179
- resolve ( stat ) ;
180
- } ) ;
181
- } ) ;
182
- if ( stat . isDirectory ( ) ) {
183
- refPath = resolver . join ( refPath , "tsconfig.json" ) ;
184
- }
185
- } catch ( _e ) {
186
- // if it doesn't exist as directory/file, try adding tsconfig.json
187
- if ( ! / \. j s o n $ / i. test ( refPath ) ) {
188
- refPath = resolver . join ( refPath , "tsconfig.json" ) ;
189
- }
190
- }
191
-
192
- const refOptions = await this . readTsconfigCompilerOptions (
193
- resolver ,
194
- refPath ,
195
- ) ;
196
- if ( ! refOptions ) continue ;
197
- fileDependencies . add ( refPath ) ;
198
-
199
- aliases . push (
200
- ...this . convertPathsToAliases (
201
- resolver ,
202
- refOptions . paths ,
203
- refOptions . baseUrl ,
204
- ) ,
205
- ) ;
206
- }
207
-
208
- return result ;
209
- } catch ( _error ) {
210
- return null ;
211
107
}
212
- }
213
108
214
- /**
215
- * Read tsconfig.json and return normalized compiler options
216
- * @param {Resolver } resolver the resolver
217
- * @param {string } absTsconfigPath absolute path to tsconfig.json
218
- * @returns {Promise<{ baseUrl: string, paths: {[key: string]: string[]} } | null> } the normalized compiler options
219
- */
220
- async readTsconfigCompilerOptions ( resolver , absTsconfigPath ) {
221
- try {
222
- const fs = resolver . fileSystem ;
223
- const json = await new Promise ( ( resolve , reject ) => {
224
- fs . readFile ( absTsconfigPath , "utf8" , ( err , data ) => {
225
- if ( err ) reject ( err ) ;
226
- resolve ( JSON . parse ( /** @type {string } */ ( data ) ) ) ;
227
- } ) ;
228
- } ) ;
229
- const compilerOptions =
230
- json && json . compilerOptions ? json . compilerOptions : { } ;
231
- let { baseUrl } = compilerOptions ;
232
- if ( ! baseUrl ) {
233
- baseUrl = path . dirname ( absTsconfigPath ) ;
234
- } else if ( ! path . isAbsolute ( baseUrl ) ) {
235
- baseUrl = resolver . join ( path . dirname ( absTsconfigPath ) , baseUrl ) ;
236
- }
237
- const paths = compilerOptions . paths || { } ;
238
- return { baseUrl, paths } ;
239
- } catch ( _e ) {
109
+ const { tsconfigFileData } = request ;
110
+
111
+ if ( ! tsconfigFileData ) {
240
112
return null ;
241
113
}
242
- }
243
-
244
- /**
245
- * Convert TypeScript paths configuration to AliasPlugin aliases
246
- * @param {Resolver } resolver the resolver
247
- * @param {{[key: string]: string[]} } paths TypeScript paths mapping
248
- * @param {string } baseUrl Base URL for resolving paths
249
- * @returns {AliasOption[] } Array of alias options
250
- */
251
- convertPathsToAliases ( resolver , paths , baseUrl ) {
252
- /** @type {AliasOption[] } */
253
- const aliases = [ ] ;
254
114
255
- for ( const [ pattern , mappings ] of Object . entries ( paths ) ) {
256
- // Handle exact matches (no wildcards)
257
- if ( ! pattern . includes ( "*" ) ) {
258
- if ( mappings . length > 0 ) {
259
- const targetPath = resolver . join ( baseUrl , mappings [ 0 ] ) ;
260
- aliases . push ( { name : pattern , alias : targetPath } ) ;
261
- }
262
- } else {
263
- // Handle wildcard patterns by mapping the directory
264
- const aliasName = pattern . replace ( / \/ \* $ / , "" ) ;
265
- // Convert targets like "dir/*" -> "dir"
266
- const aliasTargets = mappings . map ( ( mapping ) =>
267
- resolver . join ( baseUrl , mapping . replace ( / \/ \* $ / , "" ) ) ,
268
- ) ;
269
- if ( aliasTargets . length > 0 ) {
270
- aliases . push ( { name : aliasName , alias : aliasTargets [ 0 ] } ) ;
271
- }
115
+ for ( const fileDependency of tsconfigFileData . fileDependencies ) {
116
+ if ( resolveContext . fileDependencies ) {
117
+ resolveContext . fileDependencies . add ( fileDependency ) ;
272
118
}
273
119
}
274
-
275
- return aliases ;
120
+ return tsconfigFileData ;
276
121
}
277
122
} ;
0 commit comments