diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index a0a770ad093..92ecd5e31b8 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -1050,6 +1050,7 @@ namespace ts.projectSystem { const result = JsTyping.discoverTypings(host, logger.log, [app.path, jquery.path, chroma.path], getDirectoryPath(app.path), safeList, emptyMap, { enable: true }, emptyArray); assert.deepEqual(logger.finish(), [ 'Inferred typings from file names: ["jquery","chroma-js"]', + "Inferred typings from unresolved imports: []", 'Result: {"cachedTypingPaths":[],"newTypingNames":["jquery","chroma-js"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}', ]); assert.deepEqual(result.newTypingNames, ["jquery", "chroma-js"]); @@ -1114,6 +1115,8 @@ namespace ts.projectSystem { const result = JsTyping.discoverTypings(host, logger.log, [app.path], getDirectoryPath(app.path), emptySafeList, cache, { enable: true }, /*unresolvedImports*/ []); assert.deepEqual(logger.finish(), [ 'Searching for typing names in /node_modules; all files: ["/node_modules/a/package.json"]', + ' Found package names: ["a"]', + "Inferred typings from unresolved imports: []", 'Result: {"cachedTypingPaths":[],"newTypingNames":["a"],"filesToWatch":["/bower_components","/node_modules"]}', ]); assert.deepEqual(result, { diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 84f2ef5ba9a..4de7bb3191d 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -22,6 +22,7 @@ namespace ts.JsTyping { name?: string; optionalDependencies?: MapLike; peerDependencies?: MapLike; + types?: string; typings?: string; } @@ -83,14 +84,11 @@ namespace ts.JsTyping { const filesToWatch: string[] = []; - forEach(typeAcquisition.include, addInferredTyping); + if (typeAcquisition.include) addInferredTypings(typeAcquisition.include, "Explicitly included types"); const exclude = typeAcquisition.exclude || []; // Directories to search for package.json, bower.json and other typing information - const possibleSearchDirs = createMap(); - for (const f of fileNames) { - possibleSearchDirs.set(getDirectoryPath(f), true); - } + const possibleSearchDirs = arrayToSet(fileNames, getDirectoryPath); possibleSearchDirs.set(projectRootPath, true); possibleSearchDirs.forEach((_true, searchDir) => { const packageJsonPath = combinePaths(searchDir, "package.json"); @@ -109,13 +107,8 @@ namespace ts.JsTyping { // add typings for unresolved imports if (unresolvedImports) { - const x = unresolvedImports.map(moduleId => nodeCoreModules.has(moduleId) ? "node" : moduleId); - if (x.length && log) log(`Inferred typings from unresolved imports: ${JSON.stringify(x)}`); - for (const typingName of x) { - if (!inferredTypings.has(typingName)) { - inferredTypings.set(typingName, undefined); - } - } + const module = deduplicate(unresolvedImports.map(moduleId => nodeCoreModules.has(moduleId) ? "node" : moduleId)); + addInferredTypings(module, "Inferred typings from unresolved imports"); } // Add the cached typing locations for inferred typings that are already installed packageNameToTypingLocation.forEach((typingLocation, name) => { @@ -126,7 +119,8 @@ namespace ts.JsTyping { // Remove typings that the user has added to the exclude list for (const excludeTypingName of exclude) { - inferredTypings.delete(excludeTypingName); + const didDelete = inferredTypings.delete(excludeTypingName); + if (didDelete && log) log(`Typing for ${excludeTypingName} is in exclude list, will be ignored.`); } const newTypingNames: string[] = []; @@ -148,6 +142,10 @@ namespace ts.JsTyping { inferredTypings.set(typingName, undefined); } } + function addInferredTypings(typingNames: ReadonlyArray, message: string) { + if (log) log(`${message}: ${JSON.stringify(typingNames)}`); + forEach(typingNames, addInferredTyping); + } /** * Get the typing info from common package manager json files like package.json or bower.json @@ -158,20 +156,9 @@ namespace ts.JsTyping { } filesToWatch.push(jsonPath); - if (log) log(`Searching for typing names in '${jsonPath}' dependencies`); - const jsonConfig: PackageJson = readConfigFile(jsonPath, (path: string) => host.readFile(path)).config; - addInferredTypingsFromKeys(jsonConfig.dependencies); - addInferredTypingsFromKeys(jsonConfig.devDependencies); - addInferredTypingsFromKeys(jsonConfig.optionalDependencies); - addInferredTypingsFromKeys(jsonConfig.peerDependencies); - - function addInferredTypingsFromKeys(map: MapLike | undefined): void { - for (const key in map) { - if (ts.hasProperty(map, key)) { - addInferredTyping(key); - } - } - } + const jsonConfig: PackageJson = readConfigFile(jsonPath, path => host.readFile(path)).config; + const jsonTypingNames = flatMap([jsonConfig.dependencies, jsonConfig.devDependencies, jsonConfig.optionalDependencies, jsonConfig.peerDependencies], getOwnKeys); + addInferredTypings(jsonTypingNames, `Typing names in '${jsonPath}' dependencies`); } /** @@ -189,10 +176,7 @@ namespace ts.JsTyping { return safeList.get(cleanedTypingName); }); if (fromFileNames.length) { - if (log) log(`Inferred typings from file names: ${JSON.stringify(fromFileNames)}`); - for (const safe of fromFileNames) { - addInferredTyping(safe); - } + addInferredTypings(fromFileNames, "Inferred typings from file names"); } const hasJsxFile = some(fileNames, f => fileExtensionIs(f, Extension.Jsx)); @@ -217,6 +201,7 @@ namespace ts.JsTyping { // depth of 2, so we access `node_modules/foo` but not `node_modules/foo/bar` const fileNames = host.readDirectory(packagesFolderPath, [".json"], /*excludes*/ undefined, /*includes*/ undefined, /*depth*/ 2); if (log) log(`Searching for typing names in ${packagesFolderPath}; all files: ${JSON.stringify(fileNames)}`); + const packageNames: string[] = []; for (const fileName of fileNames) { const normalizedFileName = normalizePath(fileName); const baseFileName = getBaseFileName(normalizedFileName); @@ -239,14 +224,17 @@ namespace ts.JsTyping { if (!packageJson.name) { continue; } - if (packageJson.typings) { - const absolutePath = getNormalizedAbsolutePath(packageJson.typings, getDirectoryPath(normalizedFileName)); + const ownTypes = packageJson.types || packageJson.typings; + if (ownTypes) { + const absolutePath = getNormalizedAbsolutePath(ownTypes, getDirectoryPath(normalizedFileName)); + if (log) log(` Package '${packageJson.name}' provides its own types.`); inferredTypings.set(packageJson.name, absolutePath); } else { - addInferredTyping(packageJson.name); + packageNames.push(packageJson.name); } } + addInferredTypings(packageNames, " Found package names"); } }