Skip to content

Commit

Permalink
Ensure a separate webpack instance is created for different loader op…
Browse files Browse the repository at this point in the history
…tions (#1104)

* Ensure a separate webpack instance is created if different loader options are used
Add execution test for this behaviour

* Fix import order (lint error)

* Update version to 7.04 and include in changelog

* Tweak to Changelog
  • Loading branch information
appzuka committed May 11, 2020
1 parent 227d99a commit 1b84fed
Show file tree
Hide file tree
Showing 18 changed files with 183 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,8 @@
# Changelog

## v7.0.4
* [Ensure a separate webpack instance is created for different loader options](https://github.com/TypeStrong/ts-loader/pull/1104) - thanks @appzuka

## v7.0.3
* [Ensure that JSON files are included in build module resolution](https://github.com/TypeStrong/ts-loader/pull/1101) - thanks @berickson1

Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "ts-loader",
"version": "7.0.3",
"version": "7.0.4",
"description": "TypeScript loader for webpack",
"main": "index.js",
"types": "dist",
Expand Down
25 changes: 23 additions & 2 deletions src/index.ts
@@ -1,3 +1,4 @@
import * as crypto from 'crypto';
import * as loaderUtils from 'loader-utils';
import * as path from 'path';
import * as typescript from 'typescript';
Expand Down Expand Up @@ -220,6 +221,22 @@ function setModuleMeta(
}
}

/**
* Get a unique hash based on the contents of the options
* Hash is created from the values converted to strings
* Values which are functions (such as getCustomTransformers) are
* converted to strings by this code, which JSON.stringify would not do.
*/
function getOptionsHash(loaderOptions: LoaderOptions) {
const hash = crypto.createHash('sha256');
Object.values(loaderOptions).map((v: any) => {
if (v) {
hash.update(v.toString());
}
});
return hash.digest('hex').substring(0, 16);
}

/**
* either retrieves loader options from the cache
* or creates them, adds them to the cache and returns
Expand All @@ -235,8 +252,12 @@ function getLoaderOptions(loaderContext: webpack.loader.LoaderContext) {
loaderUtils.getOptions<LoaderOptions>(loaderContext) ||
({} as LoaderOptions);

// If no instance name is given in the options, use the hash of the loader options
// In this way, if different options are given the instances will be different
const instanceName =
webpackIndex + '_' + (loaderOptions.instance || 'default');
webpackIndex +
'_' +
(loaderOptions.instance || 'default_' + getOptionsHash(loaderOptions));

if (!loaderOptionsCache.hasOwnProperty(instanceName)) {
loaderOptionsCache[instanceName] = new WeakMap();
Expand Down Expand Up @@ -528,7 +549,7 @@ function getEmit(

loaderContext._module.buildMeta.tsLoaderDefinitionFileVersions = dependencies.map(
defFilePath =>
path.relative(loaderContext.rootContext,defFilePath) +
path.relative(loaderContext.rootContext, defFilePath) +
'@' +
(
instance.files.get(defFilePath) ||
Expand Down
3 changes: 3 additions & 0 deletions test/execution-tests/loaderOptions/Readme.md
@@ -0,0 +1,3 @@
## Loader Options Test

This test includes 3 submodules, only 1 of which should have the transformer applied via the options. This tests that separate webpack instances are created when module options are different.
17 changes: 17 additions & 0 deletions test/execution-tests/loaderOptions/karma.conf.js
@@ -0,0 +1,17 @@
/* eslint-disable no-var, strict */
'use strict';
var webpackConfig = require('./webpack.config.js');
var makeKarmaConfig = require('../../karmaConfig');

module.exports = function(config) {
config.set(
makeKarmaConfig({
config,
webpackConfig,
files: [
// This ensures we have the es6 shims in place from babel and then loads all the tests
'main.js'
]
})
);
};
7 changes: 7 additions & 0 deletions test/execution-tests/loaderOptions/lib/externalLib.d.ts
@@ -0,0 +1,7 @@
declare module externalLib {
export function doSomething(arg: any): void;
}

declare module 'externalLib' {
export = externalLib
}
3 changes: 3 additions & 0 deletions test/execution-tests/loaderOptions/lib/externalLib.js
@@ -0,0 +1,3 @@
module.exports = {
doSomething: function() { }
}
2 changes: 2 additions & 0 deletions test/execution-tests/loaderOptions/main.js
@@ -0,0 +1,2 @@
const testsContext = require.context('./', true, /\.tests\.ts(x?)$/);
testsContext.keys().forEach(testsContext);
10 changes: 10 additions & 0 deletions test/execution-tests/loaderOptions/package.json
@@ -0,0 +1,10 @@
{
"name": "basic",
"license": "MIT",
"version": "1.0.0",
"main": "index.js",
"devDependencies": {
"@types/jasmine": "^2.5.35",
"jasmine-core": "^2.3.4"
}
}
3 changes: 3 additions & 0 deletions test/execution-tests/loaderOptions/src/app.ts
@@ -0,0 +1,3 @@
import submodule = require('./submodule/submodule');
import externalLib = require('externalLib');
externalLib.doSomething(submodule);
5 changes: 5 additions & 0 deletions test/execution-tests/loaderOptions/src/submodule/submodule.ts
@@ -0,0 +1,5 @@
import externalLib = require('externalLib');

externalLib.doSomething("");
var message = "Hello from submodule"
export = message
@@ -0,0 +1,5 @@
import externalLib = require('externalLib');

externalLib.doSomething("");
var message = "Hello from submodule2"
export = message
@@ -0,0 +1,5 @@
import externalLib = require('externalLib');

externalLib.doSomething("");
var message = "Hello from submodule3"
export = message
22 changes: 22 additions & 0 deletions test/execution-tests/loaderOptions/test/app.tests.ts
@@ -0,0 +1,22 @@
import submodule = require('../src/submodule/submodule');
import submodule2 = require('../src/submodule/submodule2');
import submodule3 = require('../src/submodule/submodule3');
import externalLib = require('externalLib');

describe("app", () => {
it("externalLib can be called", () => {
expect(externalLib.doSomething(submodule)).toBeUndefined();
});

it("submodule return value should not be transformed", () => {
expect(submodule).toBe("Hello "+"from submodule");
});

it("submodule2 return value should be transformed", () => {
expect(submodule2).toBe("HELLO "+"FROM SUBMODULE2");
});

it("submodule3 return value should not be transformed", () => {
expect(submodule3).toBe("Hello "+"from submodule3");
});
});
5 changes: 5 additions & 0 deletions test/execution-tests/loaderOptions/tsconfig.json
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"noEmitOnError": true
}
}
@@ -0,0 +1,18 @@
"use strict";
exports.__esModule = true;
var ts = require("typescript");
var transformer = function (context) {
var visitor = function (node) {
if (node.kind === ts.SyntaxKind.StringLiteral) {
var text = node.text;
if (text.match(/^Hello from submodule/) !== null) {
if (text !== text.toUpperCase()) {
return ts.createLiteral(text.toUpperCase());
}
}
}
return ts.visitEachChild(node, visitor, context);
};
return function (node) { return ts.visitNode(node, visitor); };
};
exports["default"] = transformer;
40 changes: 40 additions & 0 deletions test/execution-tests/loaderOptions/webpack.config.js
@@ -0,0 +1,40 @@
var path = require('path')

var uppercaseStringLiteralTransformer = require('./uppercaseStringLiteralTransformer').default;

module.exports = {
mode: 'development',
entry: './src/app.ts',
output: {
filename: 'bundle.js'
},
resolve: {
alias: { externalLib: path.join(__dirname, "./lib/externalLib.js") },
extensions: ['.ts', '.js']
},
module: {
rules: [
{
test: /submodule3\.ts$/,
loader: 'ts-loader',
},
{
test: /submodule2\.ts$/,
loader: 'ts-loader',
options: {
getCustomTransformers: (program) => ({
before: [uppercaseStringLiteralTransformer]
})
},
},
{
test: /\.ts$/,
exclude: /submodule\d\.ts$/,
loader: 'ts-loader',
}
]
}
}

// for test harness purposes only, you would not need this in a normal project
module.exports.resolveLoader = { alias: { 'ts-loader': require('path').join(__dirname, "../../../index.js") } }
11 changes: 11 additions & 0 deletions test/execution-tests/loaderOptions/yarn.lock
@@ -0,0 +1,11 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@types/jasmine@^2.5.35":
version "2.8.5"
resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.5.tgz#96e58872583fa80c7ea0dd29024b180d5e133678"

jasmine-core@^2.3.4:
version "2.9.1"
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.9.1.tgz#b6bbc1d8e65250d56f5888461705ebeeeb88f22f"

0 comments on commit 1b84fed

Please sign in to comment.