UNPKG

@antora/file-publisher

Version:

Publishes the publishable files in the virtual file catalog(s) to the destination(s) specified in the playbook.

79 lines (73 loc) 3.29 kB
'use strict' const expandPath = require('@antora/expand-path-helper') const { promises: fsp } = require('fs') const mkdirp = (path) => fsp.mkdir(path, { recursive: true }) const rmrf = (path) => fsp['rm' in fsp ? 'rm' : 'rmdir'](path, { recursive: true, force: true }) const ospath = require('path') const { pathToFileURL } = require('url') const publishStream = require('./common/publish-stream') const { PassThrough, Writable } = require('stream') const forEach = (write, final) => new Writable({ objectMode: true, write, final }) const { DEFAULT_DEST_FS } = require('../constants.js') function publishToFs (config, files, playbook) { const destDir = config.path || DEFAULT_DEST_FS const absDestDir = expandPath(destDir, { dot: playbook.dir }) return (config.clean ? rmrf(absDestDir).then(() => mkdirp(absDestDir)) : mkdirp(absDestDir)) .then(() => publishStream(fsDest(absDestDir), files)) .then(() => ({ provider: 'fs', path: destDir, resolvedPath: absDestDir, fileUri: pathToFileURL(absDestDir).href })) } function fsDest (toDir, dirs = new Map(), fileRestream = new PassThrough({ objectMode: true })) { return forEach( (file, _, done) => { if (file.isNull()) return done() fileRestream.push(file) const dir = ospath.dirname(file.path) if (dir === '.' || dirs.has(dir)) return done() dirs.set(dir, true) let ancestorDir = ospath.dirname(dir) do { if (ancestorDir === '.' || dirs.get(ancestorDir) === false) break dirs.set(ancestorDir, false) } while ((ancestorDir = ospath.dirname(ancestorDir))) done() }, (done, mkdirs = []) => { dirs.forEach((create, dir) => create && mkdirs.push(mkdirp(ospath.join(toDir, dir)))) Promise.all(mkdirs).then(() => { fileRestream .end() .pipe( forEach((file, _, done_) => { const abspath = ospath.join(toDir, file.path) const { gid, mode, uid } = file.stat || {} fsp.open(abspath, 'w', mode).then(async (fh) => { try { await fh.writeFile(file.contents) const stat = await fh.stat() if (mode && mode !== stat.mode) await fh.chmod(mode) const { gid: fGid, uid: fUid } = stat const newOwner = { gid: fGid, uid: fUid } if (typeof gid === 'number' && gid >= 0 && typeof fGid === 'number' && fGid >= 0 && gid !== fGid) { newOwner.gid = gid newOwner.changed = true } if (typeof uid === 'number' && uid >= 0 && typeof fUid === 'number' && fUid >= 0 && uid !== fUid) { newOwner.uid = uid newOwner.changed = true } if (newOwner.changed) await fh.chown(newOwner.uid, newOwner.gid).catch(() => undefined) fh.close().then(done_, done_) } catch (writeErr) { const bubbleError = () => done_(writeErr) fh.close().then(bubbleError, bubbleError) } }, done_) }) ) .on('error', done) .on('close', done) }, done) } ) } module.exports = publishToFs