Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix failed builds when using thread-loader #1207

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions 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

Expand Down
2 changes: 1 addition & 1 deletion 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",
Expand Down
39 changes: 39 additions & 0 deletions 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<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
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