UNPKG

@sap/cds

Version:

SAP Cloud Application Programming Model - CDS for Node.js

107 lines (92 loc) 3.73 kB
// This module monkey patches ./tar.js to work on Windows, where tar does not work properly w/o these changes. exports = module.exports = require('./tar') exports._spawn_tar_c = (dir, args) => { args.push('.') if (Array.isArray(args[0])) return winSpawnTempDir(dir, args) else return winSpawnDir(dir, args) } exports._path = path => { if (!path) return path if (typeof path === 'string') return path.replace('C:', '//localhost/c$').replace(/\\+/g, '/') if (Array.isArray(path)) return path.map(el => exports._path(el)) } const child_process = require('child_process') const spawn = /\btar\b/.test(process.env.DEBUG) ? (cmd, args, options) => { Error.captureStackTrace(spawn,spawn) process.stderr.write(cmd +' ', args.join(' ') +' '+ spawn.stack.slice(7) + '\n') return child_process.spawn(cmd, args, options) } : child_process.spawn const cds = require('../index'), { fs, path, exists, rimraf } = cds.utils const { PassThrough } = require('stream') // spawn tar on Windows, using the cli version const winSpawnDir = (dir, args) => { if (args.some(arg => arg === '-f')) return spawn ('tar', ['c', '-C', exports._path(dir), ...exports._path(args)]) else return spawn ('tar', ['cf', '-', '-C', exports._path(dir), ...exports._path(args)]) } // copy a directory recursively on Windows, using fs.promises async function winCopyDir(src, dest) { if ((await fs.promises.stat(src)).isDirectory()) { const entries = await fs.promises.readdir(src) return Promise.all(entries.map(async each => winCopyDir(path.join(src, each), path.join(dest, each)))) } else { await fs.promises.mkdir(path.dirname(dest), { recursive: true }) return fs.promises.copyFile(src, dest) } } // copy resources containing files and folders to temp dir on Windows // cli tar has a size limit on Windows const winCreateTemp = async (root, resources) => { // Asynchronously copies the entire content from src to dest. const temp = await fs.promises.mkdtemp(`${fs.realpathSync(require('os').tmpdir())}${path.sep}tar-`) for (let resource of resources) { const destination = path.join(temp, path.relative(root, resource)) if ((await fs.promises.stat(resource)).isFile()) { const dirName = path.dirname(destination) if (!await exists(dirName)) { await fs.promises.mkdir(dirName, { recursive: true }) } await fs.promises.copyFile(resource, destination) } else { if (fs.promises.cp) { await fs.promises.cp(resource, destination, { recursive: true }) } else { // node < 16 await winCopyDir(resource, destination) } } } return temp } // spawn tar on Windows, using a temp dir, which is copied from the original dir // cli tar has a size limit on Windows const winSpawnTempDir = (dir, args) => { // Synchronous trick: use a PassThrough as placeholder const stdout = new PassThrough() const stderr = new PassThrough() const c = { stdout, stderr, on: (...a) => { stdout.on(...a); stderr.on(...a); return c }, once: (...a) => { stdout.once(...a); stderr.once(...a); return c }, kill: () => {}, } // async copy, then swap streams/events winCreateTemp(dir, args.shift()).then(tempPath => { const real = winSpawnDir(tempPath, args) real.stdout.pipe(stdout) real.stderr && real.stderr.pipe(stderr) const cleanup = () => exists(tempPath) && rimraf(tempPath) real.on('close', (...ev) => { stdout.emit('close', ...ev) stderr.emit('close', ...ev) cleanup() }) real.on('error', (...ev) => { stdout.emit('error', ...ev) stderr.emit('error', ...ev) cleanup() }) c.kill = (...ev) => real.kill(...ev) }) return c }