3
3
var commondir = require ( 'commondir' ) ;
4
4
var events = require ( 'events' ) ;
5
5
var fs = require ( 'fs' ) ;
6
+ var realpath = require ( 'fs.realpath' )
6
7
var log = require ( 'util' ) . debuglog ( require ( '../package' ) . name ) ;
8
+ var trace = require ( 'util' ) . debuglog ( require ( '../package' ) . name + '-trace' ) ;
7
9
var os = require ( 'os' ) ;
8
10
var path = require ( 'path' ) ;
9
11
var util = require ( 'util' ) ;
10
12
11
13
module . exports = function ( ts ) {
12
- function Host ( currentDirectory , languageVersion ) {
13
- this . currentDirectory = currentDirectory ;
14
+ function Host ( currentDirectory , outputDirectory , languageVersion ) {
15
+ this . currentDirectory = this . getCanonicalFileName ( path . resolve ( currentDirectory ) ) ;
16
+ this . outputDirectory = this . getCanonicalFileName ( path . resolve ( outputDirectory ) ) ;
14
17
this . languageVersion = languageVersion ;
15
18
this . files = { } ;
16
19
this . previousFiles = { } ;
@@ -31,13 +34,21 @@ module.exports = function (ts) {
31
34
log ( 'Resetting (version %d)' , this . version ) ;
32
35
} ;
33
36
34
- Host . prototype . _normalizedRelative = function ( filename ) {
35
- return ts . normalizePath ( path . relative ( this . currentDirectory , path . resolve ( filename ) ) ) ;
36
- } ;
37
-
38
37
Host . prototype . _addFile = function ( filename , root ) {
39
- var normalized = this . _normalizedRelative ( filename ) ;
40
- log ( 'Parsing %s (norm: %s)' , filename , normalized ) ;
38
+
39
+ // Ensure that the relative, non-canonical file name is what's passed
40
+ // to 'createSourceFile', as that's the name that will be used in error
41
+ // messages, etc.
42
+
43
+ var relative = ts . normalizeSlashes ( path . relative (
44
+ this . currentDirectory ,
45
+ path . resolve (
46
+ this . currentDirectory ,
47
+ filename
48
+ )
49
+ ) ) ;
50
+ var canonical = this . _canonical ( filename ) ;
51
+ trace ( 'Parsing %s' , canonical ) ;
41
52
42
53
var text ;
43
54
try {
@@ -47,52 +58,44 @@ module.exports = function (ts) {
47
58
}
48
59
49
60
var file ;
50
- var current = this . files [ normalized ] ;
51
- var previous = this . previousFiles [ normalized ] ;
61
+ var current = this . files [ canonical ] ;
62
+ var previous = this . previousFiles [ canonical ] ;
52
63
var version ;
53
64
54
65
if ( current && current . contents === text ) {
55
66
file = current . ts ;
56
67
version = current . version ;
57
- log ( 'Reused current file %s (version %d)' , normalized , version ) ;
68
+ trace ( 'Reused current file %s (version %d)' , canonical , version ) ;
58
69
} else if ( previous && previous . contents === text ) {
59
70
file = previous . ts ;
60
71
version = previous . version ;
61
- log ( 'Reused previous file %s (version %d)' , normalized , version ) ;
72
+ trace ( 'Reused previous file %s (version %d)' , canonical , version ) ;
62
73
} else {
63
- file = ts . createSourceFile ( filename , text , this . languageVersion , true ) ;
74
+ file = ts . createSourceFile ( relative , text , this . languageVersion , true ) ;
64
75
version = this . version ;
65
- log ( 'New version of source file %s (version %d)' , normalized , version ) ;
76
+ trace ( 'New version of source file %s (version %d)' , canonical , version ) ;
66
77
}
67
78
68
- this . files [ normalized ] = {
69
- filename : filename ,
79
+ this . files [ canonical ] = {
80
+ filename : relative ,
70
81
contents : text ,
71
82
ts : file ,
72
83
root : root ,
73
84
version : version
74
85
} ;
75
-
76
- this . _emitFile ( normalized ) ;
86
+ this . emit ( 'file' , canonical , relative ) ;
77
87
78
88
return file ;
79
89
} ;
80
90
81
- Host . prototype . _emitFile = function ( normalized ) {
82
- var idPath = './' + normalized ;
83
- var fullPath = path . resolve ( idPath ) ;
84
- this . emit ( 'file' , fullPath , idPath ) ;
85
- }
86
-
87
91
Host . prototype . getSourceFile = function ( filename ) {
88
- var normalized = this . _normalizedRelative ( filename ) ;
89
-
90
- if ( this . files [ normalized ] )
91
- return this . files [ normalized ] . ts ;
92
-
93
- if ( normalized === '__lib.d.ts' )
92
+ if ( filename === '__lib.d.ts' ) {
94
93
return this . libDefault ;
95
-
94
+ }
95
+ var canonical = this . _canonical ( filename ) ;
96
+ if ( this . files [ canonical ] ) {
97
+ return this . files [ canonical ] . ts ;
98
+ }
96
99
return this . _addFile ( filename , false ) ;
97
100
} ;
98
101
@@ -103,17 +106,27 @@ module.exports = function (ts) {
103
106
} ;
104
107
105
108
Host . prototype . writeFile = function ( filename , data ) {
106
- var normalized = this . _normalizedRelative ( filename ) ;
107
- log ( 'Cache write %s (norm: %s)' , filename , normalized ) ;
108
- this . output [ normalized ] = data ;
109
+
110
+ var outputCanonical = this . _canonical ( filename ) ;
111
+ log ( 'Cache write %s' , outputCanonical ) ;
112
+ this . output [ outputCanonical ] = data ;
113
+
114
+ var sourceCanonical = this . _inferSourceCanonical ( outputCanonical ) ;
115
+ var sourceFollowed = this . _follow ( path . dirname ( sourceCanonical ) ) + '/' + path . basename ( sourceCanonical ) ;
116
+
117
+ if ( sourceFollowed !== sourceCanonical ) {
118
+ outputCanonical = this . _inferOutputCanonical ( sourceFollowed ) ;
119
+ log ( 'Cache write (followed) %s' , outputCanonical ) ;
120
+ this . output [ outputCanonical ] = data ;
121
+ }
109
122
} ;
110
123
111
124
Host . prototype . getCurrentDirectory = function ( ) {
112
125
return this . currentDirectory ;
113
126
} ;
114
127
115
128
Host . prototype . getCanonicalFileName = function ( filename ) {
116
- return this . _normalizedRelative ( filename ) ;
129
+ return ts . normalizeSlashes ( ts . sys . useCaseSensitiveFileNames ? filename : filename . toLowerCase ( ) ) ;
117
130
} ;
118
131
119
132
Host . prototype . useCaseSensitiveFileNames = function ( ) {
@@ -130,8 +143,7 @@ module.exports = function (ts) {
130
143
} ;
131
144
132
145
Host . prototype . readFile = function ( filename ) {
133
- var normalized = this . _normalizedRelative ( filename ) ;
134
- return ts . sys . readFile ( normalized ) ;
146
+ return ts . sys . readFile ( filename ) ;
135
147
} ;
136
148
137
149
Host . prototype . _rootDir = function ( ) {
@@ -140,10 +152,91 @@ module.exports = function (ts) {
140
152
if ( ! Object . hasOwnProperty . call ( this . files , filename ) ) continue ;
141
153
if ( / \. d \. t s $ / . test ( filename ) ) continue ;
142
154
143
- dirs . push ( path . dirname ( filename ) ) ;
155
+ dirs . push ( this . getCanonicalFileName ( path . dirname ( filename ) ) ) ;
144
156
}
145
157
var result = commondir ( this . currentDirectory , dirs ) ;
146
- return result ;
158
+ return this . getCanonicalFileName ( result ) ;
159
+ } ;
160
+
161
+ Host . prototype . _rootFilenames = function ( ) {
162
+
163
+ var rootFilenames = [ ] ;
164
+
165
+ for ( var filename in this . files ) {
166
+ if ( ! Object . hasOwnProperty . call ( this . files , filename ) ) continue ;
167
+ if ( ! this . files [ filename ] . root ) continue ;
168
+ rootFilenames . push ( filename ) ;
169
+ }
170
+ return rootFilenames ;
171
+ }
172
+
173
+ Host . prototype . _output = function ( filename ) {
174
+
175
+ var outputCanonical = this . _inferOutputCanonical ( filename ) ;
176
+ log ( 'Cache read %s' , outputCanonical ) ;
177
+
178
+ var output = this . output [ outputCanonical ] ;
179
+ if ( ! output ) {
180
+ log ( 'Cache miss on %s' , outputCanonical ) ;
181
+ }
182
+ return output ;
183
+ }
184
+
185
+ Host . prototype . _canonical = function ( filename ) {
186
+ return this . getCanonicalFileName ( path . resolve (
187
+ this . currentDirectory ,
188
+ filename
189
+ ) ) ;
190
+ }
191
+
192
+ Host . prototype . _inferOutputCanonical = function ( filename ) {
193
+
194
+ var sourceCanonical = this . _canonical ( filename ) ;
195
+ var outputRelative = path . relative (
196
+ this . _rootDir ( ) ,
197
+ sourceCanonical
198
+ ) ;
199
+ var outputCanonical = this . getCanonicalFileName ( path . resolve (
200
+ this . outputDirectory ,
201
+ outputRelative
202
+ ) ) ;
203
+ return outputCanonical ;
204
+ }
205
+
206
+ Host . prototype . _inferSourceCanonical = function ( filename ) {
207
+
208
+ var outputCanonical = this . _canonical ( filename ) ;
209
+ var outputRelative = path . relative (
210
+ this . outputDirectory ,
211
+ outputCanonical
212
+ ) ;
213
+ var sourceCanonical = this . getCanonicalFileName ( path . resolve (
214
+ this . _rootDir ( ) ,
215
+ outputRelative
216
+ ) ) ;
217
+ return sourceCanonical ;
218
+ }
219
+
220
+ Host . prototype . _follow = function ( filename ) {
221
+
222
+ filename = this . _canonical ( filename ) ;
223
+ var basename ;
224
+ var parts = [ ] ;
225
+
226
+ do {
227
+ var stats = fs . lstatSync ( filename ) ;
228
+ if ( stats . isSymbolicLink ( ) ) {
229
+ filename = realpath . realpathSync ( filename ) ;
230
+ } else {
231
+ basename = path . basename ( filename ) ;
232
+ if ( basename ) {
233
+ parts . unshift ( basename ) ;
234
+ filename = path . dirname ( filename ) ;
235
+ }
236
+ }
237
+ } while ( basename ) ;
238
+
239
+ return ts . normalizeSlashes ( filename + parts . join ( '/' ) ) ;
147
240
} ;
148
241
149
242
return Host ;
0 commit comments