UNPKG

@happikitsune/nw-builder

Version:
250 lines (219 loc) 8.98 kB
var fs = require('graceful-fs-extra'); var path = require('path'); var _ = require('lodash'); var plist = require('plist'); var Glob = require('simple-glob'); var temp = require('temp'); var archiver = require('archiver'); var thenify = require('thenify'); var readFile = thenify(fs.readFile); var writeFile = thenify(fs.writeFile); // Automatically track and cleanup files at exit temp.track(); module.exports = { getPackageInfo: function(path) { return new Promise(function(resolve, reject) { fs.readFile(path, function (err, data) { if (err) return reject(err); try { var appPkg = JSON.parse(data); } catch(e) { reject("Invalid package.json: " + e + "\nMake sure the file is encoded as utf-8"); return; } if (!appPkg.name || !appPkg.version) { reject("Please make sure that your project's package.json includes a version and a name value"); } else { resolve(appPkg); } }); }); }, getFileList: function(fileglob) { var self = this, jsonfile, destFiles = [], srcFiles = [], package_path, matches = Glob(fileglob); return new Promise(function(resolve, reject) { if(!matches.length) return reject('No files matching'); matches.forEach(function(file) { var internalFileName = path.normalize(file); if (internalFileName.match('package.json')) { jsonfile = self.closerPathDepth(internalFileName, jsonfile); package_path = path.normalize(jsonfile.split('package.json')[0] || './'); } if(!fs.lstatSync(internalFileName).isDirectory()) { srcFiles.push(internalFileName); } }); if (!jsonfile) { return reject('Could not find a package.json in your src folder'); } srcFiles.forEach(function(file) { destFiles.push({ src: file, dest: file.replace(package_path, '') }); }); resolve({ files: destFiles, json: jsonfile }); }); }, closerPathDepth: function(path1, path2) { if (!path2) { return path1; } var d1 = this.pathDepth(path1), d2 = this.pathDepth(path2); return d1 < d2 ? path1 : path2; }, pathDepth: function(absolutePath) { return absolutePath.split(path.sep).length; }, copyFile: function (src, dest, _event, options) { return new Promise(function(resolve, reject) { options = options || {}; var stats = fs.lstatSync(src); fs.copy(src, dest, options, function (err) { if(err) return reject(err); var retryCount = 0; var existsCallback = function(exists){ if(exists){ fs.chmod(dest, stats.mode, function(err){ // ignore error if (err) { _event.emit('log', 'chmod ' + stats.mode + ' on ' + dest + ' failed after copying, ignoring'); } resolve(); }); } else if (retryCount++ < 2) { // This is antipattern!!! // Callback should be called when the copy is finished!!!! setTimeout(function(){ fs.exists(dest, existsCallback); }, 1000); } else { reject(new Error("Copied file (" + dest + ") doesn't exist in destination after copying")); } }; fs.exists(dest, existsCallback); }); }); }, mergeFiles: function (app, zipfile, chmod) { // we need to pipe the app into the zipfile and chmod it return new Promise(function(resolve, reject) { var zipStream = fs.createReadStream(zipfile), writeStream = fs.createWriteStream(app, {flags:'a'}); zipStream.on('error', reject); writeStream.on('error', reject); writeStream.on('finish', function () { if(chmod) { fs.chmodSync(app, chmod); } resolve(); }); zipStream.pipe(writeStream); }); }, generateZipFile: function (files, _event, platformSpecificManifest, zipOptions) { var destStream = temp.createWriteStream(), archive = archiver('zip', zipOptions || {}); return new Promise(function(resolve, reject) { // Resolve on close destStream.on('close', function () { resolve(destStream.path); }); // Reject on Error archive.on('error', reject); // Add the files var filesBulk = []; files.forEach(function(file){ if(file.dest === 'package.json' && platformSpecificManifest){ archive.append(platformSpecificManifest, {name: 'package.json'}); } else { archive.file(file.src, {name: file.dest}); } }); // Some logs archive.on('entry', function (file) { _event.emit('log', 'Zipping ' + file.name); }); // Pipe the stream archive.pipe(destStream); archive.finalize(); }); }, getPlistOptions: function(parsedParams, custom) { var obj = {}; if(parsedParams.name) { obj.CFBundleName = parsedParams.name; obj.CFBundleDisplayName = parsedParams.name; } if(parsedParams.version) { obj.CFBundleVersion = parsedParams.version; obj.CFBundleShortVersionString = 'Version ' + parsedParams.version; } if(parsedParams.copyright) { obj.NSHumanReadableCopyright = parsedParams.copyright; } return _.merge(obj, custom); }, editPlist: function(plistInput, plistOutput, options) { options = options || {}; // Make sure all required properties are set [ 'CFBundleName', 'CFBundleDisplayName', 'CFBundleVersion', 'CFBundleShortVersionString' ].forEach(function(prop) { if(!options.hasOwnProperty(prop)) { throw new Error('Missing macPlist property \'' + prop + '\''); } }); // Bundle identifier based on package name if(options.CFBundleIdentifier === undefined) { options.CFBundleIdentifier = 'com.nw-builder.' + options.CFBundleName.toLowerCase().replace(/[^a-z\-]/g,''); } // Read the input file return readFile(plistInput, 'utf8') // Parse it .then(plist.parse) // Then overwrite the properties with custom values .then(function(info) { // Keep backwards compatibility and handle aliases Object.keys(options).forEach(function(key) { var value = options[key]; switch(key) { case 'mac_bundle_id': info.CFBundleIdentifier = value; break; case 'mac_document_types': info.CFBundleDocumentTypes = value.map(function(type) { return { CFBundleTypeName: type.name, CFBundleTypeExtensions: type.extensions, CFBundleTypeRole: type.role, LSIsAppleDefaultForType: type.isDefault }; }); break; default: info[key] = value; } }); // Remove some unwanted properties if(!(options.hasOwnProperty('mac_document_types') || options.hasOwnProperty('CFBundleDocumentTypes'))) { info.CFBundleDocumentTypes = []; } if(!options.hasOwnProperty('UTExportedTypeDeclarations')) info.UTExportedTypeDeclarations = []; // Write output file return writeFile(plistOutput, plist.build(info)); }); } };