@@ -13,35 +13,47 @@ namespace ts.JsTyping {
13
13
readDirectory : ( path : string , extension ?: string , exclude ?: string [ ] , depth ?: number ) => string [ ] ;
14
14
} ;
15
15
16
+ interface TsdJson {
17
+ version : string ;
18
+ repo : string ;
19
+ ref : string ;
20
+ path : string ;
21
+ installed ?: Map < TsdInstalledItem > ;
22
+ } ;
23
+
24
+ interface TsdInstalledItem {
25
+ commit : string ;
26
+ } ;
27
+
28
+ interface PackageJson {
29
+ _requiredBy ?: string [ ] ;
30
+ dependencies ?: Map < string > ;
31
+ devDependencies ?: Map < string > ;
32
+ name : string ;
33
+ optionalDependencies ?: Map < string > ;
34
+ peerDependencies ?: Map < string > ;
35
+ typings ?: string ;
36
+ } ;
37
+
16
38
// A map of loose file names to library names
17
39
// that we are confident require typings
18
40
let safeList : Map < string > ;
19
- const notFoundTypingNames : string [ ] = [ ] ;
20
-
21
- function tryParseJson ( jsonPath : string , host : TypingResolutionHost ) : any {
22
- if ( host . fileExists ( jsonPath ) ) {
23
- try {
24
- const contents = removeComments ( host . readFile ( jsonPath ) ) ;
25
- return JSON . parse ( contents ) ;
26
- }
27
- catch ( e ) { }
28
- }
29
- return undefined ;
30
- }
31
41
32
42
/**
33
43
* @param host is the object providing I/O related operations.
34
44
* @param fileNames are the file names that belong to the same project.
35
- * @param globalCachePath is used to get the safe list file path and as cache path if the project root path isn't specified.
36
- * @param projectRootPath is the path to the project root directory. This is used for the local typings cache.
45
+ * @param cachePath is the path to the typings cache
46
+ * @param projectRootPath is the path to the project root directory
47
+ * @param safeListPath is the path used to retrieve the safe list
37
48
* @param typingOptions are used for customizing the typing inference process.
38
49
* @param compilerOptions are used as a source of typing inference.
39
50
*/
40
51
export function discoverTypings (
41
52
host : TypingResolutionHost ,
42
53
fileNames : string [ ] ,
43
- globalCachePath : Path ,
54
+ cachePath : Path ,
44
55
projectRootPath : Path ,
56
+ safeListPath : Path ,
45
57
typingOptions : TypingOptions ,
46
58
compilerOptions : CompilerOptions ) :
47
59
{ cachedTypingPaths : string [ ] , newTypingNames : string [ ] , filesToWatch : string [ ] } {
@@ -53,13 +65,12 @@ namespace ts.JsTyping {
53
65
return { cachedTypingPaths : [ ] , newTypingNames : [ ] , filesToWatch : [ ] } ;
54
66
}
55
67
56
- const cachePath = projectRootPath || globalCachePath ;
57
68
// Only infer typings for .js and .jsx files
58
69
fileNames = filter ( map ( fileNames , normalizePath ) , f => scriptKindIs ( f , /*LanguageServiceHost*/ undefined , ScriptKind . JS , ScriptKind . JSX ) ) ;
59
70
60
- const safeListFilePath = combinePaths ( globalCachePath , " safeList.json" ) ;
61
- if ( ! safeList && host . fileExists ( safeListFilePath ) ) {
62
- safeList = tryParseJson ( safeListFilePath , host ) ;
71
+ if ( ! safeList ) {
72
+ const result = readConfigFile ( safeListPath , host . readFile ) ;
73
+ if ( result . config ) { safeList = result . config ; }
63
74
}
64
75
65
76
const filesToWatch : string [ ] = [ ] ;
@@ -86,26 +97,20 @@ namespace ts.JsTyping {
86
97
const nodeModulesPath = combinePaths ( searchDir , "node_modules" ) ;
87
98
getTypingNamesFromNodeModuleFolder ( nodeModulesPath , filesToWatch ) ;
88
99
}
89
-
90
100
getTypingNamesFromSourceFileNames ( fileNames ) ;
91
- getTypingNamesFromCompilerOptions ( compilerOptions ) ;
92
101
}
93
102
94
103
const typingsPath = combinePaths ( cachePath , "typings" ) ;
95
104
const tsdJsonPath = combinePaths ( cachePath , "tsd.json" ) ;
96
- const tsdJsonDict = tryParseJson ( tsdJsonPath , host ) ;
97
- if ( tsdJsonDict ) {
98
- for ( const notFoundTypingName of notFoundTypingNames ) {
99
- if ( hasProperty ( inferredTypings , notFoundTypingName ) && ! inferredTypings [ notFoundTypingName ] ) {
100
- delete inferredTypings [ notFoundTypingName ] ;
101
- }
102
- }
105
+ const result = readConfigFile ( tsdJsonPath , host . readFile ) ;
106
+ if ( result . config ) {
107
+ const tsdJson : TsdJson = result . config ;
103
108
104
109
// The "installed" property in the tsd.json serves as a registry of installed typings. Each item
105
110
// of this object has a key of the relative file path, and a value that contains the corresponding
106
111
// commit hash.
107
- if ( hasProperty ( tsdJsonDict , " installed" ) ) {
108
- for ( const cachedTypingPath in tsdJsonDict . installed ) {
112
+ if ( tsdJson . installed ) {
113
+ for ( const cachedTypingPath in tsdJson . installed ) {
109
114
// Assuming the cachedTypingPath has the format of "[package name]/[file name]"
110
115
const cachedTypingName = cachedTypingPath . substr ( 0 , cachedTypingPath . indexOf ( "/" ) ) ;
111
116
// If the inferred[cachedTypingName] is already not null, which means we found a corresponding
@@ -153,20 +158,21 @@ namespace ts.JsTyping {
153
158
* Get the typing info from common package manager json files like package.json or bower.json
154
159
*/
155
160
function getTypingNamesFromJson ( jsonPath : string , filesToWatch : string [ ] ) {
156
- const jsonDict = tryParseJson ( jsonPath , host ) ;
157
- if ( jsonDict ) {
161
+ const result = readConfigFile ( jsonPath , host . readFile ) ;
162
+ if ( result . config ) {
163
+ const jsonConfig : PackageJson = result . config ;
158
164
filesToWatch . push ( jsonPath ) ;
159
- if ( hasProperty ( jsonDict , " dependencies" ) ) {
160
- mergeTypings ( getKeys ( jsonDict . dependencies ) ) ;
165
+ if ( jsonConfig . dependencies ) {
166
+ mergeTypings ( getKeys ( jsonConfig . dependencies ) ) ;
161
167
}
162
- if ( hasProperty ( jsonDict , " devDependencies" ) ) {
163
- mergeTypings ( getKeys ( jsonDict . devDependencies ) ) ;
168
+ if ( jsonConfig . devDependencies ) {
169
+ mergeTypings ( getKeys ( jsonConfig . devDependencies ) ) ;
164
170
}
165
- if ( hasProperty ( jsonDict , " optionalDependencies" ) ) {
166
- mergeTypings ( getKeys ( jsonDict . optionalDependencies ) ) ;
171
+ if ( jsonConfig . optionalDependencies ) {
172
+ mergeTypings ( getKeys ( jsonConfig . optionalDependencies ) ) ;
167
173
}
168
- if ( hasProperty ( jsonDict , " peerDependencies" ) ) {
169
- mergeTypings ( getKeys ( jsonDict . peerDependencies ) ) ;
174
+ if ( jsonConfig . peerDependencies ) {
175
+ mergeTypings ( getKeys ( jsonConfig . peerDependencies ) ) ;
170
176
}
171
177
}
172
178
}
@@ -205,75 +211,36 @@ namespace ts.JsTyping {
205
211
}
206
212
207
213
const typingNames : string [ ] = [ ] ;
208
- const jsonFiles = host . readDirectory ( nodeModulesPath , "*.json" , /*exclude*/ undefined , /*depth*/ 2 ) ;
209
- for ( const jsonFile of jsonFiles ) {
210
- if ( getBaseFileName ( jsonFile ) !== "package.json" ) { continue ; }
211
- const packageJsonDict = tryParseJson ( jsonFile , host ) ;
212
- if ( ! packageJsonDict ) { continue ; }
213
-
214
- filesToWatch . push ( jsonFile ) ;
215
-
216
- // npm 3 has the package.json contains a "_requiredBy" field
214
+ const fileNames = host . readDirectory ( nodeModulesPath , "*.json" , /*exclude*/ undefined , /*depth*/ 2 ) ;
215
+ for ( const fileName of fileNames ) {
216
+ const normalizedFileName = normalizePath ( fileName ) ;
217
+ if ( getBaseFileName ( normalizedFileName ) !== "package.json" ) { continue ; }
218
+ const result = readConfigFile ( normalizedFileName , host . readFile ) ;
219
+ if ( ! result . config ) { continue ; }
220
+ const packageJson : PackageJson = result . config ;
221
+ filesToWatch . push ( normalizedFileName ) ;
222
+
223
+ // npm 3's package.json contains a "_requiredBy" field
217
224
// we should include all the top level module names for npm 2, and only module names whose
218
225
// "_requiredBy" field starts with "#" or equals "/" for npm 3.
219
- if ( packageJsonDict . _requiredBy &&
220
- filter ( packageJsonDict . _requiredBy , ( r : string ) => r [ 0 ] === "#" || r === "/" ) . length === 0 ) {
226
+ if ( packageJson . _requiredBy &&
227
+ filter ( packageJson . _requiredBy , ( r : string ) => r [ 0 ] === "#" || r === "/" ) . length === 0 ) {
221
228
continue ;
222
229
}
223
230
224
231
// If the package has its own d.ts typings, those will take precedence. Otherwise the package name will be used
225
232
// to download d.ts files from DefinitelyTyped
226
- const packageName = packageJsonDict [ "name" ] ;
227
- if ( hasProperty ( packageJsonDict , " typings" ) ) {
228
- const absolutePath = getNormalizedAbsolutePath ( packageJsonDict . typings , getDirectoryPath ( jsonFile ) ) ;
229
- inferredTypings [ packageName ] = absolutePath ;
233
+ if ( ! packageJson . name ) { continue ; }
234
+ if ( packageJson . typings ) {
235
+ const absolutePath = getNormalizedAbsolutePath ( packageJson . typings , getDirectoryPath ( normalizedFileName ) ) ;
236
+ inferredTypings [ packageJson . name ] = absolutePath ;
230
237
}
231
238
else {
232
- typingNames . push ( packageName ) ;
239
+ typingNames . push ( packageJson . name ) ;
233
240
}
234
241
}
235
242
mergeTypings ( typingNames ) ;
236
243
}
237
244
238
- function getTypingNamesFromCompilerOptions ( options : CompilerOptions ) {
239
- const typingNames : string [ ] = [ ] ;
240
- if ( ! options ) {
241
- return ;
242
- }
243
- mergeTypings ( typingNames ) ;
244
- }
245
- }
246
-
247
- /**
248
- * Keep a list of typings names that we know cannot be obtained at the moment (could be because
249
- * of network issues or because the package doesn't hava a d.ts file in DefinitelyTyped), so
250
- * that we won't try again next time within this session.
251
- * @param newTypingNames The list of new typings that the host attempted to acquire
252
- * @param cachePath The path to the tsd.json cache
253
- * @param host The object providing I/O related operations.
254
- */
255
- export function updateNotFoundTypingNames ( newTypingNames : string [ ] , cachePath : string , host : TypingResolutionHost ) : void {
256
- const tsdJsonPath = combinePaths ( cachePath , "tsd.json" ) ;
257
- const cacheTsdJsonDict = tryParseJson ( tsdJsonPath , host ) ;
258
- if ( cacheTsdJsonDict ) {
259
- const installedTypingFiles = hasProperty ( cacheTsdJsonDict , "installed" )
260
- ? getKeys ( cacheTsdJsonDict . installed )
261
- : [ ] ;
262
- const newMissingTypingNames =
263
- filter ( newTypingNames , name => notFoundTypingNames . indexOf ( name ) < 0 && ! isInstalled ( name , installedTypingFiles ) ) ;
264
- for ( const newMissingTypingName of newMissingTypingNames ) {
265
- notFoundTypingNames . push ( newMissingTypingName ) ;
266
- }
267
- }
268
- }
269
-
270
- function isInstalled ( typing : string , installedKeys : string [ ] ) {
271
- const typingPrefix = typing + "/" ;
272
- for ( const key of installedKeys ) {
273
- if ( key . indexOf ( typingPrefix ) === 0 ) {
274
- return true ;
275
- }
276
- }
277
- return false ;
278
245
}
279
246
}
0 commit comments