@@ -13,35 +13,47 @@ namespace ts.JsTyping {
1313 readDirectory : ( path : string , extension ?: string , exclude ?: string [ ] , depth ?: number ) => string [ ] ;
1414 } ;
1515
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+
1638 // A map of loose file names to library names
1739 // that we are confident require typings
1840 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- }
3141
3242 /**
3343 * @param host is the object providing I/O related operations.
3444 * @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
3748 * @param typingOptions are used for customizing the typing inference process.
3849 * @param compilerOptions are used as a source of typing inference.
3950 */
4051 export function discoverTypings (
4152 host : TypingResolutionHost ,
4253 fileNames : string [ ] ,
43- globalCachePath : Path ,
54+ cachePath : Path ,
4455 projectRootPath : Path ,
56+ safeListPath : Path ,
4557 typingOptions : TypingOptions ,
4658 compilerOptions : CompilerOptions ) :
4759 { cachedTypingPaths : string [ ] , newTypingNames : string [ ] , filesToWatch : string [ ] } {
@@ -53,13 +65,12 @@ namespace ts.JsTyping {
5365 return { cachedTypingPaths : [ ] , newTypingNames : [ ] , filesToWatch : [ ] } ;
5466 }
5567
56- const cachePath = projectRootPath || globalCachePath ;
5768 // Only infer typings for .js and .jsx files
5869 fileNames = filter ( map ( fileNames , normalizePath ) , f => scriptKindIs ( f , /*LanguageServiceHost*/ undefined , ScriptKind . JS , ScriptKind . JSX ) ) ;
5970
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 ; }
6374 }
6475
6576 const filesToWatch : string [ ] = [ ] ;
@@ -86,26 +97,20 @@ namespace ts.JsTyping {
8697 const nodeModulesPath = combinePaths ( searchDir , "node_modules" ) ;
8798 getTypingNamesFromNodeModuleFolder ( nodeModulesPath , filesToWatch ) ;
8899 }
89-
90100 getTypingNamesFromSourceFileNames ( fileNames ) ;
91- getTypingNamesFromCompilerOptions ( compilerOptions ) ;
92101 }
93102
94103 const typingsPath = combinePaths ( cachePath , "typings" ) ;
95104 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 ;
103108
104109 // The "installed" property in the tsd.json serves as a registry of installed typings. Each item
105110 // of this object has a key of the relative file path, and a value that contains the corresponding
106111 // 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 ) {
109114 // Assuming the cachedTypingPath has the format of "[package name]/[file name]"
110115 const cachedTypingName = cachedTypingPath . substr ( 0 , cachedTypingPath . indexOf ( "/" ) ) ;
111116 // If the inferred[cachedTypingName] is already not null, which means we found a corresponding
@@ -153,20 +158,21 @@ namespace ts.JsTyping {
153158 * Get the typing info from common package manager json files like package.json or bower.json
154159 */
155160 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 ;
158164 filesToWatch . push ( jsonPath ) ;
159- if ( hasProperty ( jsonDict , " dependencies" ) ) {
160- mergeTypings ( getKeys ( jsonDict . dependencies ) ) ;
165+ if ( jsonConfig . dependencies ) {
166+ mergeTypings ( getKeys ( jsonConfig . dependencies ) ) ;
161167 }
162- if ( hasProperty ( jsonDict , " devDependencies" ) ) {
163- mergeTypings ( getKeys ( jsonDict . devDependencies ) ) ;
168+ if ( jsonConfig . devDependencies ) {
169+ mergeTypings ( getKeys ( jsonConfig . devDependencies ) ) ;
164170 }
165- if ( hasProperty ( jsonDict , " optionalDependencies" ) ) {
166- mergeTypings ( getKeys ( jsonDict . optionalDependencies ) ) ;
171+ if ( jsonConfig . optionalDependencies ) {
172+ mergeTypings ( getKeys ( jsonConfig . optionalDependencies ) ) ;
167173 }
168- if ( hasProperty ( jsonDict , " peerDependencies" ) ) {
169- mergeTypings ( getKeys ( jsonDict . peerDependencies ) ) ;
174+ if ( jsonConfig . peerDependencies ) {
175+ mergeTypings ( getKeys ( jsonConfig . peerDependencies ) ) ;
170176 }
171177 }
172178 }
@@ -205,75 +211,36 @@ namespace ts.JsTyping {
205211 }
206212
207213 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
217224 // we should include all the top level module names for npm 2, and only module names whose
218225 // "_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 ) {
221228 continue ;
222229 }
223230
224231 // If the package has its own d.ts typings, those will take precedence. Otherwise the package name will be used
225232 // 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 ;
230237 }
231238 else {
232- typingNames . push ( packageName ) ;
239+ typingNames . push ( packageJson . name ) ;
233240 }
234241 }
235242 mergeTypings ( typingNames ) ;
236243 }
237244
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 ;
278245 }
279246}
0 commit comments