Skip to content

Commit 0d735e5

Browse files
Use caches for module resolution and type reference directives when using compiler default functions (#1287)
* ClearCache for watchHost * Use module resolution cache * Use complete ResolvedModuleFull from typescript * Use type reference directive cache so that results are atomic like module resolution till watch is invoked * Some updates * Test baseline update * Update src/servicesHost.ts * Update src/servicesHost.ts * Update CHANGELOG.md * Update package.json * Update src/interfaces.ts Co-authored-by: John Reilly <[email protected]>
1 parent f6b9118 commit 0d735e5

File tree

7 files changed

+206
-51
lines changed

7 files changed

+206
-51
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## v9.1.0
4+
5+
* [Use caches for module resolution and type reference directives when using compiler default functions](https://github.com/TypeStrong/ts-loader/pull/1287) - thanks @sheetalkamat - uses: https://github.com/microsoft/TypeScript/pull/43700
6+
37
## v9.0.2
48

59
* [Remove usage of loader-utils](https://github.com/TypeStrong/ts-loader/pull/1288) - thanks @jonwallsten

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ts-loader",
3-
"version": "9.0.2",
3+
"version": "9.1.0",
44
"description": "TypeScript loader for webpack",
55
"main": "index.js",
66
"types": "dist",

src/interfaces.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ export interface ServiceHostWhichMayBeCacheable
6868
HostMayBeCacheable {}
6969

7070
export interface WatchHost
71-
extends typescript.WatchCompilerHostOfFilesAndCompilerOptions<typescript.EmitAndSemanticDiagnosticsBuilderProgram> {
71+
extends typescript.WatchCompilerHostOfFilesAndCompilerOptions<typescript.EmitAndSemanticDiagnosticsBuilderProgram>,
72+
HostMayBeCacheable {
7273
invokeFileWatcher: WatchFactory['invokeFileWatcher'];
7374
updateRootFileNames(): void;
7475
outputFiles: Map<FilePathKey, typescript.OutputFile[]>;
@@ -139,13 +140,56 @@ export interface ConfigFileInfo {
139140
dtsFiles?: string[];
140141
}
141142

143+
interface CacheWithRedirects<T> {
144+
ownMap: Map<string, T>;
145+
redirectsMap: Map<typescript.Path, Map<string, T>>;
146+
getOrCreateMapOfCacheRedirects(
147+
redirectedReference: typescript.ResolvedProjectReference | undefined
148+
): Map<string, T>;
149+
clear(): void;
150+
setOwnOptions(newOptions: typescript.CompilerOptions): void;
151+
setOwnMap(newOwnMap: Map<string, T>): void;
152+
}
153+
interface PerModuleNameCache {
154+
get(
155+
directory: string
156+
): typescript.ResolvedModuleWithFailedLookupLocations | undefined;
157+
set(
158+
directory: string,
159+
result: typescript.ResolvedModuleWithFailedLookupLocations
160+
): void;
161+
}
162+
export interface ModuleResolutionCache
163+
extends typescript.ModuleResolutionCache {
164+
directoryToModuleNameMap: CacheWithRedirects<
165+
Map<string, typescript.ResolvedModuleWithFailedLookupLocations>
166+
>;
167+
moduleNameToDirectoryMap: CacheWithRedirects<PerModuleNameCache>;
168+
clear(): void;
169+
update(compilerOptions: typescript.CompilerOptions): void;
170+
getPackageJsonInfoCache?(): any;
171+
}
172+
// Until the API has been released and ts-loader is built against a version of TypeScript that contains it - see https://github.com/microsoft/TypeScript/blob/74993a2a64bb2e423b40204bb54ff749cdd4ef54/src/compiler/moduleNameResolver.ts#L458
173+
export interface TypeReferenceDirectiveResolutionCache {
174+
getOrCreateCacheForDirectory(
175+
directoryName: string,
176+
redirectedReference?: typescript.ResolvedProjectReference
177+
): Map<
178+
string,
179+
typescript.ResolvedTypeReferenceDirectiveWithFailedLookupLocations
180+
>;
181+
clear(): void;
182+
update(compilerOptions: typescript.CompilerOptions): void;
183+
}
142184
export interface TSInstance {
143185
compiler: typeof typescript;
144186
compilerOptions: typescript.CompilerOptions;
145187
/** Used for Vue for the most part */
146188
appendTsTsxSuffixesIfRequired: (filePath: string) => string;
147189
loaderOptions: LoaderOptions;
148190
rootFileNames: Set<string>;
191+
moduleResolutionCache?: ModuleResolutionCache;
192+
typeReferenceResolutionCache?: TypeReferenceDirectiveResolutionCache;
149193
/**
150194
* a cache of all the files
151195
*/

src/servicesHost.ts

Lines changed: 147 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
CustomResolveModuleName,
1111
CustomResolveTypeReferenceDirective,
1212
FilePathKey,
13+
ModuleResolutionCache,
1314
ModuleResolutionHostMayBeCacheable,
1415
ResolvedModule,
1516
ServiceHostWhichMayBeCacheable,
@@ -247,39 +248,19 @@ function makeResolvers<T extends typescript.ModuleResolutionHost>(
247248
scriptRegex: RegExp,
248249
instance: TSInstance
249250
) {
250-
const resolveTypeReferenceDirective = makeResolveTypeReferenceDirective(
251-
compiler,
252-
compilerOptions,
253-
moduleResolutionHost,
254-
customResolveTypeReferenceDirective
255-
);
256-
257-
const resolveTypeReferenceDirectives = (
258-
typeDirectiveNames: string[],
259-
containingFile: string,
260-
_redirectedReference?: typescript.ResolvedProjectReference
261-
): (typescript.ResolvedTypeReferenceDirective | undefined)[] =>
262-
typeDirectiveNames.map(
263-
directive =>
264-
resolveTypeReferenceDirective(
265-
directive,
266-
containingFile,
267-
_redirectedReference
268-
).resolvedTypeReferenceDirective
269-
);
270-
271251
const resolveModuleName = makeResolveModuleName(
272252
compiler,
273253
compilerOptions,
274254
moduleResolutionHost,
275-
customResolveModuleName
255+
customResolveModuleName,
256+
instance
276257
);
277258

278259
const resolveModuleNames = (
279260
moduleNames: string[],
280261
containingFile: string,
281262
_reusedNames?: string[] | undefined,
282-
_redirectedReference?: typescript.ResolvedProjectReference | undefined
263+
redirectedReference?: typescript.ResolvedProjectReference | undefined
283264
): (typescript.ResolvedModule | undefined)[] => {
284265
const resolvedModules = moduleNames.map(moduleName =>
285266
resolveModule(
@@ -288,7 +269,8 @@ function makeResolvers<T extends typescript.ModuleResolutionHost>(
288269
appendTsTsxSuffixesIfRequired,
289270
scriptRegex,
290271
moduleName,
291-
containingFile
272+
containingFile,
273+
redirectedReference
292274
)
293275
);
294276

@@ -297,6 +279,28 @@ function makeResolvers<T extends typescript.ModuleResolutionHost>(
297279
return resolvedModules;
298280
};
299281

282+
const resolveTypeReferenceDirective = makeResolveTypeReferenceDirective(
283+
compiler,
284+
compilerOptions,
285+
moduleResolutionHost,
286+
customResolveTypeReferenceDirective,
287+
instance
288+
);
289+
290+
const resolveTypeReferenceDirectives = (
291+
typeDirectiveNames: string[],
292+
containingFile: string,
293+
redirectedReference?: typescript.ResolvedProjectReference
294+
): (typescript.ResolvedTypeReferenceDirective | undefined)[] =>
295+
typeDirectiveNames.map(
296+
directive =>
297+
resolveTypeReferenceDirective(
298+
directive,
299+
containingFile,
300+
redirectedReference
301+
).resolvedTypeReferenceDirective
302+
);
303+
300304
return {
301305
resolveTypeReferenceDirectives,
302306
resolveModuleNames,
@@ -492,7 +496,7 @@ export function makeWatchHost(
492496
fileName =>
493497
files.has(filePathKeyMapper(fileName)) ||
494498
compiler.sys.fileExists(fileName),
495-
/*enabledCaching*/ false
499+
instance.loaderOptions.experimentalFileCaching
496500
);
497501

498502
const watchHost: WatchHost = {
@@ -600,6 +604,60 @@ export function makeWatchHost(
600604

601605
const missingFileModifiedTime = new Date(0);
602606

607+
function identity<T>(x: T) {
608+
return x;
609+
}
610+
function toLowerCase(x: string) {
611+
return x.toLowerCase();
612+
}
613+
const fileNameLowerCaseRegExp = /[^\u0130\u0131\u00DFa-z0-9\\/:\-_\. ]+/g;
614+
function toFileNameLowerCase(x: string) {
615+
return fileNameLowerCaseRegExp.test(x)
616+
? x.replace(fileNameLowerCaseRegExp, toLowerCase)
617+
: x;
618+
}
619+
function createGetCanonicalFileName(instance: TSInstance) {
620+
return useCaseSensitiveFileNames(instance.compiler, instance.loaderOptions)
621+
? identity
622+
: toFileNameLowerCase;
623+
}
624+
625+
function createModuleResolutionCache(
626+
instance: TSInstance,
627+
moduleResolutionHost: typescript.ModuleResolutionHost
628+
): ModuleResolutionCache {
629+
const cache = instance.compiler.createModuleResolutionCache(
630+
moduleResolutionHost.getCurrentDirectory!(),
631+
createGetCanonicalFileName(instance),
632+
instance.compilerOptions
633+
) as ModuleResolutionCache;
634+
// Add new API optional methods
635+
if (!cache.clear) {
636+
cache.clear = () => {
637+
cache.directoryToModuleNameMap.clear();
638+
cache.moduleNameToDirectoryMap.clear();
639+
};
640+
}
641+
if (!cache.update) {
642+
cache.update = options => {
643+
if (!options.configFile) return;
644+
const ref: typescript.ResolvedProjectReference = {
645+
sourceFile: options.configFile! as typescript.TsConfigSourceFile,
646+
commandLine: { options } as typescript.ParsedCommandLine,
647+
};
648+
cache.directoryToModuleNameMap.setOwnMap(
649+
cache.directoryToModuleNameMap.getOrCreateMapOfCacheRedirects(ref)
650+
);
651+
cache.moduleNameToDirectoryMap.setOwnMap(
652+
cache.moduleNameToDirectoryMap.getOrCreateMapOfCacheRedirects(ref)
653+
);
654+
cache.directoryToModuleNameMap.setOwnOptions(options);
655+
cache.moduleNameToDirectoryMap.setOwnOptions(options);
656+
};
657+
}
658+
return cache;
659+
}
660+
603661
/**
604662
* Create the TypeScript Watch host
605663
*/
@@ -617,12 +675,7 @@ export function makeSolutionBuilderHost(
617675
// loader.context seems to work fine on Linux / Mac regardless causes problems for @types resolution on Windows for TypeScript < 2.3
618676
const formatDiagnosticHost: typescript.FormatDiagnosticsHost = {
619677
getCurrentDirectory: compiler.sys.getCurrentDirectory,
620-
getCanonicalFileName: useCaseSensitiveFileNames(
621-
compiler,
622-
instance.loaderOptions
623-
)
624-
? s => s
625-
: s => s.toLowerCase(),
678+
getCanonicalFileName: createGetCanonicalFileName(instance),
626679
getNewLine: () => compiler.sys.newLine,
627680
};
628681

@@ -705,6 +758,28 @@ export function makeSolutionBuilderHost(
705758
const solutionBuilderHost: SolutionBuilderWithWatchHost = {
706759
...sysHost,
707760
...moduleResolutionHost,
761+
createProgram: (
762+
rootNames,
763+
options,
764+
host,
765+
oldProgram,
766+
configFileParsingDiagnostics,
767+
projectReferences
768+
) => {
769+
instance.moduleResolutionCache?.update(options || {});
770+
instance.typeReferenceResolutionCache?.update(options || {});
771+
const result = sysHost.createProgram(
772+
rootNames,
773+
options,
774+
host,
775+
oldProgram,
776+
configFileParsingDiagnostics,
777+
projectReferences
778+
);
779+
instance.typeReferenceResolutionCache?.update(instance.compilerOptions);
780+
instance.moduleResolutionCache?.update(instance.compilerOptions);
781+
return result;
782+
},
708783
resolveModuleNames,
709784
resolveTypeReferenceDirectives,
710785
diagnostics,
@@ -1091,16 +1166,31 @@ function makeResolveTypeReferenceDirective(
10911166
moduleResolutionHost: typescript.ModuleResolutionHost,
10921167
customResolveTypeReferenceDirective:
10931168
| CustomResolveTypeReferenceDirective
1094-
| undefined
1169+
| undefined,
1170+
instance: TSInstance
10951171
): ResolveTypeReferenceDirective {
10961172
if (customResolveTypeReferenceDirective === undefined) {
1173+
// Until the api is published
1174+
if (
1175+
(compiler as any).createTypeReferenceDirectiveResolutionCache &&
1176+
!instance.typeReferenceResolutionCache
1177+
) {
1178+
instance.typeReferenceResolutionCache = (compiler as any).createTypeReferenceDirectiveResolutionCache(
1179+
moduleResolutionHost.getCurrentDirectory!(),
1180+
createGetCanonicalFileName(instance),
1181+
instance.compilerOptions,
1182+
instance.moduleResolutionCache?.getPackageJsonInfoCache?.()
1183+
);
1184+
}
10971185
return (directive, containingFile, redirectedReference) =>
1098-
compiler.resolveTypeReferenceDirective(
1186+
// Until the api is published
1187+
(compiler.resolveTypeReferenceDirective as any)(
10991188
directive,
11001189
containingFile,
11011190
compilerOptions,
11021191
moduleResolutionHost,
1103-
redirectedReference
1192+
redirectedReference,
1193+
instance.typeReferenceResolutionCache
11041194
);
11051195
}
11061196

@@ -1130,7 +1220,8 @@ function resolveModule(
11301220
appendTsTsxSuffixesIfRequired: (filePath: string) => string,
11311221
scriptRegex: RegExp,
11321222
moduleName: string,
1133-
containingFile: string
1223+
containingFile: string,
1224+
redirectedReference: typescript.ResolvedProjectReference | undefined
11341225
) {
11351226
let resolutionResult: ResolvedModule;
11361227

@@ -1150,16 +1241,19 @@ function resolveModule(
11501241
}
11511242
} catch (e) {}
11521243

1153-
const tsResolution = resolveModuleName(moduleName, containingFile);
1244+
const tsResolution = resolveModuleName(
1245+
moduleName,
1246+
containingFile,
1247+
redirectedReference
1248+
);
11541249
if (tsResolution.resolvedModule !== undefined) {
11551250
const resolvedFileName = path.normalize(
11561251
tsResolution.resolvedModule.resolvedFileName
11571252
);
11581253
const tsResolutionResult: ResolvedModule = {
1254+
...tsResolution.resolvedModule,
11591255
originalFileName: resolvedFileName,
11601256
resolvedFileName,
1161-
isExternalLibraryImport:
1162-
tsResolution.resolvedModule.isExternalLibraryImport,
11631257
};
11641258

11651259
return resolutionResult! === undefined ||
@@ -1174,26 +1268,36 @@ function resolveModule(
11741268

11751269
type ResolveModuleName = (
11761270
moduleName: string,
1177-
containingFile: string
1271+
containingFile: string,
1272+
redirectedReference: typescript.ResolvedProjectReference | undefined
11781273
) => typescript.ResolvedModuleWithFailedLookupLocations;
11791274

11801275
function makeResolveModuleName(
11811276
compiler: typeof typescript,
11821277
compilerOptions: typescript.CompilerOptions,
11831278
moduleResolutionHost: typescript.ModuleResolutionHost,
1184-
customResolveModuleName: CustomResolveModuleName | undefined
1279+
customResolveModuleName: CustomResolveModuleName | undefined,
1280+
instance: TSInstance
11851281
): ResolveModuleName {
11861282
if (customResolveModuleName === undefined) {
1187-
return (moduleName: string, containingFile: string) =>
1283+
if (!instance.moduleResolutionCache) {
1284+
instance.moduleResolutionCache = createModuleResolutionCache(
1285+
instance,
1286+
moduleResolutionHost
1287+
);
1288+
}
1289+
return (moduleName, containingFile, redirectedReference) =>
11881290
compiler.resolveModuleName(
11891291
moduleName,
11901292
containingFile,
11911293
compilerOptions,
1192-
moduleResolutionHost
1294+
moduleResolutionHost,
1295+
instance.moduleResolutionCache,
1296+
redirectedReference
11931297
);
11941298
}
11951299

1196-
return (moduleName: string, containingFile: string) =>
1300+
return (moduleName, containingFile) =>
11971301
customResolveModuleName(
11981302
moduleName,
11991303
containingFile,

0 commit comments

Comments
 (0)