Skip to content

Commit 5e7220b

Browse files
Use custom transformer when building solution references (#1550)
* Refactor getting custom transformer into a dedicated function This allows us to share the function between `initializeInstance` and `makeSolutionBuilderHost`. `makeSolutionBuilderHost` is called as a part of `buildSolutionReferences`. `buildSolutionReferences` is called before `initializeInstance` where the custom transformers are initialized. * Implement `getCustomTransformer` in `makeSolutionBuilderHost` This allows us to pass on the custom transformer while the project references are built. * Remove redundant `.toString` `options.configFilePath` is always a string, there's no reason we need `toString()` too. * Runtime check that project is a string In a previous commit, I removed `.toString()` without validating what the Typescript compiler would say. This change does a runtime check that the project variable is an actual string. * Update `CHANGELOG.md` and `package.json` Co-authored-by: John Reilly <[email protected]>
1 parent 87a9fff commit 5e7220b

26 files changed

+335
-31
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## 9.4.2
4+
* [Bug fix: Use custom transformer when building solution references](https://github.com/TypeStrong/ts-loader/pull/1550) [#1025] - thanks @feosuna1
5+
36
## 9.4.1
47
* [Hotfix: Disable `enhanced-resolve`](https://github.com/TypeStrong/ts-loader/pull/1505) - thanks @manuth
58

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ts-loader",
3-
"version": "9.4.1",
3+
"version": "9.4.2",
44
"description": "TypeScript loader for webpack",
55
"main": "index.js",
66
"types": "dist",

src/instances.ts

+38-29
Original file line numberDiff line numberDiff line change
@@ -347,33 +347,6 @@ export function initializeInstance(
347347

348348
instance.initialSetupPending = false;
349349

350-
// same strategy as https://github.com/s-panferov/awesome-typescript-loader/pull/531/files
351-
let { getCustomTransformers: customerTransformers } = instance.loaderOptions;
352-
let getCustomTransformers = Function.prototype;
353-
354-
if (typeof customerTransformers === 'function') {
355-
getCustomTransformers = customerTransformers;
356-
} else if (typeof customerTransformers === 'string') {
357-
try {
358-
customerTransformers = require(customerTransformers);
359-
} catch (err) {
360-
throw new Error(
361-
`Failed to load customTransformers from "${
362-
instance.loaderOptions.getCustomTransformers
363-
}": ${err instanceof Error ? err.message : 'unknown error'}`
364-
);
365-
}
366-
367-
if (typeof customerTransformers !== 'function') {
368-
throw new Error(
369-
`Custom transformers in "${
370-
instance.loaderOptions.getCustomTransformers
371-
}" should export a function, got ${typeof customerTransformers}`
372-
);
373-
}
374-
getCustomTransformers = customerTransformers;
375-
}
376-
377350
if (instance.loaderOptions.transpileOnly) {
378351
const program = (instance.program =
379352
instance.configParseResult.projectReferences !== undefined
@@ -385,7 +358,7 @@ export function initializeInstance(
385358
: instance.compiler.createProgram([], instance.compilerOptions));
386359

387360
const getProgram = () => program;
388-
instance.transformers = getCustomTransformers(program, getProgram);
361+
instance.transformers = getCustomTransformers(instance.loaderOptions, program, getProgram);
389362
// Setup watch run for solution building
390363
if (instance.solutionBuilderHost) {
391364
addAssetHooks(loader, instance);
@@ -419,6 +392,7 @@ export function initializeInstance(
419392
const getProgram = () => instance.builderProgram?.getProgram();
420393
instance.program = getProgram();
421394
instance.transformers = getCustomTransformers(
395+
instance.loaderOptions,
422396
instance.program,
423397
getProgram
424398
);
@@ -436,7 +410,7 @@ export function initializeInstance(
436410
);
437411

438412
const getProgram = () => instance.languageService!.getProgram();
439-
instance.transformers = getCustomTransformers(getProgram(), getProgram);
413+
instance.transformers = getCustomTransformers(instance.loaderOptions, getProgram(), getProgram);
440414
}
441415

442416
addAssetHooks(loader, instance);
@@ -448,6 +422,41 @@ export function initializeInstance(
448422
}
449423
}
450424

425+
export function getCustomTransformers(
426+
loaderOptions: LoaderOptions,
427+
program: typescript.Program | undefined,
428+
getProgram: (() => typescript.Program | undefined) | undefined
429+
) {
430+
// same strategy as https://github.com/s-panferov/awesome-typescript-loader/pull/531/files
431+
let { getCustomTransformers: customerTransformers } = loaderOptions;
432+
let getCustomTransformers = Function.prototype;
433+
434+
if (typeof customerTransformers === 'function') {
435+
getCustomTransformers = customerTransformers;
436+
} else if (typeof customerTransformers === 'string') {
437+
try {
438+
customerTransformers = require(customerTransformers);
439+
} catch (err) {
440+
throw new Error(
441+
`Failed to load customTransformers from "${
442+
loaderOptions.getCustomTransformers
443+
}": ${err instanceof Error ? err.message : 'unknown error'}`
444+
);
445+
}
446+
447+
if (typeof customerTransformers !== 'function') {
448+
throw new Error(
449+
`Custom transformers in "${
450+
loaderOptions.getCustomTransformers
451+
}" should export a function, got ${typeof customerTransformers}`
452+
);
453+
}
454+
getCustomTransformers = customerTransformers;
455+
}
456+
457+
return getCustomTransformers(program, getProgram);
458+
}
459+
451460
function getScriptRegexp(instance: TSInstance) {
452461
// If resolveJsonModules is set, we should accept json files
453462
if (instance.configParseResult.options.resolveJsonModule) {

src/servicesHost.ts

+22-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type * as typescript from 'typescript';
33
import * as webpack from 'webpack';
44
import { getParsedCommandLine } from './config';
55
import * as constants from './constants';
6-
import { getOutputFileNames } from './instances';
6+
import { getCustomTransformers, getOutputFileNames } from './instances';
77
import {
88
CacheableHost,
99
ConfigFileInfo,
@@ -766,6 +766,11 @@ export function makeSolutionBuilderHost(
766766
reportSolutionBuilderStatus,
767767
reportWatchStatus
768768
);
769+
770+
// Keeps track of the various `typescript.CustomTransformers` for each program that is created.
771+
const customTransformers = new Map<string, typescript.CustomTransformers>();
772+
773+
// let lastBuilderProgram: typescript.CreateProgram | undefined = undefined;
769774
const solutionBuilderHost: SolutionBuilderWithWatchHost = {
770775
...sysHost,
771776
...moduleResolutionHost,
@@ -789,12 +794,28 @@ export function makeSolutionBuilderHost(
789794
);
790795
instance.typeReferenceResolutionCache?.update(instance.compilerOptions);
791796
instance.moduleResolutionCache?.update(instance.compilerOptions);
797+
798+
if (options) {
799+
// The `configFilePath` is the same value that is used as the `project` parameter of
800+
// `getCustomtransformers` below.
801+
const project = options.configFilePath;
802+
if (typeof project === "string") {
803+
// Custom transformers need a reference to the `typescript.Program`, that reference is
804+
// unavailable during the the `getCustomTransformers` callback below.
805+
const transformers = getCustomTransformers(instance.loaderOptions, result.getProgram(), result.getProgram);
806+
customTransformers.set(project, transformers);
807+
}
808+
}
809+
792810
return result;
793811
},
794812
resolveModuleNames,
795813
resolveTypeReferenceDirectives,
796814
diagnostics,
797815
...createWatchFactory(filePathKeyMapper, compiler),
816+
getCustomTransformers: function (project: string) {
817+
return customTransformers.get(project);
818+
},
798819
// Overrides
799820
writeFile: (name, text, writeByteOrderMark) => {
800821
const key = filePathKeyMapper(name);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { lib } from './lib';
2+
3+
console.log(lib.one, lib.two, lib.three);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
3+
* This devtool is neither made for production nor for readable output files.
4+
* It uses "eval()" calls to create a separate source file in the browser devtools.
5+
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
6+
* or disable the default devtool with "devtool: false".
7+
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
8+
*/
9+
/******/ (() => { // webpackBootstrap
10+
/******/ "use strict";
11+
/******/ var __webpack_modules__ = ({
12+
13+
/***/ "./app.ts":
14+
/*!****************!*\
15+
!*** ./app.ts ***!
16+
\****************/
17+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
18+
19+
eval("\nexports.__esModule = true;\nvar lib_1 = __webpack_require__(/*! ./lib */ \"./lib/index.ts\");\n/*transform was here*/ console.log(lib_1.lib.one, lib_1.lib.two, lib_1.lib.three);\n\n\n//# sourceURL=webpack:///./app.ts?");
20+
21+
/***/ }),
22+
23+
/***/ "./lib/index.ts":
24+
/*!**********************!*\
25+
!*** ./lib/index.ts ***!
26+
\**********************/
27+
/***/ ((__unused_webpack_module, exports) => {
28+
29+
eval("\nexports.__esModule = true;\nexports.lib = void 0;\n/*transform was here*/ exports.lib = {\n one: 1,\n two: 2,\n three: 3\n};\n\n\n//# sourceURL=webpack:///./lib/index.ts?");
30+
31+
/***/ })
32+
33+
/******/ });
34+
/************************************************************************/
35+
/******/ // The module cache
36+
/******/ var __webpack_module_cache__ = {};
37+
/******/
38+
/******/ // The require function
39+
/******/ function __webpack_require__(moduleId) {
40+
/******/ // Check if module is in cache
41+
/******/ var cachedModule = __webpack_module_cache__[moduleId];
42+
/******/ if (cachedModule !== undefined) {
43+
/******/ return cachedModule.exports;
44+
/******/ }
45+
/******/ // Create a new module (and put it into the cache)
46+
/******/ var module = __webpack_module_cache__[moduleId] = {
47+
/******/ // no module.id needed
48+
/******/ // no module.loaded needed
49+
/******/ exports: {}
50+
/******/ };
51+
/******/
52+
/******/ // Execute the module function
53+
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
54+
/******/
55+
/******/ // Return the exports of the module
56+
/******/ return module.exports;
57+
/******/ }
58+
/******/
59+
/************************************************************************/
60+
/******/
61+
/******/ // startup
62+
/******/ // Load entry module and return exports
63+
/******/ // This entry module can't be inlined because the eval devtool is used.
64+
/******/ var __webpack_exports__ = __webpack_require__("./app.ts");
65+
/******/
66+
/******/ })()
67+
;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export declare const lib: {
2+
one: number;
3+
two: number;
4+
three: number;
5+
};

test/comparison-tests/projectReferencesWithCustomTransformer/expectedOutput-4.9/lib/index.js

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/comparison-tests/projectReferencesWithCustomTransformer/expectedOutput-4.9/lib/index.js.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"program":{"fileNames":["../../../node_modules/typescript/lib/lib.d.ts","../../../node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/typescript/lib/lib.dom.d.ts","../../../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../../node_modules/typescript/lib/lib.scripthost.d.ts","./index.ts"],"fileInfos":["2dc8c927c9c162a773c6bb3cdc4f3286c23f10eedc67414028f9cb5951610f60",{"version":"8730f4bf322026ff5229336391a18bcaa1f94d4f82416c8b2f3954e2ccaae2ba","affectsGlobalScope":true},{"version":"3aafcb693fe5b5c3bd277bd4c3a617b53db474fe498fc5df067c5603b1eebde7","affectsGlobalScope":true},{"version":"7fac8cb5fc820bc2a59ae11ef1c5b38d3832c6d0dfaec5acdb5569137d09a481","affectsGlobalScope":true},{"version":"097a57355ded99c68e6df1b738990448e0bf170e606707df5a7c0481ff2427cd","affectsGlobalScope":true},"28ead8445f54a115ea5f778da4f4f80579fbae42ac6ccc3493626084ed335839"],"options":{"composite":true,"newLine":1,"skipLibCheck":true,"sourceMap":true},"referencedMap":[],"exportedModulesMap":[],"semanticDiagnosticsPerFile":[6,1,3,2,5,4],"emitSignatures":[[6,"82b9c263edd140802d0afbd57d557b2c41db16c5ad9a744bca8c71ad5b10f66f"]],"latestChangedDtsFile":"./index.d.ts"},"version":"4.9.3"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
asset bundle.js 2.6 KiB [emitted] (name: main)
2+
asset lib/tsconfig.tsbuildinfo 1.17 KiB [compared for emit]
3+
asset lib/index.js.map 188 bytes [compared for emit]
4+
asset lib/index.js 152 bytes [compared for emit]
5+
asset lib/index.d.ts 84 bytes [compared for emit]
6+
./app.ts 131 bytes [built] [code generated]
7+
./lib/index.ts 119 bytes [built] [code generated]
8+
webpack compiled successfully
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
3+
* This devtool is neither made for production nor for readable output files.
4+
* It uses "eval()" calls to create a separate source file in the browser devtools.
5+
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
6+
* or disable the default devtool with "devtool: false".
7+
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
8+
*/
9+
/******/ (() => { // webpackBootstrap
10+
/******/ "use strict";
11+
/******/ var __webpack_modules__ = ({
12+
13+
/***/ "./app.ts":
14+
/*!****************!*\
15+
!*** ./app.ts ***!
16+
\****************/
17+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
18+
19+
eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nvar lib_1 = __webpack_require__(/*! ./lib */ \"./lib/index.ts\");\n/*transform was here*/ console.log(lib_1.lib.one, lib_1.lib.two, lib_1.lib.three);\n\n\n//# sourceURL=webpack:///./app.ts?");
20+
21+
/***/ }),
22+
23+
/***/ "./lib/index.ts":
24+
/*!**********************!*\
25+
!*** ./lib/index.ts ***!
26+
\**********************/
27+
/***/ ((__unused_webpack_module, exports) => {
28+
29+
eval("\nexports.__esModule = true;\nexports.lib = void 0;\n/*transform was here*/ exports.lib = {\n one: 1,\n two: 2,\n three: 3\n};\n\n\n//# sourceURL=webpack:///./lib/index.ts?");
30+
31+
/***/ })
32+
33+
/******/ });
34+
/************************************************************************/
35+
/******/ // The module cache
36+
/******/ var __webpack_module_cache__ = {};
37+
/******/
38+
/******/ // The require function
39+
/******/ function __webpack_require__(moduleId) {
40+
/******/ // Check if module is in cache
41+
/******/ var cachedModule = __webpack_module_cache__[moduleId];
42+
/******/ if (cachedModule !== undefined) {
43+
/******/ return cachedModule.exports;
44+
/******/ }
45+
/******/ // Create a new module (and put it into the cache)
46+
/******/ var module = __webpack_module_cache__[moduleId] = {
47+
/******/ // no module.id needed
48+
/******/ // no module.loaded needed
49+
/******/ exports: {}
50+
/******/ };
51+
/******/
52+
/******/ // Execute the module function
53+
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
54+
/******/
55+
/******/ // Return the exports of the module
56+
/******/ return module.exports;
57+
/******/ }
58+
/******/
59+
/************************************************************************/
60+
/******/
61+
/******/ // startup
62+
/******/ // Load entry module and return exports
63+
/******/ // This entry module can't be inlined because the eval devtool is used.
64+
/******/ var __webpack_exports__ = __webpack_require__("./app.ts");
65+
/******/
66+
/******/ })()
67+
;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export declare const lib: {
2+
one: number;
3+
two: number;
4+
three: number;
5+
};

test/comparison-tests/projectReferencesWithCustomTransformer/expectedOutput-transpile-4.9/lib/index.js

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/comparison-tests/projectReferencesWithCustomTransformer/expectedOutput-transpile-4.9/lib/index.js.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"program":{"fileNames":["../../../node_modules/typescript/lib/lib.d.ts","../../../node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/typescript/lib/lib.dom.d.ts","../../../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../../node_modules/typescript/lib/lib.scripthost.d.ts","./index.ts"],"fileInfos":["2dc8c927c9c162a773c6bb3cdc4f3286c23f10eedc67414028f9cb5951610f60",{"version":"8730f4bf322026ff5229336391a18bcaa1f94d4f82416c8b2f3954e2ccaae2ba","affectsGlobalScope":true},{"version":"3aafcb693fe5b5c3bd277bd4c3a617b53db474fe498fc5df067c5603b1eebde7","affectsGlobalScope":true},{"version":"7fac8cb5fc820bc2a59ae11ef1c5b38d3832c6d0dfaec5acdb5569137d09a481","affectsGlobalScope":true},{"version":"097a57355ded99c68e6df1b738990448e0bf170e606707df5a7c0481ff2427cd","affectsGlobalScope":true},"28ead8445f54a115ea5f778da4f4f80579fbae42ac6ccc3493626084ed335839"],"options":{"composite":true,"newLine":1,"skipLibCheck":true,"sourceMap":true},"referencedMap":[],"exportedModulesMap":[],"semanticDiagnosticsPerFile":[6,1,3,2,5,4],"emitSignatures":[[6,"82b9c263edd140802d0afbd57d557b2c41db16c5ad9a744bca8c71ad5b10f66f"]],"latestChangedDtsFile":"./index.d.ts"},"version":"4.9.3"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
asset bundle.js 2.64 KiB [emitted] (name: main)
2+
asset lib/tsconfig.tsbuildinfo 1.17 KiB [compared for emit]
3+
asset lib/index.js.map 188 bytes [compared for emit]
4+
asset lib/index.js 152 bytes [compared for emit]
5+
asset lib/index.d.ts 84 bytes [compared for emit]
6+
./app.ts 167 bytes [built] [code generated]
7+
./lib/index.ts 119 bytes [built] [code generated]
8+
webpack compiled successfully
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!*.js.map
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export declare const lib: {
2+
one: number;
3+
two: number;
4+
three: number;
5+
};

test/comparison-tests/projectReferencesWithCustomTransformer/lib/index.js

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/comparison-tests/projectReferencesWithCustomTransformer/lib/index.js.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)