From 385a14ecdc174d646d90b8acf73e22eb58e210aa Mon Sep 17 00:00:00 2001 From: Kristian Kraljic Date: Sun, 3 Dec 2023 13:08:21 +0100 Subject: [PATCH] Add minifyPipeline function --- CHANGELOG.md | 5 +++++ README.md | 8 ++++++++ index.js | 4 ++++ package-lock.json | 6 +++--- package.json | 2 +- test.js | 27 ++++++++++++++++++++++----- 6 files changed, 43 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e204136..3c65bfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,13 @@ ## `HEAD` +No changes yet + +# 4.3.0 + - Back to the roots, minimize dependency footprint, similar to how Mathias Bynens handled the library - Minimum required node version to execute tests is now 16.7.0, due to the use of `node:stream/consumers` +- Asynchronous `minifyPipeline` function utilizing Node.js `node:stream/promises` pipeline ## 4.2.0 diff --git a/README.md b/README.md index d6570f1..2d3e623 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,14 @@ fs.createReadStream("sitemap.xml", "utf8") .pipe(process.stdout); ``` +Similar to streams, Node.js 15 introduced an asynchronous [`stream.pipeline` API](https://nodejs.org/docs/latest-v18.x/api/stream.html#streampipelinesource-transforms-destination-options) that with `stream/promises` utilizes promises. This way you can utilize the advantages of the streaming API (namely no file size limit) in conjunction with the convenience of using a modern promise based API: + +```js +import { minifyPipeline as minifyXMLPipeline } from "minify-xml"; + +await minifyXMLPipeline(fs.createReadStream("catalogue.xml", "utf8"), process.stdout, { end: false }); +``` + ## Options You may pass in the following options when calling minify: diff --git a/index.js b/index.js index 8a36037..68c85ca 100644 --- a/index.js +++ b/index.js @@ -311,6 +311,10 @@ export function minifyStream(options) { } }; +import { pipeline } from "node:stream/promises"; +export const minifyPipeline = async (source, destination, options) => + await pipeline(source, minifyStream(options), destination, { end: options?.end }); + export function debug(xml, options) { xml && console.log(`\x1b[90m${xml}\x1b[0m`); diff --git a/package-lock.json b/package-lock.json index ff7e0af..d36a9c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "minify-xml", - "version": "4.2.0", + "version": "4.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "minify-xml", - "version": "4.2.0", + "version": "4.3.0", "license": "(MIT OR Apache-2.0)", "dependencies": { "meow": "^12.1.1", @@ -24,7 +24,7 @@ "tmp-promise": "^3.0.3" }, "engines": { - "node": ">=16.7" + "node": ">=16" } }, "node_modules/@bcoe/v8-coverage": { diff --git a/package.json b/package.json index dbbb209..79463df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minify-xml", - "version": "4.2.0", + "version": "4.3.0", "description": "Fast XML minifier / compressor / uglifier with a command-line", "keywords": [ "XML", diff --git a/test.js b/test.js index 8a12b0d..a4b7851 100644 --- a/test.js +++ b/test.js @@ -6,7 +6,17 @@ import { readFileSync, promises as fs, createReadStream } from "node:fs"; const exists = path => fs.access(path).then(() => true).catch(() => false); import { execa } from "execa"; -import { Readable } from "node:stream"; +import { Readable, Writable } from "node:stream"; +const newWritableStream = () => new Writable({ + write(chunk, encoding, callback) { + (this.chunks ?? (this.chunks = [])).push(Buffer.from(chunk)); + callback(); + }, + final(callback) { + this.emit("end"); + callback(); + } +}); import { text as getStream } from "node:stream/consumers"; import { fileURLToPath } from "node:url"; @@ -17,7 +27,7 @@ const cliPath = path.join(dirname, "cli.js"), cli = async (...options) => import { withFile } from "tmp-promise"; const xml = readFileSync(xmlPath, "utf8"); -import { default as minify, defaultOptions, minifyStream, defaultStreamOptions } from "./index.js"; +import { default as minify, defaultOptions, minifyStream, defaultStreamOptions, minifyPipeline } from "./index.js"; const minifiedXml = minify(xml), minifiedStreamXml = minify(xml, defaultStreamOptions); glob.sync("test/*/").forEach(dir => { @@ -34,10 +44,17 @@ glob.sync("test/*/").forEach(dir => { // minify in.xml with streamOptions.json (or options.json / default options) as a stream and expect stream.xml if (await exists(path.join(dir, "stream.xml"))) { - t.is(await getStream(createReadStream(path.join(dir, "in.xml"), "utf8").pipe(minifyStream(streamOptions))), - await fs.readFile(path.join(dir, "stream.xml"), "utf8")); + const stream = () => createReadStream(path.join(dir, "in.xml"), "utf8"), + expected = await fs.readFile(path.join(dir, "stream.xml"), "utf8"); + t.is(await getStream(stream().pipe(minifyStream(streamOptions))), expected); + + const writeStream = newWritableStream(); + await minifyPipeline(stream(), writeStream, streamOptions); + t.is(Buffer.concat(writeStream.chunks ?? []).toString("utf8"), expected); } else { - t.throws(() => minifyStream(options), { message: /cannot be used with streams/ }); + const expected = { message: /cannot be used with streams/ }; + t.throws(() => minifyStream(streamOptions), expected); + await t.throwsAsync(async () => await minifyPipeline(undefined, undefined, streamOptions), expected) } }); });