@@ -25,36 +25,55 @@ const path = require("path");
25
25
*/
26
26
27
27
/**
28
- * Convert TypeScript paths configuration to AliasPlugin aliases
28
+ * @param {string } pattern Path pattern
29
+ * @returns {number } Length of the prefix
30
+ */
31
+ function getPrefixLength ( pattern ) {
32
+ const prefixLength = pattern . indexOf ( "*" ) ;
33
+ if ( prefixLength === - 1 ) {
34
+ return pattern . length ;
35
+ }
36
+ return pattern . slice ( 0 , Math . max ( 0 , prefixLength ) ) . length ;
37
+ }
38
+
39
+ /**
40
+ * Sort path patterns.
41
+ * If a module name can be matched with multiple patterns then pattern with the longest prefix will be picked.
42
+ * @param {string[] } arr Array of path patterns
43
+ * @returns {string[] } Array of path patterns sorted by longest prefix
44
+ */
45
+ function sortByLongestPrefix ( arr ) {
46
+ return [ ...arr ] . sort ( ( a , b ) => getPrefixLength ( b ) - getPrefixLength ( a ) ) ;
47
+ }
48
+
49
+ /**
50
+ * Converts an absolute baseUrl and paths to an array of absolute mapping entries.
51
+ * The array is sorted by longest prefix.
52
+ * Having an array with entries allows us to keep a sorting order rather than
53
+ * sort by keys each time we use the mappings.
29
54
* @param {{[key: string]: string[]} } paths TypeScript paths mapping
30
55
* @param {string } baseUrl Base URL for resolving paths
31
56
* @returns {AliasOption[] } Array of alias options
32
57
*/
33
- function convertPathsToAliases ( paths , baseUrl ) {
58
+ function getAbsoluteMappingEntries ( paths , baseUrl ) {
59
+ /** @type {string[] } */
60
+ const sortedKeys = sortByLongestPrefix ( Object . keys ( paths ) ) ;
34
61
/** @type {AliasOption[] } */
35
- const aliases = [ ] ;
36
-
37
- for ( const [ pattern , mappings ] of Object . entries ( paths ) ) {
38
- // Handle exact matches (no wildcards)
39
- if ( ! pattern . includes ( "*" ) ) {
40
- if ( mappings . length > 0 ) {
41
- const targetPath = path . join ( baseUrl , mappings [ 0 ] ) ;
42
- aliases . push ( { name : pattern , alias : targetPath } ) ;
43
- }
44
- } else {
45
- // Handle wildcard patterns by mapping the directory
46
- const aliasName = pattern . replace ( / \/ \* $ / , "" ) ;
47
- // Convert targets like "dir/*" -> "dir"
48
- const aliasTargets = mappings . map ( ( mapping ) =>
49
- path . join ( baseUrl , mapping . replace ( / \/ \* $ / , "" ) ) ,
50
- ) ;
51
- if ( aliasTargets . length > 0 ) {
52
- aliases . push ( { name : aliasName , alias : aliasTargets [ 0 ] } ) ;
53
- }
62
+ const absolutePaths = [ ] ;
63
+
64
+ for ( const pattern of sortedKeys ) {
65
+ const mappings = paths [ pattern ] ;
66
+ const aliasName = pattern . replace ( / \/ \* $ / , "" ) ;
67
+ // Convert targets like "dir/*" -> "dir"
68
+ const aliasTargets = mappings . map ( ( mapping ) =>
69
+ path . join ( baseUrl , mapping . replace ( / \/ \* $ / , "" ) ) ,
70
+ ) ;
71
+ if ( aliasTargets . length > 0 ) {
72
+ absolutePaths . push ( { name : aliasName , alias : aliasTargets } ) ;
54
73
}
55
74
}
56
75
57
- return aliases ;
76
+ return absolutePaths ;
58
77
}
59
78
60
79
/**
@@ -65,9 +84,8 @@ function convertPathsToAliases(paths, baseUrl) {
65
84
*/
66
85
async function readTsconfigCompilerOptions ( fileSystem , absTsconfigPath ) {
67
86
try {
68
- const fs = fileSystem ;
69
87
const data = await new Promise ( ( resolve , reject ) => {
70
- fs . readFile ( absTsconfigPath , "utf8" , ( err , data ) => {
88
+ fileSystem . readFile ( absTsconfigPath , "utf8" , ( err , data ) => {
71
89
if ( err ) reject ( err ) ;
72
90
resolve ( data ) ;
73
91
} ) ;
@@ -112,63 +130,12 @@ async function loadTsconfigPaths(fileSystem, configFile) {
112
130
const fileDependencies = new Set ( ) ;
113
131
114
132
aliases . push (
115
- ...convertPathsToAliases (
133
+ ...getAbsoluteMappingEntries (
116
134
mainOptions . paths ,
117
135
/** @type {string } */ ( mainOptions . baseUrl ) ,
118
136
) ,
119
137
) ;
120
138
121
- // Collect references from the main tsconfig.json
122
- const tsconfigJson = await new Promise ( ( resolve , reject ) => {
123
- fs . readFile ( configPath , "utf8" , ( err , data ) => {
124
- if ( err ) reject ( err ) ;
125
- resolve ( JSON . parse ( /** @type {string } */ ( data ) ) ) ;
126
- } ) ;
127
- } ) ;
128
- fileDependencies . add ( configPath ) ;
129
-
130
- const references = Array . isArray ( tsconfigJson . references )
131
- ? tsconfigJson . references
132
- : [ ] ;
133
-
134
- for ( const ref of references ) {
135
- /** @type {string } */
136
- // TypeScript allows string or object with path
137
- const refPathLike = typeof ref === "string" ? ref : ref && ref . path ;
138
- if ( ! refPathLike ) continue ;
139
- let refPath = path . isAbsolute ( refPathLike )
140
- ? refPathLike
141
- : path . join ( path . dirname ( configPath ) , refPathLike ) ;
142
- // If reference points to a directory, append tsconfig.json
143
- try {
144
- const stat = await new Promise ( ( resolve , reject ) => {
145
- fs . stat ( refPath , ( err , stat ) => {
146
- if ( err ) reject ( err ) ;
147
- resolve ( stat ) ;
148
- } ) ;
149
- } ) ;
150
- if ( stat . isDirectory ( ) ) {
151
- refPath = path . join ( refPath , "tsconfig.json" ) ;
152
- }
153
- } catch ( _e ) {
154
- // if it doesn't exist as directory/file, try adding tsconfig.json
155
- if ( ! / \. j s o n $ / i. test ( refPath ) ) {
156
- refPath = path . join ( refPath , "tsconfig.json" ) ;
157
- }
158
- }
159
-
160
- const refOptions = await readTsconfigCompilerOptions ( fs , refPath ) ;
161
- if ( ! refOptions ) continue ;
162
- fileDependencies . add ( refPath ) ;
163
-
164
- aliases . push (
165
- ...convertPathsToAliases (
166
- refOptions . paths ,
167
- /** @type {string } */ ( refOptions . baseUrl ) ,
168
- ) ,
169
- ) ;
170
- }
171
-
172
139
return {
173
140
aliases,
174
141
fileDependencies,
@@ -187,7 +154,7 @@ function tsconfigPathsToResolveOptions(aliases) {
187
154
const modules = [ ] ;
188
155
for ( const opt of aliases ) {
189
156
if ( opt . name === "*" ) {
190
- modules . push ( /** @type {string } */ ( opt . alias ) ) ;
157
+ modules . push ( ... /** @type {Array< string> } */ ( opt . alias ) ) ;
191
158
} else {
192
159
alias . push ( opt ) ;
193
160
}
@@ -218,7 +185,7 @@ async function loadTsconfigFile(fileSystem, configFile) {
218
185
}
219
186
}
220
187
221
- module . exports . convertPathsToAliases = convertPathsToAliases ;
188
+ module . exports . getAbsoluteMappingEntries = getAbsoluteMappingEntries ;
222
189
module . exports . loadTsconfigFile = loadTsconfigFile ;
223
190
module . exports . loadTsconfigPaths = loadTsconfigPaths ;
224
191
module . exports . readTsconfigCompilerOptions = readTsconfigCompilerOptions ;
0 commit comments