Repository navigation
chore: trim unused devDependencies and tooling; upgrade mocha to 12 - #1716
Conversation
Remove `babel`, `babel-polyfill` and `babel-preset-es2016`, plus their knip ignore entries. Nothing at the root uses them: the babel execution tests that reference them are skipped and install their own deps. `babel-preset-react` stays because the babel-es6resolveParent comparison test uses the `react` preset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lint-staged was configured in package.json, but nothing installs a git hook to run it (no husky or similar), so it never ran. Formatting and linting are already checked in CI by the format and lint workflows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Benchmark (Ubuntu)
PR branch = |
Benchmark (macOS)
PR branch = |
The babel execution tests are not skipped (the isNotBabelTest filter is commented out), and they resolve these from the root install: 2.1.4_babel-es2016 and 2.1.4_babel-react use the es2016 preset, and 2.4.1_importCodeSplitting imports babel-polyfill. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Benchmark (Windows)
PR branch = |
semver was only used by the execution test runner to skip tests whose folder name starts with a minimum TypeScript/webpack version. A major.minor.patch comparison covers that, including skipping the `_5.0.0_` tests on webpack 4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The comparison test harness only used glob.sync to list every file under
a directory. A small `listFiles` helper built on
fs.readdirSync({ recursive: true }) does the same: same results and
order, `/`-separated paths on Windows, dotfiles optional, and an empty
list for a missing directory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It was only used by the generate-toc script, and the README's TOC is
already maintained by hand: Prettier has rewritten its bullets, so
re-running the generator would fight Prettier. Also fix the one stale
TOC label ("Declarations" -> "Declaration Files").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two changes keep the comparison test runner working with mocha 12: - Pass `--color` instead of `-c`. Mocha 6 restarted itself with the long form in process.argv, which picocolors uses to turn colors on; mocha 12 passes `-c` through as-is, so the colored output snapshots no longer matched. - Pass `--test-to-run=<name>`. Mocha 12 parses arguments with util.parseArgs and treats a separate value as a spec file. The test file now reads its arguments with util.parseArgs too (non-strict, as mocha's own arguments are mixed in), accepts both forms, and fails clearly when the test name is missing. Also point the allowed mocha commands in .claude/settings.json at bin/mocha.js, as mocha 12 removed bin/_mocha. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It was only used by the execution test runner to run `webpack --bail` for the one test without a karma config (5.6.2_5.0.0_optionsCaching). A small run-webpack.js script now does the same through webpack's Node API: it builds once with `bail: true`, prints the stats, and exits with 1 on errors. `--debug` still works via `node --inspect-brk`. The comparison tests README now suggests `npx webpack-cli` for building a test by hand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Dockerfile was built on node:18 (ts-loader needs Node 22+) with outdated Chrome install steps, and nothing else used it. The test-probe workflow and CI already cover running the tests on Linux. Also remove .dockerignore (only used by this Dockerfile; the devcontainer builds from its own folder) and their .npmignore entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- run-webpack.js accepts the same config shapes as webpack-cli: an object, an array, a function (called with webpack-cli's env) or a Promise. - run-tests.js runs it with execFileSync, so no shell quoting or doubled backslashes on Windows. - listFiles skips a broken symlink, or a file deleted mid-listing, instead of crashing the run, and uses the repo's pathExists helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The folder was removed, so the entry no longer does anything. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Like webpack-cli, print stats using each config's own `stats` setting, with colors only when stdout is a terminal unless the config says otherwise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing passed it: run-tests.js only sends --test-to-run and --save-output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mocha 12 treats a separate value as a spec file, so match the form the comparison test runner now uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Named functions stay as declarations, and the test function createTest returns stays a `function` because it uses mocha's `this.timeout`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`npm run check:comparison-tests` and `npm run check:execution-tests` type-check the harness scripts with tsc. The comparison tests' tsconfig now also covers link-test-lib.js, and the execution tests get a tsconfig covering run-tests.js and run-webpack.js. To pass, run-webpack.js types the webpack callback's stats as Stats or MultiStats, treats missing stats as a failed build, and uses const; and an unused catch binding in the comparison run-tests.js is renamed _err. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run `npm run check:comparison-tests` and `npm run check:execution-tests` after `npm run lint`, so type errors in the harness scripts fail CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ts-loader runs in Node, so check imports with Node's resolution rules rather than a bundler's. `module: node20` implies `moduleResolution: node16`, and emits the same CommonJS: the build output (.js, .d.ts and maps) is byte-identical. Also drop `declarationDir`, which matched `outDir`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The comparison and execution test tsconfigs had identical compiler options, so move them to test/tsconfig.base.json (with lib es2022, to match src). The execution tests check now also covers test/run-tests.js and test/karmaConfig.js, which nothing type-checked before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add `npm run check:benchmark-tests` and run it in the lint workflow, as nothing type-checked the benchmark .mts files. Also drop `esModuleInterop` from their tsconfig, as TypeScript 7 always enables it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Declare the mocha globals the harness uses (describe, it, and the test's `this.timeout` and `done`) in globals.d.ts, as mocha 12 ships no types and this avoids a dependency on @types/mocha. - Check for webpack() returning null (per its types) and fail the test with a clear error. - Type newline.loader.js's `this` as webpack's LoaderContext. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The test-name guard permits empty values that clear the entire staging root, alongside unresolved symlink traversal, erroneous snapshot deletion, and missing config-factory bail semantics.
4 open issues
What changed in this PR
This PR reduces ts-loader’s development-tooling footprint, updates its test harnesses for Mocha 12, and expands type checking in CI.
Changes:
- Trim root dependencies, unused scripts, and obsolete Docker/dev-container setups.
- Upgrade Mocha and replace glob, semver, and webpack-cli usage with local helpers.
- Add harness type checks, share compiler options, and use Node-aware resolution for source imports.
| File | Description |
|---|---|
test/tsconfig.base.json |
Shares JavaScript harness-check options. |
test/execution-tests/tsconfig.json |
Adds execution and shared harness type checking. |
test/execution-tests/run-webpack.js |
Adds a one-shot webpack Node API runner. |
test/execution-tests/run-tests.js |
Replaces semver and invokes the new webpack runner. |
test/comparison-tests/tsconfig.json |
Shares options and expands checked files. |
test/comparison-tests/run-tests.js |
Updates Mocha color and test-selection arguments. |
test/comparison-tests/README.md |
Documents manual builds using npx webpack-cli. |
test/comparison-tests/newline.loader.js |
Types the webpack loader context. |
test/comparison-tests/globals.d.ts |
Declares the Mocha globals used by the harness. |
test/comparison-tests/create-and-execute-test.js |
Replaces glob and adds argument parsing and compiler checks. |
test/benchmark-tests/tsconfig.json |
Removes the redundant interop option. |
src/tsconfig.json |
Uses Node-aware module resolution. |
README.md |
Corrects the declaration-files TOC label. |
package.json |
Trims dependencies/scripts and adds harness checks. |
package-lock.json |
Prunes packages and refreshes tool resolutions. |
knip.json |
Removes exclusions for deleted dependencies. |
Dockerfile |
Deletes the obsolete root test image. |
.npmignore |
Removes exclusions for deleted container files. |
.github/workflows/lint.yml |
Runs all three harness type checks in CI. |
.dockerignore |
Deletes the unused Docker exclusions. |
.devcontainer/Dockerfile |
Removes the outdated development image. |
.devcontainer/devcontainer.json |
Removes the obsolete dev-container configuration. |
.claude/settings.json |
Updates allowed commands for Mocha’s new CLI. |
Review effort: Max
- Reject an empty --test-to-run, which made the whole comparison-tests folder "the test" (and cleared the .test staging folder), and a value starting with `-`, which means parseArgs took the next flag as the name. - listFiles walks directories itself instead of using readdirSync's recursive mode, which follows symlinked folders: a link back to a parent listed the same files over and over. Symlinked folders aren't followed; symlinked files still count. Dot-folders are skipped without being walked, and only symlinks need a stat call. - Only a missing target (ENOENT) or a link loop (ELOOP) makes a symlink "not a file"; other errors are thrown, so --save-output can't delete the snapshot of a file it couldn't read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Pass `argv.bail: true` to a config function, as `webpack --bail` did. - Call function (or Promise) entries in an exported array. - Start the Promise chain first, so a config function that throws goes through the script's own error handling. - Use arrow functions for callbacks, matching create-and-execute-test.js. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AGENTS.md said lint had no separate type-check script, which is no longer true. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>


First small step in trimming devDependencies.
Removed
babel: old v6 placeholder package (it only tells you to installbabel-cli). Nothing at the root requires it, and the babel execution tests that use it list it in their ownpackage.jsonlint-staged: it was configured inpackage.json, but nothing installs a git hook to run it (no husky or similar), so it never ran. CI already runsnpm run format:checkandnpm run lintsemver: only used bytest/execution-tests/run-tests.jsto skip tests whose folder name starts with a minimum TypeScript/webpack version (e.g.2.1.4_,4.7.0_5.0.0_). It's replaced by a smallisLowerVersionhelper that compares major.minor.patch. One deliberate difference: prerelease tags are ignored, so7.1.0-betacounts as equal to7.1.0(semver calls it lower). No test prefix is anywhere near 7.x, so this changes nothing todayglob: only used bytest/comparison-tests/create-and-execute-test.jsto list every file in a directory. It's replaced by a documentedlistFiles(dir, { includeDotFiles })helper that walks directories withfs.readdirSync(dir, { withFileTypes: true }). It returns/-separated paths (also on Windows), keeps glob's sort order, returns an empty list for a missing directory like glob did, and keeps the one call that skipped dotfiles (cleanHashFromOutput) skipping them (without walking into dot-folders). Deliberate differences: symlinked folders aren't followed (Node's recursivereaddirSyncfollows them, so a link back to a parent listed the same files over and over), and a broken symlink is skipped; any other error, such as permissions, is thrown so--save-outputcan't delete a snapshot it couldn't read.globis still installed indirectly viakarma-webpack; we just no longer depend on it directlymarkdown-toc: only used by thegenerate-tocscript, which is also removed. The README table of contents was already maintained by hand: Prettier has rewritten its bullets, so re-running the generator would fight Prettier. Also fixed the one stale TOC entry ("Declarations (.d.ts)" -> "Declaration Files (.d.ts)", matching its heading). This removes 573 lines frompackage-lock.jsonwebpack-cli: only used bytest/execution-tests/run-tests.jsto runwebpack --bailfor the one execution test without a karma config (5.6.2_5.0.0_optionsCaching). A newtest/execution-tests/run-webpack.jsscript does the same through webpack's Node API: like webpack-cli, it accepts a config that exports an object, a function or a Promise, or an array of them; config functions get webpack-cli'senvandargv(includingbail: true), and one that throws is reported through the script's own error handling; it builds once withbail: true, prints the stats using each config's ownstatssetting (colors only when stdout is a terminal, unless the config says otherwise), and exits with 1 on errors.run-tests.jsruns it withexecFileSync, so there's no shell quoting.--debugstill works vianode --inspect-brk.test/comparison-tests/README.mdnow suggestsnpx webpack-clifor building a test by hand. This removes 39 packages fromnode_modulesAlso removed their entries from
knip.json(ignoreDependencies), thelint-stagedconfig block and thegenerate-tocscript frompackage.json.Upgraded
mocha6 → 12. This removes about 1,080 lines frompackage-lock.json. Only the comparison test runner uses mocha, and it needed three changes:test/comparison-tests/run-tests.jspasses--colorinstead of-c. Mocha 6 restarted itself with--colorinprocess.argv, which is how picocolors decides to enable colors; mocha 12 passes-cthrough unchanged, so the colored output snapshots stopped matching--test-to-run=<name>instead of--test-to-run <name>. Mocha 12 parses arguments withutil.parseArgsand treated the separate test name as a spec file, printing a warning for every testtest/comparison-tests/create-and-execute-test.jsnow reads its arguments withutil.parseArgs(non-strict, since mocha's own arguments are mixed in). It accepts both forms of--test-to-run, and fails with a clear error if the test name is missing, empty, or looks like a flag (meaningparseArgstook the next flag as the value) (before, it silently used the node binary's path).claude/settings.json: the allowed mocha commands now point atnode_modules/mocha/bin/mocha.js, as mocha 12 removedbin/_mocha, and pass the test name as--test-to-run=*Other cleanup
Dockerfileand thedocker:build/postdocker:buildscripts. The image was built onnode:18(ts-loader needs Node 22+, mocha 12 needs 20.19+) with outdated Chrome install steps (apt-key,libgconf-2-4), and nothing else used it. The test-probe workflow and CI already cover running the tests on Linux.dockerignore(only used by that Dockerfile) and theDockerfile/.dockerignoreentries in.npmignore. The published package is unchanged (npm pack --dry-runstill lists 44 files).devcontainer/(Dockerfileanddevcontainer.json). It was based on an old Node 14 dev container template, well below the Node 22+ that ts-loader needs. Its.npmignoreentry is gone too--extra-optionargument fromtest/comparison-tests/create-and-execute-test.js. Nothing passed it:run-tests.jsonly sends--test-to-runand--save-outputvar→const/letintest/execution-tests/run-tests.jsandrun-webpack.js, and dropped a commented-out Karmarequiretest/comparison-tests/create-and-execute-test.jsare now arrow functions. Named functions stay as declarations, and the test functioncreateTestreturns stays afunctionbecause it uses mocha'sthis.timeoutnpm run check:comparison-testsandnpm run check:execution-testsscripts type-check the test harness scripts withtsc. The comparison tests'tsconfig.jsonnow also coverslink-test-lib.js, and the execution tests get their owntsconfig.json(run-tests.js,run-webpack.js). To pass,run-webpack.jstypes the webpack callback'sstatsasStatsorMultiStatsand treats missing stats as a failed build. The lint workflow (.github/workflows/lint.yml) now runs both checks afternpm run lint, so type errors in the harness scripts fail CItest/tsconfig.base.json(withlib: es2022, matchingsrc). The execution tests check now also coverstest/run-tests.jsandtest/karmaConfig.js, which nothing type-checked before@ts-ignorecomments from the comparison test harness. A newtest/comparison-tests/globals.d.tsdeclares the mocha globals it uses (describe,it, and the test'sthis.timeoutanddone), since mocha 12 ships no types; this avoids adding@types/mocha.create-and-execute-test.jsnow checks forwebpack()returningnull(as its types allow) and fails the test with a clear error, andnewline.loader.jstypes itsthisas webpack'sLoaderContextAGENTS.mdlists the threecheck:scripts, and no longer says lint has no separate type-check scriptnpm run check:benchmark-tests, also run by the lint workflow, as nothing type-checked the benchmark.mtsfiles. DroppedesModuleInteropfrom their tsconfig, as TypeScript 7 always enables itsrc/tsconfig.jsonnow uses"module": "node20"(which impliesmoduleResolution: node16) instead ofmodule: commonjs+moduleResolution: bundler, so imports are checked with Node's resolution rules, which is where ts-loader runs. Also droppeddeclarationDir, which matchedoutDir. The build output is byte-identical: building the old and new configs side by side gives the same 36 files, including.d.tsand source mapsKept
The first version of this PR also removed
babel-polyfillandbabel-preset-es2016, which broke CI. The babel execution tests do run (theisNotBabelTestfilter is commented out), and some of them load these packages from the root install:2.1.4_babel-es2016and2.1.4_babel-reactuse thees2016preset2.4.1_importCodeSplittingimportsbabel-polyfillBoth are restored with their original version ranges.
babel-core,babel-loader,babel-preset-es2015andbabel-preset-reactstay too, because the comparison and execution tests use them.Testing
npm run knip,npm run format:check,npm run check:benchmark-tests,npm run check:comparison-testsandnpm run check:execution-testspassnpm run execution-tests: all 27 suites pass locally (webpack 5), including after thevar→constchangenpm run comparison-tests: all 57 suites pass locally (with mocha 12)--save-outputonbasicruns cleanly and leaves no changed snapshots--save-output, the old--test-to-run <name>form (used by the allowed Claude commands) and a missing test name (clear error) all behave as expectedrun-webpack.js:5.6.2_5.0.0_optionsCachingpasses. For each config shape (object, array, function, Promise), a working build writes its bundles and exits 0, and a broken build exits 1. The config'sstatssetting is respected:'errors-only'prints nothing on success and still shows errors, an array uses each config's own setting, and colors are off when output isn't a terminal unless the config forces them. That test is skipped on webpack 4, so the webpack 4 CI jobs don't run the scriptrun-webpack.jsconfig functions: an array of functions (includingasync) builds every entry, a function that throws exits 1 through the script's error handling, andargv.bailistrue--test-to-run=and--test-to-run --save-outputboth fail with the clear error messagelistFilesreturns exactly whatglob.syncdid (same files, same order) for all 729 folders undertest/comparison-tests, with and without dotfiles, plus the missing-folder case. A folder with aloop -> .symlink gives each file once; a broken symlink is skipped; a symlink into an unreadable folder throwsEACCES. The Windows path handling is only covered by CI's Windows comparison-test jobisLowerVersiongives the same answers assemver.ltfor every test prefix against the TypeScript and webpack versions CI uses, apart from the prerelease case above_5.0.0_tests are skipped (4.7.0_5.0.0_node16-file-suffix,5.6.2_5.0.0_optionsCaching). On webpack 5, none are skipped🤖 Generated with Claude Code