| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- 'use strict';
- const fs = require('fs');
- const path = require('path');
- const mkdirp = require('mkdirp');
- const pump = require('pump');
- // file/fileBuffer/stream
- exports.sourceType = source => {
- if (!source) return undefined;
- if (source instanceof Buffer) return 'buffer';
- if (typeof source._read === 'function' || typeof source._transform === 'function') return 'stream';
- if (typeof source !== 'string') {
- const err = new Error('Type is not supported, must be a file path, file buffer, or a readable stream');
- err.name = 'IlligalSourceError';
- throw err;
- }
- return 'file';
- };
- function destType(dest) {
- if (typeof dest._write === 'function' || typeof dest._transform === 'function') return 'stream';
- if (typeof dest !== 'string') {
- const err = new Error('Type is not supported, must be a file path, or a writable stream');
- err.name = 'IlligalDestinationError';
- throw err;
- }
- return 'path';
- }
- exports.destType = destType;
- const illigalEntryError = new Error('Type is not supported, must be a file path, directory path, file buffer, or a readable stream');
- illigalEntryError.name = 'IlligalEntryError';
- // fileOrDir/fileBuffer/stream
- exports.entryType = entry => {
- if (!entry) return;
- if (entry instanceof Buffer) return 'buffer';
- if (typeof entry._read === 'function' || typeof entry._transform === 'function') return 'stream';
- if (typeof entry !== 'string') throw illigalEntryError;
- return 'fileOrDir';
- };
- exports.clone = obj => {
- const newObj = {};
- for (const i in obj) {
- newObj[i] = obj[i];
- }
- return newObj;
- };
- exports.makeFileProcessFn = StreamClass => {
- return (source, dest, opts) => {
- opts = opts || {};
- opts.source = source;
- const destStream = destType(dest) === 'path' ? fs.createWriteStream(dest) : dest;
- const compressStream = new StreamClass(opts);
- return safePipe([ compressStream, destStream ]);
- };
- };
- exports.makeCompressDirFn = StreamClass => {
- return (dir, dest, opts) => {
- const destStream = destType(dest) === 'path' ? fs.createWriteStream(dest) : dest;
- const compressStream = new StreamClass();
- compressStream.addEntry(dir, opts);
- return safePipe([ compressStream, destStream ]);
- };
- };
- exports.makeUncompressFn = StreamClass => {
- return (source, destDir, opts) => {
- opts = opts || {};
- opts.source = source;
- if (destType(destDir) !== 'path') {
- const error = new Error('uncompress destination must be a directory');
- error.name = 'IlligalDestError';
- throw error;
- }
- return new Promise((resolve, reject) => {
- mkdirp(destDir, err => {
- if (err) return reject(err);
- let entryCount = 0;
- let successCount = 0;
- let isFinish = false;
- function done() {
- // resolve when both stream finish and file write finish
- if (isFinish && entryCount === successCount) resolve();
- }
- new StreamClass(opts)
- .on('finish', () => {
- isFinish = true;
- done();
- })
- .on('error', reject)
- .on('entry', (header, stream, next) => {
- stream.on('end', next);
- if (header.type === 'file') {
- const fullpath = path.join(destDir, header.name);
- mkdirp(path.dirname(fullpath), err => {
- if (err) return reject(err);
- entryCount++;
- pump(stream, fs.createWriteStream(fullpath, { mode: header.mode }), err => {
- if (err) return reject(err);
- successCount++;
- done();
- });
- });
- } else { // directory
- mkdirp(path.join(destDir, header.name), err => {
- if (err) return reject(err);
- stream.resume();
- });
- }
- });
- });
- });
- };
- };
- exports.streamToBuffer = stream => {
- return new Promise((resolve, reject) => {
- const chunks = [];
- stream
- .on('readable', () => {
- let chunk;
- while ((chunk = stream.read())) chunks.push(chunk);
- })
- .on('end', () => resolve(Buffer.concat(chunks)))
- .on('error', err => reject(err));
- });
- };
- function safePipe(streams) {
- return new Promise((resolve, reject) => {
- pump(streams[0], streams[1], err => {
- if (err) return reject(err);
- resolve();
- });
- });
- }
- exports.safePipe = safePipe;
- exports.stripFileName = (strip, fileName, type) => {
- // before
- // node/package.json
- // node/lib/index.js
- //
- // when strip 1
- // package.json
- // lib/index.js
- //
- // when strip 2
- // package.json
- // index.js
- if (Buffer.isBuffer(fileName)) fileName = fileName.toString();
- // use / instead of \\
- if (fileName.indexOf('\\') !== -1) fileName = fileName.replace(/\\+/g, '/');
- // fix absolute path
- // /foo => foo
- if (fileName[0] === '/') fileName = fileName.replace(/^\/+/, '');
- let s = fileName.split('/');
- // fix relative path
- // foo/../bar/../../asdf/
- // => asdf/
- if (s.indexOf('..') !== -1) {
- fileName = path.normalize(fileName);
- // https://npm.taobao.org/mirrors/node/latest/docs/api/path.html#path_path_normalize_path
- if (process.platform === 'win32') fileName = fileName.replace(/\\+/g, '/');
- // replace '../' on ../../foo/bar
- fileName = fileName.replace(/(\.\.\/)+/, '');
- if (type === 'directory' && fileName && fileName[fileName.length - 1] !== '/') {
- fileName += '/';
- }
- s = fileName.split('/');
- }
- strip = Math.min(strip, s.length - 1);
- return s.slice(strip).join('/') || '/';
- };
|