UNPKG

aemsync

Version:

The code and content synchronization for Sling / AEM (Adobe Experience Manager).

106 lines (90 loc) 3.04 kB
import AdmZip from 'adm-zip' import fs from 'fs' import os from 'os' import path from 'path' const DEFAULT_ARCHIVE_PATH = path.join(os.tmpdir(), 'aemsync.zip') export default class Zip { constructor (archivePath) { this.zip = archivePath ? new AdmZip(archivePath) : new AdmZip() } // One method to add them all. Clean API is always nice. add (localPathOrBuffer, zipPath) { if (typeof localPathOrBuffer === 'string') { for (const entry of this._getEntriesToAdd(localPathOrBuffer, zipPath)) { this._addIfNotExists(entry.zipPath, entry.buffer) } } else { this._addIfNotExists(zipPath, localPathOrBuffer) } } save (archivePath = DEFAULT_ARCHIVE_PATH) { // TODO: // Technically, the data could be stored to a buffer instead. // The package manager however does not play well with buffers. // Something to think about in future. this.zip.writeZip(archivePath) return archivePath } _addIfNotExists (zipPath, buffer) { if (this.zip.getEntry(zipPath) === null) { this.zip.addFile(zipPath, buffer) } } // Using getLocalFolder() and getLocalFile() methods would have been simpler, // however adm-zip does not handle empty folders properly. // A top-down walk to identify all items makes the handling consistent // for all the cases. _getEntriesToAdd (localPath, zipPath) { const entries = [] // [{ localPath, zipPath, buffer }] const pipeline = [{ localPath, zipPath }] while (pipeline.length) { const current = pipeline.pop() if (this._isFolder(current.localPath)) { // Add folder. entries.push({ localPath: current.localPath, zipPath: current.zipPath + '/', buffer: Buffer.alloc(0) }) // Walk down the tree. for (const entityName of fs.readdirSync(current.localPath)) { pipeline.push({ localPath: current.localPath + '/' + entityName, zipPath: current.zipPath + '/' + entityName }) } } else { // Add file. entries.push({ ...current, buffer: fs.readFileSync(current.localPath) }) } } return entries } _isFolder (filePath) { try { return fs.lstatSync(filePath).isDirectory() } catch (err) { return false } } // // Archive debugging. // inspect () { return { entries: this._getEntries(), filter: this._getFilter() } } _getEntries () { const entries = [] for (const entry of this.zip.getEntries()) { if (entry.entryName.endsWith('.content.xml')) { // Read the resource type. const re = /jcr:primaryType\s*=\s*"([^"]+)"/g const content = this.zip.readAsText(entry.entryName) const type = (re.exec(content) || ['', 'undefined'])[1] entries.push(entry.entryName + '@' + type) } else { entries.push(entry.entryName) } } return entries.sort() } _getFilter () { return this.zip.readAsText('META-INF/vault/filter.xml').split('\n').map(line => line.trim()) } }