Skip to content

Commit b347bb7

Browse files
chore: trim runtime dependencies — remove enhanced-resolve/semver, swap micromatch → picomatch (#1701)
* chore: remove enhanced-resolve and semver; replace micromatch with picomatch * fix: correctly handle picomatch negation patterns for reportFiles * chore: remove versionGte helper and version check from compilerSetup * refactor: replace two filter calls with single reduce in reportFiles pattern splitting * chore: bump version to 9.6.2 and update CHANGELOG * perf: hoist picomatch pattern compilation outside filter callback in formatErrors * refactor: remove dead else branch in compilerSetup getCompiler * Apply remaining changes * Update changelog for TypeScript version support Update CHANGELOG.md to reflect TypeScript version support changes. * chore: semver devdep * refactor: fiddingl --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: John Reilly <johnny_reilly@hotmail.com>
1 parent 32d82e6 commit b347bb7

6 files changed

Lines changed: 245 additions & 297 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 9.6.2
4+
* [chore: trim runtime dependencies — remove enhanced-resolve/semver, swap micromatch → picomatch](https://github.com/TypeStrong/ts-loader/pull/1701) - thanks @johnnyreilly
5+
6+
Officially ts-loader has supported 3.6.3+ versions of TypeScript. This change means that certain scenarios with older versions of TS will now certainly fail. If anyone is actually using these versions it would be surprising.
7+
38
## 9.6.1
49
* [fix: rspack support](https://github.com/TypeStrong/ts-loader/pull/1699) - thanks @johnnyreilly and @bhollis
510

package.json

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ts-loader",
3-
"version": "9.6.1",
3+
"version": "9.6.2",
44
"description": "TypeScript loader for webpack",
55
"main": "index.js",
66
"types": "dist",
@@ -54,16 +54,13 @@
5454
"homepage": "https://github.com/TypeStrong/ts-loader",
5555
"dependencies": {
5656
"chalk": "^4.1.0",
57-
"enhanced-resolve": "^5.0.0",
58-
"micromatch": "^4.0.0",
59-
"semver": "^7.3.4",
57+
"picomatch": "^4.0.0",
6058
"source-map": "^0.7.4"
6159
},
6260
"devDependencies": {
6361
"@eslint/js": "^10.0.1",
64-
"@types/micromatch": "^4.0.0",
65-
"@types/node": "*",
66-
"@types/semver": "^7.3.4",
62+
"@types/node": "^26.0.0",
63+
"@types/picomatch": "^4.0.0",
6764
"babel": "^6.0.0",
6865
"babel-core": "^6.0.0",
6966
"babel-loader": "^7.0.0",
@@ -74,7 +71,6 @@
7471
"escape-string-regexp": "^2.0.0",
7572
"eslint": "^10.0.0",
7673
"eslint-config-prettier": "^10.0.0",
77-
"eslint-plugin-n": "^17.0.0",
7874
"fs-extra": "^11.0.0",
7975
"glob": "^7.1.1",
8076
"husky": "^8.0.0",
@@ -92,6 +88,7 @@
9288
"mocha": "^6.0.0",
9389
"prettier": "^2.0.5",
9490
"rimraf": "^2.6.2",
91+
"semver": "^7.8.5",
9592
"typescript": "^6.0.2",
9693
"typescript-eslint": "^8.59.4",
9794
"webpack": "^5.74.0",

src/compilerSetup.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import * as semver from 'semver';
21
import type typescript from 'typescript';
32

43
import type { LoaderOptions } from './interfaces';
@@ -25,17 +24,8 @@ export function getCompiler(loaderOptions: LoaderOptions, log: logger.Logger) {
2524
}`;
2625
compilerCompatible = false;
2726
if (loaderOptions.compiler === 'typescript') {
28-
if (
29-
compiler!.version !== undefined &&
30-
semver.gte(compiler!.version, '3.6.3')
31-
) {
32-
// don't log yet in this case, if a tsconfig.json exists we want to combine the message
33-
compilerCompatible = true;
34-
} else {
35-
log.logError(
36-
`${compilerDetailsLogMessage}. This version is incompatible with ts-loader. Please upgrade to the latest version of TypeScript.`
37-
);
38-
}
27+
// don't log yet in this case, if a tsconfig.json exists we want to combine the message
28+
compilerCompatible = true;
3929
} else {
4030
log.logWarning(
4131
`${compilerDetailsLogMessage}. This version may or may not be compatible with ts-loader.`

src/resolver.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import type * as webpack from 'webpack';
22

3-
import { create as _create } from 'enhanced-resolve';
4-
53
export function makeResolver(
64
_options: webpack.WebpackOptionsNormalized
75
): ResolveSync {

src/utils.ts

Lines changed: 58 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Chalk } from 'chalk';
22
import * as fs from 'fs';
3-
import micromatch from 'micromatch';
3+
import picomatch from 'picomatch';
44
import * as path from 'path';
55
import * as webpack from 'webpack';
66
import type typescript from 'typescript';
@@ -38,6 +38,40 @@ function defaultErrorFormatter(error: ErrorInfo, colors: Chalk) {
3838
);
3939
}
4040

41+
/**
42+
* Build a file-matcher from a reportFiles pattern array that replicates
43+
* micromatch semantics: a file must match a positive pattern AND not match
44+
* any negative pattern. Returns null when reportFiles is empty (no filtering).
45+
*/
46+
function makeReportFilesMatcher(
47+
reportFiles: string[],
48+
): ((fileName: string) => boolean) | null {
49+
if (reportFiles.length === 0) {
50+
return null;
51+
}
52+
const { positivePatterns, negativePatterns } = reportFiles.reduce<{
53+
positivePatterns: string[];
54+
negativePatterns: string[];
55+
}>(
56+
(acc, p) => {
57+
if (p.startsWith('!')) {
58+
acc.negativePatterns.push(p.slice(1));
59+
} else {
60+
acc.positivePatterns.push(p);
61+
}
62+
return acc;
63+
},
64+
{ positivePatterns: [], negativePatterns: [] },
65+
);
66+
const matchPos = picomatch(
67+
positivePatterns.length > 0 ? positivePatterns : ['**'],
68+
);
69+
const matchNeg =
70+
negativePatterns.length > 0 ? picomatch(negativePatterns) : null;
71+
return (fileName: string) =>
72+
matchPos(fileName) && !(matchNeg && matchNeg(fileName));
73+
}
74+
4175
/**
4276
* Take TypeScript errors, parse them and format to webpack errors
4377
* Optionally adds a file name
@@ -48,30 +82,21 @@ export function formatErrors(
4882
colors: Chalk,
4983
compiler: typeof typescript,
5084
merge: { file?: string; module?: webpack.Module },
51-
context: string
85+
context: string,
5286
): webpack.WebpackError[] {
87+
const matchesReportFiles = makeReportFilesMatcher(loaderOptions.reportFiles);
88+
5389
return diagnostics === undefined
5490
? []
5591
: diagnostics
5692
.filter(diagnostic => {
5793
if (loaderOptions.ignoreDiagnostics.indexOf(diagnostic.code) !== -1) {
5894
return false;
5995
}
60-
if (
61-
loaderOptions.reportFiles.length > 0 &&
62-
diagnostic.file !== undefined
63-
) {
64-
const relativeFileName = path.relative(
65-
context,
66-
diagnostic.file.fileName
67-
);
68-
const matchResult = micromatch(
69-
[relativeFileName],
70-
loaderOptions.reportFiles
71-
);
72-
if (matchResult.length === 0) {
96+
if (matchesReportFiles !== null &&
97+
diagnostic.file !== undefined &&
98+
!matchesReportFiles(path.relative(context, diagnostic.file.fileName))) {
7399
return false;
74-
}
75100
}
76101
return true;
77102
})
@@ -88,7 +113,7 @@ export function formatErrors(
88113
].toLowerCase() as Severity,
89114
content: compiler.flattenDiagnosticMessageText(
90115
diagnostic.messageText,
91-
constants.EOL
116+
constants.EOL,
92117
),
93118
file: file === undefined ? '' : path.normalize(file.fileName),
94119
line: start === undefined ? 0 : start.line,
@@ -106,7 +131,7 @@ export function formatErrors(
106131
message,
107132
merge.file === undefined ? errorInfo.file : merge.file,
108133
start,
109-
end
134+
end,
110135
);
111136

112137
return Object.assign(error, merge);
@@ -116,7 +141,7 @@ export function formatErrors(
116141
function getFileLocations(
117142
file: typescript.SourceFile,
118143
position: number,
119-
length = 0
144+
length = 0,
120145
) {
121146
const startLC = file.getLineAndCharacterOfPosition(position);
122147
const start: FileLocation = {
@@ -136,7 +161,7 @@ function getFileLocations(
136161

137162
export function fsReadFile(
138163
fileName: string,
139-
encoding: BufferEncoding | undefined = 'utf8'
164+
encoding: BufferEncoding | undefined = 'utf8',
140165
) {
141166
fileName = path.normalize(fileName);
142167
try {
@@ -151,7 +176,7 @@ export function makeError(
151176
message: string,
152177
file: string,
153178
location?: FileLocation,
154-
endLocation?: FileLocation
179+
endLocation?: FileLocation,
155180
): webpack.WebpackError {
156181
if (isWebpack5) {
157182
const error = new webpack.WebpackError(message);
@@ -163,7 +188,7 @@ export function makeError(
163188
error.details = tsLoaderSource(loaderOptions);
164189

165190
return error;
166-
}
191+
}
167192

168193
return {
169194
message,
@@ -185,7 +210,7 @@ interface WebpackSourcePosition {
185210

186211
function makeWebpackLocation(
187212
location: FileLocation,
188-
endLocation?: FileLocation
213+
endLocation?: FileLocation,
189214
) {
190215
const start: WebpackSourcePosition = {
191216
line: location.line,
@@ -205,7 +230,7 @@ export function tsLoaderSource(loaderOptions: LoaderOptions) {
205230
export function appendSuffixIfMatch(
206231
patterns: (RegExp | string)[],
207232
filePath: string,
208-
suffix: string
233+
suffix: string,
209234
): string {
210235
if (patterns.length > 0) {
211236
for (const regexp of patterns) {
@@ -219,7 +244,7 @@ export function appendSuffixIfMatch(
219244

220245
export function appendSuffixesIfMatch(
221246
suffixDict: { [suffix: string]: (RegExp | string)[] },
222-
filePath: string
247+
filePath: string,
223248
): string {
224249
let amendedPath = filePath;
225250
for (const suffix in suffixDict) {
@@ -243,10 +268,10 @@ export function unorderedRemoveItem<T>(array: T[], item: T): boolean {
243268
export function populateDependencyGraph(
244269
resolvedModules: ResolvedModule[],
245270
instance: TSInstance,
246-
containingFile: string
271+
containingFile: string,
247272
) {
248273
resolvedModules = resolvedModules.filter(
249-
mod => mod !== null && mod !== undefined
274+
mod => mod !== null && mod !== undefined,
250275
);
251276
if (resolvedModules.length) {
252277
const containingFileKey = instance.filePathKeyMapper(containingFile);
@@ -268,7 +293,7 @@ export function populateReverseDependencyGraph(instance: TSInstance) {
268293
instance.solutionBuilderHost
269294
? getInputFileNameFromOutput(instance, resolvedFileName) ||
270295
resolvedFileName
271-
: resolvedFileName
296+
: resolvedFileName,
272297
);
273298
let map = reverseDependencyGraph.get(key);
274299
if (!map) {
@@ -287,7 +312,7 @@ export function populateReverseDependencyGraph(instance: TSInstance) {
287312
export function collectAllDependants(
288313
reverseDependencyGraph: ReverseDependencyGraph,
289314
fileName: FilePathKey,
290-
result: Map<FilePathKey, true> = new Map()
315+
result: Map<FilePathKey, true> = new Map(),
291316
): Map<FilePathKey, true> {
292317
result.set(fileName, true);
293318
const dependants = reverseDependencyGraph.get(fileName);
@@ -316,7 +341,8 @@ export function ensureProgram(instance: TSInstance) {
316341
instance.watchHost.updateRootFileNames();
317342
}
318343
if (instance.watchOfFilesAndCompilerOptions) {
319-
instance.builderProgram = instance.watchOfFilesAndCompilerOptions.getProgram();
344+
instance.builderProgram =
345+
instance.watchOfFilesAndCompilerOptions.getProgram();
320346
instance.program = instance.builderProgram.getProgram();
321347
}
322348
instance.hasUnaccountedModifiedFiles = false;
@@ -342,14 +368,14 @@ export function isReferencedFile(instance: TSInstance, filePath: string) {
342368
return (
343369
!!instance.solutionBuilderHost &&
344370
!!instance.solutionBuilderHost.watchedFiles.get(
345-
instance.filePathKeyMapper(filePath)
371+
instance.filePathKeyMapper(filePath),
346372
)
347373
);
348374
}
349375

350376
export function useCaseSensitiveFileNames(
351377
compiler: typeof typescript,
352-
loaderOptions: LoaderOptions
378+
loaderOptions: LoaderOptions,
353379
) {
354380
return loaderOptions.useCaseSensitiveFileNames !== undefined
355381
? loaderOptions.useCaseSensitiveFileNames

0 commit comments

Comments
 (0)