diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 561419d367e..fad2db307aa 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3,6 +3,7 @@ /* @internal */ namespace ts { export const emptyArray: never[] = [] as never[]; + export const emptyMap: ReadonlyMap = createMap(); export const externalHelpersModuleNameText = "tslib"; diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index 5f6b1350578..a0a770ad093 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -1027,6 +1027,8 @@ namespace ts.projectSystem { }); describe("discover typings", () => { + const emptySafeList = emptyMap; + it("should use mappings from safe list", () => { const app = { path: "/a/b/app.js", @@ -1040,11 +1042,12 @@ namespace ts.projectSystem { path: "/a/b/chroma.min.js", content: "" }; - const cache = createMap(); + + const safeList = createMapFromTemplate({ jquery: "jquery", chroma: "chroma-js" }); const host = createServerHost([app, jquery, chroma]); const logger = trackingLogger(); - const result = JsTyping.discoverTypings(host, logger.log, [app.path, jquery.path, chroma.path], getDirectoryPath(app.path), /*safeListPath*/ undefined, cache, { enable: true }, []); + 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"]', 'Result: {"cachedTypingPaths":[],"newTypingNames":["jquery","chroma-js"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}', @@ -1062,7 +1065,7 @@ namespace ts.projectSystem { for (const name of JsTyping.nodeCoreModuleList) { const logger = trackingLogger(); - const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(f.path), /*safeListPath*/ undefined, cache, { enable: true }, [name, "somename"]); + const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(f.path), emptySafeList, cache, { enable: true }, [name, "somename"]); assert.deepEqual(logger.finish(), [ 'Inferred typings from unresolved imports: ["node","somename"]', 'Result: {"cachedTypingPaths":[],"newTypingNames":["node","somename"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}', @@ -1083,7 +1086,7 @@ namespace ts.projectSystem { const host = createServerHost([f, node]); const cache = createMapFromTemplate({ "node": node.path }); const logger = trackingLogger(); - const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(f.path), /*safeListPath*/ undefined, cache, { enable: true }, ["fs", "bar"]); + const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(f.path), emptySafeList, cache, { enable: true }, ["fs", "bar"]); assert.deepEqual(logger.finish(), [ 'Inferred typings from unresolved imports: ["node","bar"]', 'Result: {"cachedTypingPaths":["/a/b/node.d.ts"],"newTypingNames":["bar"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}', @@ -1108,7 +1111,7 @@ namespace ts.projectSystem { const host = createServerHost([app, a, b]); const cache = createMap(); const logger = trackingLogger(); - const result = JsTyping.discoverTypings(host, logger.log, [app.path], getDirectoryPath(app.path), /*safeListPath*/ undefined, cache, { enable: true }, /*unresolvedImports*/ []); + 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"]', 'Result: {"cachedTypingPaths":[],"newTypingNames":["a"],"filesToWatch":["/bower_components","/node_modules"]}', diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index b1fddfb4129..8a3840fd984 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -85,6 +85,7 @@ namespace ts.server.typingsInstaller { private readonly missingTypingsSet: Map = createMap(); private readonly knownCachesSet: Map = createMap(); private readonly projectWatchers: Map = createMap(); + private safeList: JsTyping.SafeList | undefined; readonly pendingRunRequests: PendingRequest[] = []; private installRunCount = 1; @@ -143,12 +144,15 @@ namespace ts.server.typingsInstaller { this.processCacheLocation(req.cachePath); } + if (this.safeList === undefined) { + this.safeList = JsTyping.loadSafeList(this.installTypingHost, this.safeListPath); + } const discoverTypingsResult = JsTyping.discoverTypings( this.installTypingHost, this.log.isEnabled() ? this.log.writeLine : undefined, req.fileNames, req.projectRootPath, - this.safeListPath, + this.safeList, this.packageNameToTypingLocation, req.typeAcquisition, req.unresolvedImports); diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 670633eb44b..84f2ef5ba9a 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -25,10 +25,6 @@ namespace ts.JsTyping { typings?: string; } - // A map of loose file names to library names - // that we are confident require typings - let safeList: Map; - /* @internal */ export const nodeCoreModuleList: ReadonlyArray = [ "buffer", "querystring", "events", "http", "cluster", @@ -40,6 +36,16 @@ namespace ts.JsTyping { const nodeCoreModules = arrayToMap(nodeCoreModuleList, x => x); + /** + * A map of loose file names to library names that we are confident require typings + */ + export type SafeList = ReadonlyMap; + + export function loadSafeList(host: TypingResolutionHost, safeListPath: Path): SafeList { + const result = readConfigFile(safeListPath, path => host.readFile(path)); + return createMapFromTemplate(result.config); + } + /** * @param host is the object providing I/O related operations. * @param fileNames are the file names that belong to the same project @@ -54,8 +60,8 @@ namespace ts.JsTyping { log: ((message: string) => void) | undefined, fileNames: string[], projectRootPath: Path, - safeListPath: Path, - packageNameToTypingLocation: Map, + safeList: SafeList, + packageNameToTypingLocation: ReadonlyMap, typeAcquisition: TypeAcquisition, unresolvedImports: ReadonlyArray): { cachedTypingPaths: string[], newTypingNames: string[], filesToWatch: string[] } { @@ -75,11 +81,6 @@ namespace ts.JsTyping { } }); - if (!safeList) { - const result = readConfigFile(safeListPath, (path: string) => host.readFile(path)); - safeList = createMapFromTemplate(result.config); - } - const filesToWatch: string[] = []; forEach(typeAcquisition.include, addInferredTyping); diff --git a/src/services/shims.ts b/src/services/shims.ts index e86e9053e57..b7c85d2ce82 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -1005,6 +1005,7 @@ namespace ts { class CoreServicesShimObject extends ShimBase implements CoreServicesShim { private logPerformance = false; + private safeList: JsTyping.SafeList | undefined; constructor(factory: ShimFactory, public readonly logger: Logger, private readonly host: CoreServicesShimHostAdapter) { super(factory); @@ -1114,12 +1115,15 @@ namespace ts { const getCanonicalFileName = createGetCanonicalFileName(/*useCaseSensitivefileNames:*/ false); return this.forwardJSONCall("discoverTypings()", () => { const info = JSON.parse(discoverTypingsJson); - return ts.JsTyping.discoverTypings( + if (this.safeList === undefined) { + this.safeList = JsTyping.loadSafeList(this.host, toPath(info.safeListPath, info.safeListPath, getCanonicalFileName)); + } + return JsTyping.discoverTypings( this.host, msg => this.logger.log(msg), info.fileNames, toPath(info.projectRootPath, info.projectRootPath, getCanonicalFileName), - toPath(info.safeListPath, info.safeListPath, getCanonicalFileName), + this.safeList, info.packageNameToTypingLocation, info.typeAcquisition, info.unresolvedImports);