From b104f8f30e14c7ab57678c62ae7f402151cfd0ca Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sat, 24 May 2025 11:53:12 +0800 Subject: [PATCH 01/11] feat: support xz closes https://github.com/node-modules/compressing/issues/96 All codes gen by cursor --- README.md | 19 +++++++- index.d.ts | 22 +++++++++ index.js | 1 + lib/xz/file_stream.js | 54 +++++++++++++++++++++ lib/xz/index.js | 11 +++++ lib/xz/uncompress_stream.js | 49 +++++++++++++++++++ package.json | 1 + test/fixtures/xx.log.xz | Bin 0 -> 1632 bytes test/xz/file_stream.test.js | 91 ++++++++++++++++++++++++++++++++++++ 9 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 lib/xz/file_stream.js create mode 100644 lib/xz/index.js create mode 100644 lib/xz/uncompress_stream.js create mode 100644 test/fixtures/xx.log.xz create mode 100644 test/xz/file_stream.test.js diff --git a/README.md b/README.md index e416e0c..f0607e1 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Currently supported: - gzip - tgz - zip +- xz ## Install @@ -33,7 +34,7 @@ npm install compressing ### Compress a single file -Use gzip as an example, tar, tgz and zip is same as gzip. +Use gzip as an example, tar, tgz, zip and xz is same as gzip. __promise style__ @@ -235,6 +236,7 @@ Use this API to compress a single file. This is a convenient method, which wraps - tar.compressFile(source, dest, opts) - tgz.compressFile(source, dest, opts) - zip.compressFile(source, dest, opts) +- xz.compressFile(source, dest, opts) Params @@ -268,6 +270,7 @@ Use this API to uncompress a file. This is a convenient method, which wraps Unco - tgz.uncompress(source, dest, opts) - zip.uncompress(source, dest, opts) - gzip.uncompress(source, dest, opts) +- xz.uncompress(source, dest, opts) Params @@ -291,6 +294,7 @@ __Note: If you are not very familiar with streams, just use compressFile() API, - new tar.FileStream(opts) - new tgz.FileStream(opts) - new zip.FileStream(opts) +- new xz.FileStream(opts) Common params: @@ -315,6 +319,10 @@ Zip params: - opts.relativePath {String} - Adds a file from source into the compressed result file as opts.relativePath. Uncompression programs would extract the file from the compressed file as relativePath. If opts.source is a file path, opts.relativePath is optional, otherwise it's required. - opts.yazl {Object} - zip.FileStream compression uses [yazl](https://github.com/thejoshwolfe/yazl), pass this param to control the behavior of yazl. +XZ params: + +- opts.lzma - {Object} xz.FileStream uses lzma-native to compress, pass this param to control the behavior of lzma-native. + ### Stream The readable stream to compress anything as you need. @@ -355,11 +363,20 @@ __Constructor__ - new tar.UncompressStream(opts) - new tgz.UncompressStream(opts) - new zip.UncompressStream(opts) +- new xz.UncompressStream(opts) Common params: - opts.source {String|Buffer|Stream} - source to be uncompressed, could be a file path, buffer, or a readable stream. +Gzip params: + +- opts.zlib - {Object} gzip.UncompressStream uses zlib to uncompress, pass this param to control the behavior of zlib. + +XZ params: + +- opts.lzma - {Object} xz.UncompressStream uses lzma-native to uncompress, pass this param to control the behavior of lzma-native. + __CAUTION for zip.UncompressStream__ Due to the design of the .zip file format, it's impossible to interpret a .zip file without loading all data into memory. diff --git a/index.d.ts b/index.d.ts index e5f8504..fb5cbc4 100644 --- a/index.d.ts +++ b/index.d.ts @@ -199,3 +199,25 @@ export namespace zip { } } + +export namespace xz { + function compressFile(source: sourceType, dest: destType, opts?: any): Promise + function uncompress(source: sourceType, dest: destType, opts?: any): Promise + function decompress(source: sourceType, dest: destType, opts?: any): Promise + + export class FileStream extends ReadStream { + constructor(opts?: { + lzma?: object, + source: sourceType + }); + } + + export class UncompressStream extends WriteStream { + constructor(opts?: { + lzma?: object, + source: sourceType + }); + on(event: string, listener: (...args: any[]) => void): this + on(event: 'error', listener: (err: Error) => void): this + } +} diff --git a/index.js b/index.js index b104632..259e10b 100644 --- a/index.js +++ b/index.js @@ -4,3 +4,4 @@ exports.zip = require('./lib/zip'); exports.gzip = require('./lib/gzip'); exports.tar = require('./lib/tar'); exports.tgz = require('./lib/tgz'); +exports.xz = require('./lib/xz'); diff --git a/lib/xz/file_stream.js b/lib/xz/file_stream.js new file mode 100644 index 0000000..091375a --- /dev/null +++ b/lib/xz/file_stream.js @@ -0,0 +1,54 @@ +'use strict'; + +const fs = require('fs'); +const lzma = require('lzma-native'); +const utils = require('../utils'); +const streamifier = require('streamifier'); +const stream = require('stream'); + +class XzFileStream extends stream.Transform { + constructor(opts) { + opts = opts || {}; + super(opts); + + const sourceType = utils.sourceType(opts.source); + const compressor = lzma.createCompressor(opts.lzma); + + compressor.on('error', err => this.emit('error', err)); + compressor.on('end', () => this.push(null)); + compressor.on('data', chunk => this.push(chunk)); + + if (sourceType === 'file') { + const stream = fs.createReadStream(opts.source, opts.fs); + stream.on('error', err => this.emit('error', err)); + stream.pipe(compressor); + return; + } + + if (sourceType === 'buffer') { + const stream = streamifier.createReadStream(opts.source, opts.streamifier); + stream.on('error', err => this.emit('error', err)); + stream.pipe(compressor); + return; + } + + if (sourceType === 'stream') { + opts.source.on('error', err => this.emit('error', err)); + opts.source.pipe(compressor); + return; + } + + // For streaming input + this.on('pipe', srcStream => { + srcStream.unpipe(srcStream); + srcStream.pipe(compressor); + }); + } + + _transform(chunk, encoding, callback) { + // This will be handled by the compressor stream + callback(); + } +} + +module.exports = XzFileStream; diff --git a/lib/xz/index.js b/lib/xz/index.js new file mode 100644 index 0000000..d6fcea4 --- /dev/null +++ b/lib/xz/index.js @@ -0,0 +1,11 @@ +'use strict'; + +const utils = require('../utils'); +const XzFileStream = require('./file_stream'); +const XzUncompressStream = require('./uncompress_stream'); + +exports.FileStream = XzFileStream; +exports.UncompressStream = XzUncompressStream; +exports.compressFile = utils.makeFileProcessFn(XzFileStream); +exports.uncompress = utils.makeFileProcessFn(XzUncompressStream); +exports.decompress = utils.makeFileProcessFn(XzUncompressStream); diff --git a/lib/xz/uncompress_stream.js b/lib/xz/uncompress_stream.js new file mode 100644 index 0000000..b96f093 --- /dev/null +++ b/lib/xz/uncompress_stream.js @@ -0,0 +1,49 @@ +'use strict'; + +const fs = require('fs'); +const lzma = require('lzma-native'); +const utils = require('../utils'); +const streamifier = require('streamifier'); +const { PassThrough } = require('stream'); + +class XzUncompressStream extends PassThrough { + constructor(opts) { + opts = opts || {}; + super(opts); + + const sourceType = utils.sourceType(opts.source); + const decompressor = lzma.createDecompressor(opts.lzma); + + decompressor.on('error', err => this.emit('error', err)); + decompressor.on('end', () => this.end()); + + // Handle single file decompression + if (sourceType === 'file') { + const stream = fs.createReadStream(opts.source, opts.fs); + stream.on('error', err => this.emit('error', err)); + stream.pipe(decompressor).pipe(this); + return; + } + + if (sourceType === 'buffer') { + const stream = streamifier.createReadStream(opts.source, opts.streamifier); + stream.on('error', err => this.emit('error', err)); + stream.pipe(decompressor).pipe(this); + return; + } + + if (sourceType === 'stream') { + opts.source.on('error', err => this.emit('error', err)); + opts.source.pipe(decompressor).pipe(this); + return; + } + + // For streaming input + this.on('pipe', srcStream => { + srcStream.unpipe(srcStream); + srcStream.pipe(decompressor).pipe(this); + }); + } +} + +module.exports = XzUncompressStream; diff --git a/package.json b/package.json index 8e5a657..a954f02 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "flushwritable": "^1.0.0", "get-ready": "^1.0.0", "iconv-lite": "^0.5.0", + "lzma-native": "^8.0.5", "mkdirp": "^0.5.1", "pump": "^3.0.0", "streamifier": "^0.1.1", diff --git a/test/fixtures/xx.log.xz b/test/fixtures/xx.log.xz new file mode 100644 index 0000000000000000000000000000000000000000..1ae02eabaca03028ed4a782b7c4966a7eff96d00 GIT binary patch literal 1632 zcmV-m2A}!;H+ooF000E$*0e?hz@H4Uhyo!2761SM0001Se5>x@gfIpdT>ubilum*I z)gyUcvpG$OE3V|oO(!mED~c5}2H~0toT-l-u9d#oOX^jPfvuL(Gp+6ghiLO9f(l=) z8!zMr#u57*J5s?t)=gJL!=c`?wT@qyRO;_Y2>))ERkza+K|@3~5kuNXE3 z_nw19rc|FB1@7iWIKt6U!AA=J58ta!R9ob-ce1A{FxTeS$?iBh2_>uL_&VO?A4|X_d|m8IZ5I1$61iHTD-H6nz3;7#V_j5 zB}eh#3s*bqk}p5$IBi~Y8X*TqZuop@_=RJxi8Xse?Nl}~=v+Oc)O)FecZLPQXOi95 z3<vVeqLlM2?lmHEK>g_Nx7e`n!9EWQ8q7|(HV<1t2T zY*o9}1nj&}Hnosh26M>TS;#x#9qwK2d=-;QQ??Ap2x>TbdgFkxVabyiRxty36|?9) zCrfNz+-{c;1zVNpv`Y9ERcdlxYzY~kOyNXlPMsp9D zUdd)LvA{DuWnw1)l#7W1-6Z{+%>N7XYxl4Lj{rWMr}I|CNczxBu9zM=e>!JOKnt;G z%0_Y7+fFL@7|i2`JcV$@6*x!4MxE6D9kUW*ChjJuk`+)7wF)BIQ z7s==(p}bEMzAnIVp7>>0cj+}(q8Q`5FW8993(J#igh!i22yu$L+TRSoVY$(YP) zYljH%M_@o42AMr@DZ#AdncQ@j5&Xt*wWg&vtir6Hj2G@GmY*&dQIwIWHP@)2sjAuD z;LNJhgPL7ULc_l~yJ2#k8)-Oju`M760?4GEiV({;fpKErySgR8lKwPabgH<~g^6p> zklp$x{=^p!`^yG>Or}KkSP3l~8;15^)!C%3S27M)Oe~{cOIE zp+P~ecdF}9QXGmdMmSCI zl|ea_Kc?ZxxhzOZvXTDGY*Nc1TcIt=ijtO06X-o5|=89uVar?n2G;p zwPP8Cs>CX3=`27?4vHx*sz)}X0y8QEgw)Zu&`=V1IdF-w$+U(SWzjhP0002bb { + const sourceFile = path.join(__dirname, '../fixtures/xx.log'); + const xzFile = path.join(__dirname, '../fixtures/xx.log.xz'); + + it('should compress file to xz', done => { + const dest = path.join(__dirname, '../fixtures/xx.log.xz.tmp'); + compressing.xz.compressFile(sourceFile, dest) + .then(() => { + assert(fs.existsSync(dest)); + // 文件大小应该小于原始文件 + assert(fs.statSync(dest).size < fs.statSync(sourceFile).size); + fs.unlinkSync(dest); + done(); + }) + .catch(done); + }); + + it('should decompress xz file to log', done => { + const dest = path.join(__dirname, '../fixtures/xx.log.tmp'); + compressing.xz.uncompress(xzFile, dest) + .then(() => { + assert(fs.existsSync(dest)); + // 内容应该一致 + const raw = fs.readFileSync(sourceFile); + const out = fs.readFileSync(dest); + assert(raw.equals(out)); + fs.unlinkSync(dest); + done(); + }) + .catch(done); + }); + + it('should compress buffer to xz', done => { + const buf = fs.readFileSync(sourceFile); + const dest = path.join(__dirname, '../fixtures/xx.log.xz.tmp'); + compressing.xz.compressFile(buf, dest) + .then(() => { + assert(fs.existsSync(dest)); + fs.unlinkSync(dest); + done(); + }) + .catch(done); + }); + + it('should decompress xz buffer to log', done => { + const buf = fs.readFileSync(xzFile); + const dest = path.join(__dirname, '../fixtures/xx.log.tmp'); + compressing.xz.uncompress(buf, dest) + .then(() => { + assert(fs.existsSync(dest)); + const raw = fs.readFileSync(sourceFile); + const out = fs.readFileSync(dest); + assert(raw.equals(out)); + fs.unlinkSync(dest); + done(); + }) + .catch(done); + }); + + it('should compress stream to xz', done => { + const src = fs.createReadStream(sourceFile); + const dest = path.join(__dirname, '../fixtures/xx.log.xz.tmp'); + compressing.xz.compressFile(src, dest) + .then(() => { + assert(fs.existsSync(dest)); + fs.unlinkSync(dest); + done(); + }) + .catch(done); + }); + + it('should decompress xz stream to log', done => { + const src = fs.createReadStream(xzFile); + const dest = path.join(__dirname, '../fixtures/xx.log.tmp'); + compressing.xz.uncompress(src, dest) + .then(() => { + assert(fs.existsSync(dest)); + const raw = fs.readFileSync(sourceFile); + const out = fs.readFileSync(dest); + assert(raw.equals(out)); + fs.unlinkSync(dest); + done(); + }) + .catch(done); + }); +}); From 58055eacd6caeb26b0e5304540594b9036b64230 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sat, 24 May 2025 11:56:25 +0800 Subject: [PATCH 02/11] f --- lib/xz/file_stream.js | 2 +- lib/xz/index.js | 2 +- lib/xz/uncompress_stream.js | 2 +- test/xz/file_stream.test.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/xz/file_stream.js b/lib/xz/file_stream.js index 091375a..9931a4b 100644 --- a/lib/xz/file_stream.js +++ b/lib/xz/file_stream.js @@ -51,4 +51,4 @@ class XzFileStream extends stream.Transform { } } -module.exports = XzFileStream; +module.exports = XzFileStream; diff --git a/lib/xz/index.js b/lib/xz/index.js index d6fcea4..d888f57 100644 --- a/lib/xz/index.js +++ b/lib/xz/index.js @@ -8,4 +8,4 @@ exports.FileStream = XzFileStream; exports.UncompressStream = XzUncompressStream; exports.compressFile = utils.makeFileProcessFn(XzFileStream); exports.uncompress = utils.makeFileProcessFn(XzUncompressStream); -exports.decompress = utils.makeFileProcessFn(XzUncompressStream); +exports.decompress = utils.makeFileProcessFn(XzUncompressStream); diff --git a/lib/xz/uncompress_stream.js b/lib/xz/uncompress_stream.js index b96f093..cc6a0aa 100644 --- a/lib/xz/uncompress_stream.js +++ b/lib/xz/uncompress_stream.js @@ -46,4 +46,4 @@ class XzUncompressStream extends PassThrough { } } -module.exports = XzUncompressStream; +module.exports = XzUncompressStream; diff --git a/test/xz/file_stream.test.js b/test/xz/file_stream.test.js index 218382c..d44caad 100644 --- a/test/xz/file_stream.test.js +++ b/test/xz/file_stream.test.js @@ -88,4 +88,4 @@ describe('test/xz/file_stream.test.js', () => { }) .catch(done); }); -}); +}); From 9b17e98aabf5e02184736c0ac8ed56d5f9d5815b Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 25 May 2025 10:55:47 +0800 Subject: [PATCH 03/11] Update lib/xz/uncompress_stream.js Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- lib/xz/uncompress_stream.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/xz/uncompress_stream.js b/lib/xz/uncompress_stream.js index cc6a0aa..59094e5 100644 --- a/lib/xz/uncompress_stream.js +++ b/lib/xz/uncompress_stream.js @@ -40,10 +40,11 @@ class XzUncompressStream extends PassThrough { // For streaming input this.on('pipe', srcStream => { - srcStream.unpipe(srcStream); + srcStream.unpipe(this); srcStream.pipe(decompressor).pipe(this); }); } } +} module.exports = XzUncompressStream; From d80454edf87e2e6dfd776735bf2850c116843100 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 25 May 2025 11:07:02 +0800 Subject: [PATCH 04/11] =?UTF-8?q?=E8=AE=A9=20lzma-native=20=E5=8F=98?= =?UTF-8?q?=E6=88=90=E5=8F=AF=E9=80=89=E4=BE=9D=E8=B5=96=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E4=B8=94=E5=9C=A8=20xz=20=E4=BD=BF=E7=94=A8=E7=9A=84=E6=97=B6?= =?UTF-8?q?=E5=80=99=E5=88=A4=E6=96=AD=E4=BE=9D=E8=B5=96=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=AD=98=E5=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 ++++++- lib/xz/index.js | 60 ++++++++++++++++++++++++++++++++----- lib/xz/uncompress_stream.js | 1 - package.json | 4 ++- 4 files changed, 64 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index f0607e1..995ae8a 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Currently supported: - gzip - tgz - zip -- xz +- xz (requires optional dependency `lzma-native`) ## Install @@ -30,6 +30,12 @@ Currently supported: npm install compressing ``` +For xz compression/decompression support, you need to install the optional dependency: + +```bash +npm install lzma-native +``` + ## Usage ### Compress a single file @@ -323,6 +329,8 @@ XZ params: - opts.lzma - {Object} xz.FileStream uses lzma-native to compress, pass this param to control the behavior of lzma-native. +__Note: xz compression/decompression requires the optional dependency `lzma-native`. If you try to use xz features without installing it, you'll get an error asking you to install it.__ + ### Stream The readable stream to compress anything as you need. diff --git a/lib/xz/index.js b/lib/xz/index.js index d888f57..62d4fcb 100644 --- a/lib/xz/index.js +++ b/lib/xz/index.js @@ -1,11 +1,55 @@ 'use strict'; const utils = require('../utils'); -const XzFileStream = require('./file_stream'); -const XzUncompressStream = require('./uncompress_stream'); - -exports.FileStream = XzFileStream; -exports.UncompressStream = XzUncompressStream; -exports.compressFile = utils.makeFileProcessFn(XzFileStream); -exports.uncompress = utils.makeFileProcessFn(XzUncompressStream); -exports.decompress = utils.makeFileProcessFn(XzUncompressStream); + +let XzFileStream; +let XzUncompressStream; + +function checkDependency() { + try { + require('lzma-native'); + return true; + } catch (err) { + return false; + } +} + +function throwIfNoDependency() { + if (!checkDependency()) { + throw new Error('lzma-native is required for xz compression/decompression. Please install it with: npm install lzma-native'); + } +} + +// Lazy load the implementation +function getImplementation() { + if (!XzFileStream) { + throwIfNoDependency(); + XzFileStream = require('./file_stream'); + XzUncompressStream = require('./uncompress_stream'); + } + return { XzFileStream, XzUncompressStream }; +} + +exports.FileStream = function(opts) { + const { XzFileStream } = getImplementation(); + return new XzFileStream(opts); +}; + +exports.UncompressStream = function(opts) { + const { XzUncompressStream } = getImplementation(); + return new XzUncompressStream(opts); +}; + +exports.compressFile = function(source, dest, opts) { + throwIfNoDependency(); + const { XzFileStream } = getImplementation(); + return utils.makeFileProcessFn(XzFileStream)(source, dest, opts); +}; + +exports.uncompress = function(source, dest, opts) { + throwIfNoDependency(); + const { XzUncompressStream } = getImplementation(); + return utils.makeFileProcessFn(XzUncompressStream)(source, dest, opts); +}; + +exports.decompress = exports.uncompress; diff --git a/lib/xz/uncompress_stream.js b/lib/xz/uncompress_stream.js index 59094e5..73ffb6c 100644 --- a/lib/xz/uncompress_stream.js +++ b/lib/xz/uncompress_stream.js @@ -45,6 +45,5 @@ class XzUncompressStream extends PassThrough { }); } } -} module.exports = XzUncompressStream; diff --git a/package.json b/package.json index a954f02..bf1f60d 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "flushwritable": "^1.0.0", "get-ready": "^1.0.0", "iconv-lite": "^0.5.0", - "lzma-native": "^8.0.5", "mkdirp": "^0.5.1", "pump": "^3.0.0", "streamifier": "^0.1.1", @@ -50,6 +49,9 @@ "@eggjs/yauzl": "^2.11.0", "yazl": "^2.4.2" }, + "optionalDependencies": { + "lzma-native": "^8.0.6" + }, "devDependencies": { "@types/mocha": "10", "@types/node": "20", From ee421a670e6846ceeb35791cc821ea8aa92c5dc3 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 25 May 2025 11:07:55 +0800 Subject: [PATCH 05/11] Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 995ae8a..76cd42d 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ npm install lzma-native ### Compress a single file -Use gzip as an example, tar, tgz, zip and xz is same as gzip. +Use gzip as an example; tar, tgz, zip, and xz are the same as gzip. __promise style__ From e8465b5a7a6c4dd9820b9c7707ad623139010ac5 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 25 May 2025 11:11:33 +0800 Subject: [PATCH 06/11] f --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 76cd42d..42472ec 100644 --- a/README.md +++ b/README.md @@ -30,12 +30,6 @@ Currently supported: npm install compressing ``` -For xz compression/decompression support, you need to install the optional dependency: - -```bash -npm install lzma-native -``` - ## Usage ### Compress a single file From 4f0f447faf5ef17dbc1c65fe63d5c56f8cbf5089 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 25 May 2025 11:14:41 +0800 Subject: [PATCH 07/11] Update lib/xz/file_stream.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/xz/file_stream.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xz/file_stream.js b/lib/xz/file_stream.js index 9931a4b..4ea0671 100644 --- a/lib/xz/file_stream.js +++ b/lib/xz/file_stream.js @@ -40,7 +40,7 @@ class XzFileStream extends stream.Transform { // For streaming input this.on('pipe', srcStream => { - srcStream.unpipe(srcStream); + srcStream.unpipe(this); srcStream.pipe(compressor); }); } From 8d9380f3a3d04fce990f5b32036f429d03029238 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 25 May 2025 11:23:19 +0800 Subject: [PATCH 08/11] f --- test/xz/file_stream.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/xz/file_stream.test.js b/test/xz/file_stream.test.js index d44caad..1f4b312 100644 --- a/test/xz/file_stream.test.js +++ b/test/xz/file_stream.test.js @@ -28,7 +28,7 @@ describe('test/xz/file_stream.test.js', () => { // 内容应该一致 const raw = fs.readFileSync(sourceFile); const out = fs.readFileSync(dest); - assert(raw.equals(out)); + assert.deepEqual(out, raw); fs.unlinkSync(dest); done(); }) @@ -55,7 +55,7 @@ describe('test/xz/file_stream.test.js', () => { assert(fs.existsSync(dest)); const raw = fs.readFileSync(sourceFile); const out = fs.readFileSync(dest); - assert(raw.equals(out)); + assert.deepEqual(out, raw); fs.unlinkSync(dest); done(); }) @@ -82,7 +82,7 @@ describe('test/xz/file_stream.test.js', () => { assert(fs.existsSync(dest)); const raw = fs.readFileSync(sourceFile); const out = fs.readFileSync(dest); - assert(raw.equals(out)); + assert.deepEqual(out, raw); fs.unlinkSync(dest); done(); }) From b16735515fef191a81067eacb3d0a18e672caad2 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 25 May 2025 11:26:46 +0800 Subject: [PATCH 09/11] f --- test/xz/file_stream.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/xz/file_stream.test.js b/test/xz/file_stream.test.js index 1f4b312..07f2172 100644 --- a/test/xz/file_stream.test.js +++ b/test/xz/file_stream.test.js @@ -82,7 +82,7 @@ describe('test/xz/file_stream.test.js', () => { assert(fs.existsSync(dest)); const raw = fs.readFileSync(sourceFile); const out = fs.readFileSync(dest); - assert.deepEqual(out, raw); + assert.equal(out.toString(), raw.toString()); fs.unlinkSync(dest); done(); }) From b3cb500810705a0523c1a9c5c457af50b71f5f80 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 25 May 2025 11:40:02 +0800 Subject: [PATCH 10/11] f --- lib/xz/uncompress_stream.js | 6 +++++- test/xz/file_stream.test.js | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/xz/uncompress_stream.js b/lib/xz/uncompress_stream.js index 73ffb6c..8b1f48f 100644 --- a/lib/xz/uncompress_stream.js +++ b/lib/xz/uncompress_stream.js @@ -12,7 +12,11 @@ class XzUncompressStream extends PassThrough { super(opts); const sourceType = utils.sourceType(opts.source); - const decompressor = lzma.createDecompressor(opts.lzma); + // Set text mode to true to handle line endings correctly on Windows + const decompressor = lzma.createDecompressor({ + ...opts.lzma, + textMode: true, + }); decompressor.on('error', err => this.emit('error', err)); decompressor.on('end', () => this.end()); diff --git a/test/xz/file_stream.test.js b/test/xz/file_stream.test.js index 07f2172..0ec55dc 100644 --- a/test/xz/file_stream.test.js +++ b/test/xz/file_stream.test.js @@ -62,6 +62,22 @@ describe('test/xz/file_stream.test.js', () => { .catch(done); }); + it('should compress/decompress utf-8 text to xz', async () => { + const buf = Buffer.from('你好\nhello xz\n'); + const dest = path.join(__dirname, '../fixtures/xx.log.xz.utf8.tmp'); + await compressing.xz.compressFile(buf, dest); + assert(fs.existsSync(dest)); + + const dest2 = path.join(__dirname, '../fixtures/xx.log.utf8.tmp'); + const xzBuf = fs.readFileSync(dest); + await compressing.xz.uncompress(xzBuf, dest2); + const outBuf = fs.readFileSync(dest2); + assert.deepEqual(outBuf.toString(), buf.toString()); + + fs.unlinkSync(dest); + fs.unlinkSync(dest2); + }); + it('should compress stream to xz', done => { const src = fs.createReadStream(sourceFile); const dest = path.join(__dirname, '../fixtures/xx.log.xz.tmp'); From d6a07f001ef601f1f29d6480d70f19c1493b3c13 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 25 May 2025 11:46:37 +0800 Subject: [PATCH 11/11] f --- test/xz/file_stream.test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/xz/file_stream.test.js b/test/xz/file_stream.test.js index 0ec55dc..7d04d17 100644 --- a/test/xz/file_stream.test.js +++ b/test/xz/file_stream.test.js @@ -28,6 +28,7 @@ describe('test/xz/file_stream.test.js', () => { // 内容应该一致 const raw = fs.readFileSync(sourceFile); const out = fs.readFileSync(dest); + assert.equal(out.length, raw.length); assert.deepEqual(out, raw); fs.unlinkSync(dest); done(); @@ -55,6 +56,7 @@ describe('test/xz/file_stream.test.js', () => { assert(fs.existsSync(dest)); const raw = fs.readFileSync(sourceFile); const out = fs.readFileSync(dest); + assert.equal(out.length, raw.length); assert.deepEqual(out, raw); fs.unlinkSync(dest); done(); @@ -63,7 +65,7 @@ describe('test/xz/file_stream.test.js', () => { }); it('should compress/decompress utf-8 text to xz', async () => { - const buf = Buffer.from('你好\nhello xz\n'); + const buf = Buffer.from('你好\nhello xz\nWindows\r\n'); const dest = path.join(__dirname, '../fixtures/xx.log.xz.utf8.tmp'); await compressing.xz.compressFile(buf, dest); assert(fs.existsSync(dest)); @@ -98,6 +100,7 @@ describe('test/xz/file_stream.test.js', () => { assert(fs.existsSync(dest)); const raw = fs.readFileSync(sourceFile); const out = fs.readFileSync(dest); + assert.equal(out.length, raw.length); assert.equal(out.toString(), raw.toString()); fs.unlinkSync(dest); done();