From e498789209259f113f24c90ac4ba833ddfbde074 Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Sat, 31 Oct 2026 13:57:36 +0000 Subject: [PATCH 1/2] refactor(precompile): minify with webpack's SourceProcessor where webpack has one webpack's `javascript.syntax.printer.load()` is an experimental shim over the one `SourceProcessor` minify pass and is going away; webpack 5.112.x, which has no `SourceProcessor`, still minifies through it. Claude-Session: https://claude.ai/code/session_01Tgxgyb4N35ndP6cC4KcrXC --- .changeset/precompile-source-processor.md | 5 +++ src/precompile.js | 29 +++++++++++---- test/precompile.test.js | 44 +++++++++++++++++++++++ 3 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 .changeset/precompile-source-processor.md diff --git a/.changeset/precompile-source-processor.md b/.changeset/precompile-source-processor.md new file mode 100644 index 0000000..cc3f398 --- /dev/null +++ b/.changeset/precompile-source-processor.md @@ -0,0 +1,5 @@ +--- +"schema-utils": patch +--- + +Minify a precompiled check with webpack's `SourceProcessor` where webpack has one. diff --git a/src/precompile.js b/src/precompile.js index 843c970..8a24685 100644 --- a/src/precompile.js +++ b/src/precompile.js @@ -265,8 +265,8 @@ async function loadFileSchema(uri) { * @returns {Promise} code without error objects, minified */ async function minify(code, esm) { - /** @type {{ minify: (code: string, options: Record) => Promise<{ code?: string }> }} */ - let minifier; + /** @type {(code: string, options: Record) => Promise | string} */ + let minifyWith; // webpack's own minifier, which writes what terser writes without loading it - the packages // shipping a check already have webpack. A bundler that bundles `schema-utils` must neither @@ -280,9 +280,25 @@ async function minify(code, esm) { const self = module; try { - minifier = await (nodeRequire || self.require.bind(self))( + const { syntax } = (nodeRequire || self.require.bind(self))( "webpack", - ).javascript.syntax.printer.load(); + ).javascript; + + if (syntax.SourceProcessor) { + // One `SourceProcessor` run reads, mangles, compresses and prints it. + minifyWith = (source, options) => + new syntax.SourceProcessor().process(source, { + ...options, + sourceType: esm ? "module" : "script", + mode: "minify", + }).code; + } else { + // TODO in the next major release: require the webpack whose `SourceProcessor` minifies and drop `printer.load()` + const minifier = await syntax.printer.load(); + + minifyWith = async (source, options) => + /** @type {string} */ ((await minifier.minify(source, options)).code); + } } catch (err) { throw new Error( "Install webpack 5.112.0 or later to minify a precompiled check", @@ -296,15 +312,14 @@ async function minify(code, esm) { /(=\s*\[?)\{instancePath(?::instancePath\b[^{}]*?)?,schemaPath:"(?:[^"\\]|\\.)*",keyword:"\w+",/g, "$1{", ); - const result = await minifier.minify(stripped, { + + return minifyWith(stripped, { compress: { passes: 3 }, mangle: true, ecma: 2015, module: esm, toplevel: true, }); - - return /** @type {string} */ (result.code); } /** diff --git a/test/precompile.test.js b/test/precompile.test.js index 25a1509..06999da 100644 --- a/test/precompile.test.js +++ b/test/precompile.test.js @@ -212,6 +212,50 @@ describe("precompile", () => { expect(code).not.toMatch(/require\(|module\.exports/); }); + it("should minify with webpack's SourceProcessor where it has one", async () => { + /** @type {Record[]} */ + const runs = []; + + jest.doMock("webpack", () => ({ + javascript: { + syntax: { + SourceProcessor: class { + /** + * @param {string} code code + * @param {Record} options options + * @returns {{ code: string }} the code + */ + process(code, options) { + runs.push(options); + return { code: `/* minified */${code.length}` }; + } + }, + }, + }, + })); + + try { + const code = await precompile(schema, { esm: true, minify: true }); + + expect(code).toMatch(/\/\* minified \*\/\d+$/); + expect(runs).toEqual([ + { + compress: { passes: 3 }, + mangle: true, + ecma: 2015, + module: true, + toplevel: true, + sourceType: "module", + mode: "minify", + }, + ]); + } finally { + jest.dontMock("webpack"); + // A mock once required stays in the registry for the next `doMock`. + jest.resetModules(); + } + }); + it("should ask for webpack when it has no minifier", async () => { // webpack before 5.112.0 jest.doMock("webpack", () => ({})); From d88d6e75e6e57af0520f8feed494ef2e73135ee3 Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Sat, 31 Oct 2026 15:00:03 +0000 Subject: [PATCH 2/2] chore(deps): bump fast-uri to 3.1.9 for npm audit Ports dependabot's #240 so the Lint job's `npm audit` passes. Claude-Session: https://claude.ai/code/session_01Tgxgyb4N35ndP6cC4KcrXC --- package-lock.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index bbf1302..b6d4037 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "schema-utils", - "version": "4.8.0", + "version": "4.8.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "schema-utils", - "version": "4.8.0", + "version": "4.8.1", "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.15", @@ -7677,9 +7677,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", - "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz", + "integrity": "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==", "funding": [ { "type": "github",