UNPKG

@serverless-devs/s-zip

Version:

Serverless Devs Tool ZIP Component

215 lines (185 loc) 5.86 kB
const fs = require('fs-extra') const archiver = require('archiver') const path = require('path') const ignore = require('ignore') const readline = require('readline') const processCwd = process.cwd() const _ = require('lodash') const isWindows = process.platform === 'win32' async function packTo({ codeUri, exclude, include, outputFileName = 'dome.zip', outputFilePath = './.s/.cache' }) { await fs.ensureDir(outputFilePath) if (!(await fs.pathExists(codeUri))) { throw new Error(`CodeUri: ${codeUri} is not exist`) } const fsStat = await fs.stat(codeUri) if (fsStat.isFile() && !(_.isEmpty(exclude) && _.isEmpty(include))) { throw new Error(`${codeUri} is file, and Include/Exclude was not provided`) } const output = fs.createWriteStream(`${outputFilePath}/${outputFileName}`) console.log('Packing ...') const zipArchiver = archiver('zip', { zlib: { level: 9 } }) .on('warning', (err) => { console.warn(err) }) .on('error', (err) => { throw err }) zipArchiver.pipe(output) let count = await zipTo(codeUri, zipArchiver, exclude) if (include) { for (const item of include) { const c = await zipTo(item, zipArchiver) count += c } } return await new Promise((resolve, reject) => { output.on('close', () => { const compressedSize = zipArchiver.pointer() console.log('Package complete.') resolve({ count, compressedSize }) }) try { zipArchiver.finalize() } catch (err) { reject(err) } }) } async function zipTo(codeUri, zipArchiver, exclude) { const asbFilePath = path.resolve(codeUri) const fsStat = await fs.stat(codeUri) // if (fsStat.isFile()) { // const prefix = codeUri.split('.').pop(); // if (zipPrefix.includes(prefix)) { // console.log(`File is ${zipPrefix.join('/')}`); // return false; // } // } let count if (fsStat.isFile()) { const isBootstrap = isBootstrapPath(asbFilePath, asbFilePath, true) zipArchiver.file(asbFilePath, { name: path.basename(codeUri), mode: isBootstrap ? fsStat.mode | 73 : fsStat.mode }) return 1 } let funignore = null if (exclude) { funignore = await generateFunIngore(processCwd, path.join(processCwd, codeUri), exclude) } count = await zipFolder(zipArchiver, codeUri, [], funignore, codeUri, '') return count } async function zipFolder(zipArchiver, folder, folders, funignore, codeUri, prefix = '') { folders.push(folder) const absCodeUri = path.resolve(codeUri) const dir = path.join(...folders) const dirItems = await fs.readdir(dir) const absDir = path.resolve(dir) const relative = path.relative(absCodeUri, absDir) if (!_.isEmpty(relative)) { zipArchiver.append(null, { name: relative, type: 'directory', prefix }) } return ( await Promise.all( dirItems.map(async (f) => { const fPath = path.join(dir, f) let s try { s = await fs.lstat(fPath) } catch (error) { console.log( `Before zip: could not found fPath ${fPath}, absolute fPath is ${path.resolve( fPath )}, exception is ${error}, skiping` ) return 0 } if (funignore && funignore(fPath)) { console.log('file %s is ignored.', fPath) return 0 } const absFilePath = path.resolve(fPath) const relative = path.relative(absCodeUri, absFilePath) const isBootstrap = isBootstrapPath(absFilePath, absCodeUri, false) if (s.size === 1067) { const content = await readLines(fPath) if (_.head(content) === 'XSym' && content.length === 5) { const target = content[3] zipArchiver.symlink(relative, target, { mode: isBootstrap || isWindows ? s.mode | 73 : s.mode }) return 1 } } if (s.isFile() || s.isSymbolicLink()) { zipArchiver.file(fPath, { name: relative, prefix, mode: isBootstrap || isWindows ? s.mode | 73 : s.mode, stats: s // The archiver uses fs.stat by default, and pasing the result of lstat to ensure that the symbolic link is properly packaged }) return 1 } else if (s.isDirectory()) { return await zipFolder(zipArchiver, f, folders.slice(), funignore, codeUri, prefix) } console.error( `Ignore file ${absFilePath}, because it isn't a file, symbolic link or directory` ) return 0 }) ) ).reduce((sum, curr) => sum + curr, 0) } function readLines(fileName) { return new Promise((resolve, reject) => { const lines = [] readline .createInterface({ input: fs.createReadStream(fileName) }) .on('line', (line) => lines.push(line)) .on('close', () => resolve(lines)) .on('error', reject) }) } function isBootstrapPath(absFilePath, absCodeUri, isFile = true) { let absBootstrapDir if (isFile) { absBootstrapDir = path.dirname(absCodeUri) } else { absBootstrapDir = absCodeUri } return path.join(absBootstrapDir, 'bootstrap') === absFilePath } async function generateFunIngore(baseDir, codeUri, exclude) { const absCodeUri = path.resolve(baseDir, codeUri) const absBaseDir = path.resolve(baseDir) const relative = path.relative(absBaseDir, absCodeUri) if (codeUri.startsWith('..') || relative.startsWith('..')) { console.warn(`\t\tFunignore is not supported for your CodeUri: ${codeUri}`) return null } const ig = ignore().add(exclude) return function(f) { const relativePath = path.relative(baseDir, f) if (relativePath === '') { return false } return ig.ignores(relativePath) } } module.exports = { packTo }