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

Ensure a separate webpack instance is created for different loader options #1104

Merged
merged 4 commits into from May 11, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you thank yourself please? It's traditional now 🤗


## 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"