From 7bf5aaad7b64b492aab91c8b0e0abcd0869ed533 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 14 May 2026 18:03:28 +0000 Subject: [PATCH 1/4] test: cover loading an ESM loader via require() on Node.js 22.12+ Node.js enables `require(esm)` by default since 22.12.0, so a `.mjs` loader can now be consumed without the explicit `type: "module"` hint. --- test/runLoaders.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/runLoaders.js b/test/runLoaders.js index e00d923..815a9e1 100644 --- a/test/runLoaders.js +++ b/test/runLoaders.js @@ -738,6 +738,29 @@ describe("runLoaders", () => { ); }); } + + const [nodeMajor, nodeMinor] = process.versions.node.split(".").map(Number); + // `require(esm)` is enabled by default starting from Node.js 22.12.0 + if (nodeMajor > 22 || (nodeMajor === 22 && nodeMinor >= 12)) { + it("should load an esm loader using require()", (done) => { + runLoaders( + { + resource: path.resolve(fixtures, "resource.bin"), + loaders: [path.resolve(fixtures, "esm-loader.mjs")], + }, + (err, result) => { + if (err) return done(err); + result.result.should.be.eql(["resource-esm"]); + result.cacheable.should.be.eql(true); + result.fileDependencies.should.be.eql([ + path.resolve(fixtures, "resource.bin"), + ]); + result.contextDependencies.should.be.eql([]); + done(); + } + ); + }); + } it("should support escaping in resource", (done) => { runLoaders( { From 95b2fc0bf7488af571b24d7602c50f9432cb4e1b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 14 May 2026 18:03:48 +0000 Subject: [PATCH 2/4] chore: sync package-lock.json version with package.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 62e9c57..1509d37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "loader-runner", - "version": "4.3.1", + "version": "4.3.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "loader-runner", - "version": "4.3.1", + "version": "4.3.2", "license": "MIT", "devDependencies": { "@eslint/js": "^9.28.0", From 8adcb60ac9a3841fce526b8ba4836516630eff52 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 14 May 2026 18:12:07 +0000 Subject: [PATCH 3/4] test: cover loading a CommonJS loader via import() --- test/runLoaders.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/runLoaders.js b/test/runLoaders.js index 815a9e1..2547bca 100644 --- a/test/runLoaders.js +++ b/test/runLoaders.js @@ -761,6 +761,32 @@ describe("runLoaders", () => { ); }); } + + if (Number(process.versions.modules) >= 83) { + it("should load a commonjs loader using import()", (done) => { + runLoaders( + { + resource: path.resolve(fixtures, "resource.bin"), + loaders: [ + { + loader: path.resolve(fixtures, "simple-loader.js"), + type: "module", + }, + ], + }, + (err, result) => { + if (err) return done(err); + result.result.should.be.eql(["resource-simple"]); + result.cacheable.should.be.eql(true); + result.fileDependencies.should.be.eql([ + path.resolve(fixtures, "resource.bin"), + ]); + result.contextDependencies.should.be.eql([]); + done(); + } + ); + }); + } it("should support escaping in resource", (done) => { runLoaders( { From 2a83e634c4bda5d201013c84fff489a3e0f801fc Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 14 May 2026 18:22:52 +0000 Subject: [PATCH 4/4] test: cover esm pitch/raw exports and import() error paths Adds fixtures for an ESM loader with a named `pitch` export, an ESM loader exposing `raw = true` alongside `default`, and an ESM loader whose default export is not a function. Tests exercise the `module.pitch` / `module.raw` reads on a real namespace and the `import()` rejection branch in `loadLoader`. --- test/fixtures/esm-invalid-loader.mjs | 1 + test/fixtures/esm-pitching-loader.mjs | 3 + test/fixtures/esm-raw-loader.mjs | 8 +++ test/runLoaders.js | 99 +++++++++++++++++++++++++++ 4 files changed, 111 insertions(+) create mode 100644 test/fixtures/esm-invalid-loader.mjs create mode 100644 test/fixtures/esm-pitching-loader.mjs create mode 100644 test/fixtures/esm-raw-loader.mjs diff --git a/test/fixtures/esm-invalid-loader.mjs b/test/fixtures/esm-invalid-loader.mjs new file mode 100644 index 0000000..9cf3c27 --- /dev/null +++ b/test/fixtures/esm-invalid-loader.mjs @@ -0,0 +1 @@ +export default ""; diff --git a/test/fixtures/esm-pitching-loader.mjs b/test/fixtures/esm-pitching-loader.mjs new file mode 100644 index 0000000..d8d882d --- /dev/null +++ b/test/fixtures/esm-pitching-loader.mjs @@ -0,0 +1,3 @@ +export function pitch(remainingRequest, previousRequest) { + return [remainingRequest, previousRequest].join(":"); +} diff --git a/test/fixtures/esm-raw-loader.mjs b/test/fixtures/esm-raw-loader.mjs new file mode 100644 index 0000000..8bc655a --- /dev/null +++ b/test/fixtures/esm-raw-loader.mjs @@ -0,0 +1,8 @@ +export default function (source) { + return Buffer.from( + source.toString("hex") + source.toString("utf-8"), + "utf-8" + ); +} + +export const raw = true; diff --git a/test/runLoaders.js b/test/runLoaders.js index 2547bca..d6d9946 100644 --- a/test/runLoaders.js +++ b/test/runLoaders.js @@ -786,6 +786,105 @@ describe("runLoaders", () => { } ); }); + + it("should process an esm pitching loader using import()", (done) => { + runLoaders( + { + resource: path.resolve(fixtures, "resource.bin"), + loaders: [ + path.resolve(fixtures, "simple-loader.js"), + { + loader: path.resolve(fixtures, "esm-pitching-loader.mjs"), + type: "module", + }, + path.resolve(fixtures, "simple-async-loader.js"), + ], + }, + (err, result) => { + if (err) return done(err); + result.result.should.be.eql([ + `${path.resolve(fixtures, "simple-async-loader.js")}!${path.resolve( + fixtures, + "resource.bin" + )}:${path.resolve(fixtures, "simple-loader.js")}-simple`, + ]); + result.cacheable.should.be.eql(true); + result.fileDependencies.should.be.eql([]); + result.contextDependencies.should.be.eql([]); + done(); + } + ); + }); + + it("should process an esm raw loader using import()", (done) => { + runLoaders( + { + resource: path.resolve(fixtures, "bom.bin"), + loaders: [ + { + loader: path.resolve(fixtures, "esm-raw-loader.mjs"), + type: "module", + }, + ], + }, + (err, result) => { + if (err) return done(err); + result.result[0].toString("utf8").should.be.eql("efbbbf62c3b66dböm"); + done(); + } + ); + }); + + it("should not return dependencies when an esm loader is not found", (done) => { + runLoaders( + { + resource: path.resolve(fixtures, "resource.bin"), + loaders: [ + { + loader: path.resolve(fixtures, "does-not-exist-loader.mjs"), + type: "module", + }, + ], + }, + (err, result) => { + err.should.be.instanceOf(Error); + result.should.be.eql({ + cacheable: false, + fileDependencies: [], + contextDependencies: [], + missingDependencies: [], + }); + done(); + } + ); + }); + + it("should not return dependencies when an esm loader has an invalid default export", (done) => { + runLoaders( + { + resource: path.resolve(fixtures, "resource.bin"), + loaders: [ + { + loader: path.resolve(fixtures, "esm-invalid-loader.mjs"), + type: "module", + }, + ], + }, + (err, result) => { + err.should.be.instanceOf(Error); + err.message.should.match( + /esm-invalid-loader\.mjs' is not a loader \(must have normal or pitch function\)$/ + ); + result.should.be.eql({ + cacheable: false, + fileDependencies: [], + contextDependencies: [], + missingDependencies: [], + }); + done(); + } + ); + }); } it("should support escaping in resource", (done) => { runLoaders(