diff --git a/lib/pack.js b/lib/pack.js index 09b6ac59..a11f848d 100644 --- a/lib/pack.js +++ b/lib/pack.js @@ -23,7 +23,7 @@ class PackJob { } const MiniPass = require('minipass') -const zlib = require('minizlib') +const zlib = require('../lib/zlib.js') const ReadEntry = require('./read-entry.js') const WriteEntry = require('./write-entry.js') const WriteEntrySync = WriteEntry.Sync diff --git a/lib/parse.js b/lib/parse.js index 63c7ee9c..e3396677 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -28,7 +28,7 @@ const Yallist = require('yallist') const maxMetaEntrySize = 1024 * 1024 const Entry = require('./read-entry.js') const Pax = require('./pax.js') -const zlib = require('minizlib') +const zlib = require('./zlib.js') const gzipHeader = new Buffer([0x1f, 0x8b]) const STATE = Symbol('state') diff --git a/lib/zlib.js b/lib/zlib.js new file mode 100644 index 00000000..6f059e54 --- /dev/null +++ b/lib/zlib.js @@ -0,0 +1,9 @@ +'use strict' +const semver = require('semver') + +module.exports = select(process.version) +module.exports._SELECT_ZLIB = select + +function select (version) { + return semver.gt(version, 'v9.0.0-0') ? require('zlib') : require('minizlib') +} diff --git a/package-lock.json b/package-lock.json index 2b38edd8..3b475211 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2639,6 +2639,11 @@ "integrity": "sha512-aSLEDudu6OoRr/2rU609gRmnYboRLxgDG1z9o2Q0os7236FwvcqIOO8r8U5JUEwivZOhDaKlFO4SbPTJYyBEyQ==", "dev": true }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", diff --git a/package.json b/package.json index b4423281..9e137805 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "minipass": "^2.0.2", "minizlib": "^1.0.3", "mkdirp": "^0.5.0", + "semver": "^5.4.1", "yallist": "^3.0.2" }, "devDependencies": { diff --git a/test/pack.js b/test/pack.js index baec9acb..338104cb 100644 --- a/test/pack.js +++ b/test/pack.js @@ -11,7 +11,7 @@ const tars = path.resolve(fixtures, 'tars') const chmodr = require('chmodr') const Header = require('../lib/header.js') const zlib = require('zlib') -const miniz = require('minizlib') +const miniz = require('../lib/zlib.js') const mutateFS = require('mutate-fs') const MiniPass = require('minipass') process.env.USER = 'isaacs' @@ -457,70 +457,81 @@ t.test('very deep gzip path, sync', t => { pack.pause() pack.resume() - const zipped = pack.read() - t.isa(zipped, Buffer) - const data = zlib.unzipSync(zipped) - const entries = [] - for (var i = 0; i < data.length; i += 512) { - const slice = data.slice(i, i + 512) - const h = new Header(slice) - if (h.nullBlock) - entries.push('null block') - else if (h.cksumValid) - entries.push([h.type, h.path]) - else if (entries[entries.length-1][0] === 'File') - entries[entries.length-1].push(slice.toString().replace(/\0.*$/, '')) - } - - const expect = [ - [ 'Directory', 'dir/' ], - [ 'File', 'dir/x' ], - [ 'Directory', 'long-path/' ], - [ 'Directory', 'long-path/r/' ], - [ 'Directory', 'long-path/r/e/' ], - [ 'Directory', 'long-path/r/e/a/' ], - [ 'Directory', 'long-path/r/e/a/l/' ], - [ 'Directory', 'long-path/r/e/a/l/l/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/' ], - [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/h/' ], - [ 'File', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/h/a.txt', 'short\n' ], - [ 'File', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/h/cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc', '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111' ], - [ 'ExtendedHeader', 'PaxHeader/ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'], - [ 'File', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/h/ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc', '2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222' ], - [ 'ExtendedHeader', 'PaxHeader/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxccccccccccccccccccccccccccccccccccccccc' ], - [ 'File', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/h/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxccccccccccccccccccccccccccccccccccccccccccccccccc', 'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc' ], - [ 'ExtendedHeader', 'PaxHeader/Ω.txt' ], - [ 'File', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/h/Ω.txt', 'Ω' ], - 'null block', - 'null block' - ] + // these have to be declared before the on('data') call because + // on('data') is synchronous with minizlib. We have to use data + // events because zlib is NOT synchronous and it could be either type now. + pack.on('end', onEnd) + pack.on('error', onEnd) + + const chunks = [] + pack.on('data', data => chunks.push(data)) + + function onEnd () { + const zipped = Buffer.concat(chunks) + t.isa(zipped, Buffer) + const data = zlib.unzipSync(zipped) + const entries = [] + for (var i = 0; i < data.length; i += 512) { + const slice = data.slice(i, i + 512) + const h = new Header(slice) + if (h.nullBlock) + entries.push('null block') + else if (h.cksumValid) + entries.push([h.type, h.path]) + else if (entries[entries.length-1][0] === 'File') + entries[entries.length-1].push(slice.toString().replace(/\0.*$/, '')) + } - let ok = true - entries.forEach((entry, i) => { - ok = ok && - t.equal(entry[0], expect[i][0]) && - t.equal(entry[1], expect[i][1]) && - (!entry[2] || t.equal(entry[2], expect[i][2])) - }) + const expect = [ + [ 'Directory', 'dir/' ], + [ 'File', 'dir/x' ], + [ 'Directory', 'long-path/' ], + [ 'Directory', 'long-path/r/' ], + [ 'Directory', 'long-path/r/e/' ], + [ 'Directory', 'long-path/r/e/a/' ], + [ 'Directory', 'long-path/r/e/a/l/' ], + [ 'Directory', 'long-path/r/e/a/l/l/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/' ], + [ 'Directory', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/h/' ], + [ 'File', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/h/a.txt', 'short\n' ], + [ 'File', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/h/cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc', '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111' ], + [ 'ExtendedHeader', 'PaxHeader/ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'], + [ 'File', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/h/ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc', '2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222' ], + [ 'ExtendedHeader', 'PaxHeader/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxccccccccccccccccccccccccccccccccccccccc' ], + [ 'File', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/h/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxccccccccccccccccccccccccccccccccccccccccccccccccc', 'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc' ], + [ 'ExtendedHeader', 'PaxHeader/Ω.txt' ], + [ 'File', 'long-path/r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/h/Ω.txt', 'Ω' ], + 'null block', + 'null block' + ] + + let ok = true + entries.forEach((entry, i) => { + ok = ok && + t.equal(entry[0], expect[i][0]) && + t.equal(entry[1], expect[i][1]) && + (!entry[2] || t.equal(entry[2], expect[i][2])) + }) - // t.match(entries, expect) - t.end() + // t.match(entries, expect) + t.end() + } }) t.test('write after end', t => { diff --git a/test/parse.js b/test/parse.js index cde112b8..1812fe2e 100644 --- a/test/parse.js +++ b/test/parse.js @@ -508,10 +508,15 @@ t.test('truncated gzip input', t => { const p = new Parse({ onwarn: message => warnings.push(message) }) let aborted = false p.on('abort', _ => aborted = true) + p.on('abort', onEnd) + p.on('error', onEnd) + p.on('end', onEnd) p.end(trunc) - t.equal(aborted, true, 'aborted writing') - t.same(warnings, [ 'zlib error: unexpected end of file' ]) - t.end() + function onEnd () { + t.equal(aborted, true, 'aborted writing') + t.same(warnings, [ 'zlib error: unexpected end of file' ]) + t.end() + } }) t.test('just wrong', t => { @@ -519,13 +524,18 @@ t.test('truncated gzip input', t => { const p = new Parse({ onwarn: message => warnings.push(message) }) let aborted = false p.on('abort', _ => aborted = true) + p.on('abort', onEnd) + p.on('error', onEnd) + p.on('end', onEnd) p.write(trunc) p.write(trunc) p.write(tgz.slice(split)) p.end() - t.equal(aborted, true, 'aborted writing') - t.same(warnings, [ 'zlib error: incorrect data check' ]) - t.end() + function onEnd () { + t.equal(aborted, true, 'aborted writing') + t.same(warnings, [ 'zlib error: incorrect data check' ]) + t.end() + } }) t.end() diff --git a/test/select-zlib.js b/test/select-zlib.js new file mode 100644 index 00000000..ca47c7fc --- /dev/null +++ b/test/select-zlib.js @@ -0,0 +1,15 @@ +'use strict' +const zlib = require('zlib') +const minizlib = require('minizlib') +const szlib = require('../lib/zlib.js') +const select = szlib._SELECT_ZLIB +const test = require('tap').test + +test('select the right zlib', t => { + t.is(select('v8.5.0'), minizlib, 'older nodes get faster zlib') + t.is(select('v9.0.0-pre'), zlib, 'newer nodes get compat zlib') + t.is(select('v9.1.0'), zlib, 'non-pre newer nodes get compat zlib') + t.is(select('v10.0.0-pre'), zlib, 'even newer pre-release nodes get compat zlib') + t.is(select('v10.0.0'), zlib, 'even newer nodes get compat zlib') + t.end() +}) diff --git a/test/unpack.js b/test/unpack.js index 2fb77b72..23d1098a 100644 --- a/test/unpack.js +++ b/test/unpack.js @@ -8,7 +8,7 @@ const t = require('tap') const makeTar = require('./make-tar.js') const Header = require('../lib/header.js') -const z = require('minizlib') +const z = require('../lib/zlib.js') const fs = require('fs') const path = require('path') const fixtures = path.resolve(__dirname, 'fixtures')