From e6ecdfeba21bed5f9e82ce84c442b6d0eff689a4 Mon Sep 17 00:00:00 2001 From: Valerio Pipolo Date: Wed, 4 Nov 2020 13:13:38 +0100 Subject: [PATCH 1/3] Use a dummy compiler reference when the calling webpack compiler is undefined or null --- src/instances.ts | 58 ++++++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/src/instances.ts b/src/instances.ts index 69008844e..38a653803 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -33,22 +33,44 @@ import { } from './utils'; import { makeWatchRun } from './watch-run'; -// Each TypeScript instance is based on the webpack instance (key of the WeakMap) +// Each TypeScript instance is cached based on the webpack instance (key of the WeakMap) // and also the name that was generated or passed via the options (string key of the // internal Map) -const instanceCache = new WeakMap>(); -const instancesBySolutionBuilderConfigs = new Map(); +class InstanceCache { + // Some loaders (e.g. thread-loader) will set the _compiler property to undefined. + // We can't use undefined as a WeakMap key as it will throw an error at runtime, + // thus we keep a dummy "marker" object to use as key in those situations. + private marker: webpack.Compiler = {} as webpack.Compiler; + private map: WeakMap>; + + constructor() { + this.map = new WeakMap(); + } -function addTSInstanceToCache( - key: webpack.Compiler, - instanceName: string, - instance: TSInstance -) { - const instances = instanceCache.get(key) ?? new Map(); - instances.set(instanceName, instance); - instanceCache.set(key, instances); + get(key: webpack.Compiler, name: string): TSInstance | undefined { + const compiler = key ?? this.marker; + + let instances = this.map.get(compiler); + if (!instances) { + instances = new Map(); + this.map.set(compiler, instances); + } + + return instances.get(name); + } + + set(key: webpack.Compiler, name: string, instance: TSInstance) { + const compiler = key ?? this.marker; + + const instances = this.map.get(compiler) ?? new Map(); + instances.set(name, instance); + this.map.set(compiler, instances); + } } +const instanceCache = new InstanceCache(); +const instancesBySolutionBuilderConfigs = new Map(); + /** * The loader is executed once for each file seen by webpack. However, we need to keep * a persistent instance of TypeScript that contains all of the files in the program @@ -60,13 +82,7 @@ export function getTypeScriptInstance( loaderOptions: LoaderOptions, loader: webpack.loader.LoaderContext ): { instance?: TSInstance; error?: WebpackError } { - let instances = instanceCache.get(loader._compiler); - if (!instances) { - instances = new Map(); - instanceCache.set(loader._compiler, instances); - } - - const existing = instances.get(loaderOptions.instance); + const existing = instanceCache.get(loader._compiler, loaderOptions.instance); if (existing) { if (!existing.initialSetupPending) { ensureProgram(existing); @@ -160,7 +176,7 @@ function successfulTypeScriptInstance( const existing = getExistingSolutionBuilderHost(configFileKey); if (existing) { // Reuse the instance if config file for project references is shared. - addTSInstanceToCache(loader._compiler, loaderOptions.instance, existing); + instanceCache.set(loader._compiler, loaderOptions.instance, existing); return { instance: existing }; } } @@ -246,7 +262,7 @@ function successfulTypeScriptInstance( filePathKeyMapper, }; - addTSInstanceToCache( + instanceCache.set( loader._compiler, loaderOptions.instance, transpileInstance @@ -303,7 +319,7 @@ function successfulTypeScriptInstance( filePathKeyMapper, }; - addTSInstanceToCache(loader._compiler, loaderOptions.instance, instance); + instanceCache.set(loader._compiler, loaderOptions.instance, instance); return { instance }; } From 305871f160f17c84b2885bacb993f482eebca0d4 Mon Sep 17 00:00:00 2001 From: Valerio Pipolo Date: Wed, 4 Nov 2020 14:39:16 +0100 Subject: [PATCH 2/3] Use a module instead of a class for caching instances --- src/instance-cache.ts | 39 +++++++++++++++++++++++++++++++++++ src/instances.ts | 48 ++++++++----------------------------------- 2 files changed, 47 insertions(+), 40 deletions(-) create mode 100644 src/instance-cache.ts diff --git a/src/instance-cache.ts b/src/instance-cache.ts new file mode 100644 index 000000000..4ed44e9a5 --- /dev/null +++ b/src/instance-cache.ts @@ -0,0 +1,39 @@ +import * as webpack from 'webpack'; +import { TSInstance } from './interfaces'; + +// Some loaders (e.g. thread-loader) will set the _compiler property to undefined. +// We can't use undefined as a WeakMap key as it will throw an error at runtime, +// thus we keep a dummy "marker" object to use as key in those situations. +const marker: webpack.Compiler = {} as webpack.Compiler; + +// Each TypeScript instance is cached based on the webpack instance (key of the WeakMap) +// and also the name that was generated or passed via the options (string key of the +// internal Map) +const cache: WeakMap> = new WeakMap(); + +export function getTSInstanceFromCache( + key: webpack.Compiler, + name: string +): TSInstance | undefined { + const compiler = key ?? marker; + + let instances = cache.get(compiler); + if (!instances) { + instances = new Map(); + cache.set(compiler, instances); + } + + return instances.get(name); +} + +export function setTSInstanceInCache( + key: webpack.Compiler, + name: string, + instance: TSInstance +) { + const compiler = key ?? marker; + + const instances = cache.get(compiler) ?? new Map(); + instances.set(name, instance); + cache.set(compiler, instances); +} diff --git a/src/instances.ts b/src/instances.ts index 38a653803..f7496132a 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -8,6 +8,7 @@ import { makeAfterCompile } from './after-compile'; import { getCompiler, getCompilerOptions } from './compilerSetup'; import { getConfigFile, getConfigParseResult } from './config'; import { dtsDtsxOrDtsDtsxMapRegex, EOL, tsTsxRegex } from './constants'; +import { getTSInstanceFromCache, setTSInstanceInCache } from './instance-cache'; import { FilePathKey, LoaderOptions, @@ -33,42 +34,6 @@ import { } from './utils'; import { makeWatchRun } from './watch-run'; -// Each TypeScript instance is cached based on the webpack instance (key of the WeakMap) -// and also the name that was generated or passed via the options (string key of the -// internal Map) -class InstanceCache { - // Some loaders (e.g. thread-loader) will set the _compiler property to undefined. - // We can't use undefined as a WeakMap key as it will throw an error at runtime, - // thus we keep a dummy "marker" object to use as key in those situations. - private marker: webpack.Compiler = {} as webpack.Compiler; - private map: WeakMap>; - - constructor() { - this.map = new WeakMap(); - } - - get(key: webpack.Compiler, name: string): TSInstance | undefined { - const compiler = key ?? this.marker; - - let instances = this.map.get(compiler); - if (!instances) { - instances = new Map(); - this.map.set(compiler, instances); - } - - return instances.get(name); - } - - set(key: webpack.Compiler, name: string, instance: TSInstance) { - const compiler = key ?? this.marker; - - const instances = this.map.get(compiler) ?? new Map(); - instances.set(name, instance); - this.map.set(compiler, instances); - } -} - -const instanceCache = new InstanceCache(); const instancesBySolutionBuilderConfigs = new Map(); /** @@ -82,7 +47,10 @@ export function getTypeScriptInstance( loaderOptions: LoaderOptions, loader: webpack.loader.LoaderContext ): { instance?: TSInstance; error?: WebpackError } { - const existing = instanceCache.get(loader._compiler, loaderOptions.instance); + const existing = getTSInstanceFromCache( + loader._compiler, + loaderOptions.instance + ); if (existing) { if (!existing.initialSetupPending) { ensureProgram(existing); @@ -176,7 +144,7 @@ function successfulTypeScriptInstance( const existing = getExistingSolutionBuilderHost(configFileKey); if (existing) { // Reuse the instance if config file for project references is shared. - instanceCache.set(loader._compiler, loaderOptions.instance, existing); + setTSInstanceInCache(loader._compiler, loaderOptions.instance, existing); return { instance: existing }; } } @@ -262,7 +230,7 @@ function successfulTypeScriptInstance( filePathKeyMapper, }; - instanceCache.set( + setTSInstanceInCache( loader._compiler, loaderOptions.instance, transpileInstance @@ -319,7 +287,7 @@ function successfulTypeScriptInstance( filePathKeyMapper, }; - instanceCache.set(loader._compiler, loaderOptions.instance, instance); + setTSInstanceInCache(loader._compiler, loaderOptions.instance, instance); return { instance }; } From de75841ddfc2c0aedfdbd5a9cce8a1a2c9aa0e51 Mon Sep 17 00:00:00 2001 From: Valerio Pipolo Date: Wed, 4 Nov 2020 14:39:39 +0100 Subject: [PATCH 3/3] Bump version and update changelog --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b396a94e7..763cc44d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## v8.0.9 +* [Fixed build failing when using thread-loader](https://github.com/TypeStrong/ts-loader/pull/1207) - thanks @valerio + ## v8.0.8 * [Fixed memory leak when using multiple webpack instances](https://github.com/TypeStrong/ts-loader/pull/1205) - thanks @valerio diff --git a/package.json b/package.json index e5e48f498..9c12bc5cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ts-loader", - "version": "8.0.8", + "version": "8.0.9", "description": "TypeScript loader for webpack", "main": "index.js", "types": "dist",