Skip to content

Commit

Permalink
Fix failed builds when using thread-loader (TypeStrong#1207)
Browse files Browse the repository at this point in the history
* Use a dummy compiler reference when the calling webpack compiler is undefined or null

* Use a module instead of a class for caching instances

* Bump version and update changelog
  • Loading branch information
Valerio Pipolo authored Nov 4, 2020
1 parent e90f8ad commit 3f73e98
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 25 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
39 changes: 39 additions & 0 deletions src/instance-cache.ts
Original file line number Diff line number Diff line change
@@ -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<webpack.Compiler, Map<string, TSInstance>> = 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<string, TSInstance>();
instances.set(name, instance);
cache.set(compiler, instances);
}
32 changes: 8 additions & 24 deletions src/instances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -33,22 +34,8 @@ import {
} from './utils';
import { makeWatchRun } from './watch-run';

// Each TypeScript instance is 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<webpack.Compiler, Map<string, TSInstance>>();
const instancesBySolutionBuilderConfigs = new Map<FilePathKey, TSInstance>();

function addTSInstanceToCache(
key: webpack.Compiler,
instanceName: string,
instance: TSInstance
) {
const instances = instanceCache.get(key) ?? new Map<string, TSInstance>();
instances.set(instanceName, instance);
instanceCache.set(key, instances);
}

/**
* 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
Expand All @@ -60,13 +47,10 @@ 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 = getTSInstanceFromCache(
loader._compiler,
loaderOptions.instance
);
if (existing) {
if (!existing.initialSetupPending) {
ensureProgram(existing);
Expand Down Expand Up @@ -160,7 +144,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);
setTSInstanceInCache(loader._compiler, loaderOptions.instance, existing);
return { instance: existing };
}
}
Expand Down Expand Up @@ -246,7 +230,7 @@ function successfulTypeScriptInstance(
filePathKeyMapper,
};

addTSInstanceToCache(
setTSInstanceInCache(
loader._compiler,
loaderOptions.instance,
transpileInstance
Expand Down Expand Up @@ -303,7 +287,7 @@ function successfulTypeScriptInstance(
filePathKeyMapper,
};

addTSInstanceToCache(loader._compiler, loaderOptions.instance, instance);
setTSInstanceInCache(loader._compiler, loaderOptions.instance, instance);
return { instance };
}

Expand Down

0 comments on commit 3f73e98

Please sign in to comment.