@sharekey/meteor-desktop
Version:
Build a Meteor's desktop client with hot code push.
740 lines (706 loc) • 114 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _runtime = _interopRequireDefault(require("regenerator-runtime/runtime"));
var _asar = _interopRequireDefault(require("@electron/asar"));
var _assignIn = _interopRequireDefault(require("lodash/assignIn"));
var _lodash = _interopRequireDefault(require("lodash"));
var _installLocal = require("install-local");
var _core = require("@babel/core");
var _crypto = _interopRequireDefault(require("crypto"));
var _del = _interopRequireDefault(require("del"));
var _presetEnv = _interopRequireDefault(require("@babel/preset-env"));
var _fs = _interopRequireDefault(require("fs"));
var _path = _interopRequireDefault(require("path"));
var _shelljs = _interopRequireDefault(require("shelljs"));
var _semver = _interopRequireDefault(require("semver"));
var _terser = _interopRequireDefault(require("terser"));
var _log = _interopRequireDefault(require("./log"));
var _electronAppScaffold = _interopRequireDefault(require("./electronAppScaffold"));
var _dependenciesManager = _interopRequireDefault(require("./dependenciesManager"));
var _binaryModulesDetector = _interopRequireDefault(require("./binaryModulesDetector"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// eslint-disable-next-line no-unused-vars
_shelljs.default.config.fatal = true;
/**
* Represents the .desktop dir scaffold.
* @class
*/
class ElectronApp {
/**
* @param {MeteorDesktop} $ - context
* @constructor
*/
constructor($) {
this.log = new _log.default('electronApp');
this.scaffold = new _electronAppScaffold.default($);
this.depsManager = new _dependenciesManager.default($, this.scaffold.getDefaultPackageJson().dependencies);
this.$ = $;
this.meteorApp = this.$.meteorApp;
this.packageJson = null;
this.version = null;
this.compatibilityVersion = null;
this.deprectatedPlugins = ['meteor-desktop-localstorage'];
}
/**
* Makes an app.asar from the skeleton app.
* @property {Array} excludeFromDel - list of paths to exclude from deleting
* @returns {Promise}
*/
packSkeletonToAsar(excludeFromDel = []) {
this.log.info('packing skeleton app and node_modules to asar archive');
return new Promise(resolve => {
const extract = this.getModulesToExtract();
// We want to pack skeleton app and node_modules together, so we need to temporarily
// move node_modules to app dir.
this.log.debug('moving node_modules to app dir');
_fs.default.renameSync(this.$.env.paths.electronApp.nodeModules, _path.default.join(this.$.env.paths.electronApp.appRoot, 'node_modules'));
let extracted = false;
extracted = this.extractModules(extract);
this.log.debug('packing');
_asar.default.createPackage(this.$.env.paths.electronApp.appRoot, this.$.env.paths.electronApp.appAsar).then(() => {
// Lets move the node_modules back.
this.log.debug('moving node_modules back from app dir');
_shelljs.default.mv(_path.default.join(this.$.env.paths.electronApp.appRoot, 'node_modules'), this.$.env.paths.electronApp.nodeModules);
if (extracted) {
// We need to create a full node modules back. In other words we want
// the extracted modules back.
extract.forEach(module => _shelljs.default.cp('-rf', _path.default.join(this.$.env.paths.electronApp.extractedNodeModules, module), _path.default.join(this.$.env.paths.electronApp.nodeModules, module)));
// Get the .bin back.
if (this.$.utils.exists(this.$.env.paths.electronApp.extractedNodeModulesBin)) {
_shelljs.default.cp(_path.default.join(this.$.env.paths.electronApp.extractedNodeModulesBin, '*'), _path.default.join(this.$.env.paths.electronApp.nodeModules, '.bin'));
}
}
this.log.debug('deleting source files');
const exclude = [this.$.env.paths.electronApp.nodeModules].concat([this.$.env.paths.electronApp.appAsar, this.$.env.paths.electronApp.packageJson], excludeFromDel);
_del.default.sync([`${this.$.env.paths.electronApp.root}${_path.default.sep}*`].concat(exclude.map(pathToExclude => `!${pathToExclude}`)), {
force: true
});
resolve();
});
});
}
/**
* Moves specified node modules to a separate directory.
* @param {Array} extract
* @returns {boolean}
*/
extractModules(extract) {
const ext = ['.js', '.bat', '.sh', '.cmd', ''];
if (extract.length > 0) {
if (this.$.utils.exists(this.$.env.paths.electronApp.extractedNodeModules)) {
_shelljs.default.rm('-rf', this.$.env.paths.electronApp.extractedNodeModules);
}
_fs.default.mkdirSync(this.$.env.paths.electronApp.extractedNodeModules);
_fs.default.mkdirSync(this.$.env.paths.electronApp.extractedNodeModulesBin);
extract.forEach(module => {
_fs.default.renameSync(_path.default.join(this.$.env.paths.electronApp.appRoot, 'node_modules', module), _path.default.join(this.$.env.paths.electronApp.extractedNodeModules, module));
// Move bins.
this.extractBin(module, ext);
});
return true;
}
return false;
}
/**
* Extracts the bin files associated with a certain node modules.
*
* @param module
* @param ext
*/
extractBin(module, ext) {
let packageJson;
try {
packageJson = JSON.parse(_fs.default.readFileSync(_path.default.join(this.$.env.paths.electronApp.extractedNodeModules, module, 'package.json'), 'utf8'));
} catch (e) {
packageJson = {};
}
const bins = 'bin' in packageJson && typeof packageJson.bin === 'object' ? Object.keys(packageJson.bin) : [];
if (bins.length > 0) {
bins.forEach(bin => {
ext.forEach(extension => {
const binFilePath = _path.default.join(this.$.env.paths.electronApp.appRoot, 'node_modules', '.bin', `${bin}${extension}`);
if (this.$.utils.exists(binFilePath) || this.$.utils.symlinkExists(binFilePath)) {
_fs.default.renameSync(binFilePath, _path.default.join(this.$.env.paths.electronApp.extractedNodeModulesBin, `${bin}${extension}`));
}
});
});
}
}
/**
* Merges the `extract` field with automatically detected modules.
*/
getModulesToExtract() {
const binaryModulesDetector = new _binaryModulesDetector.default(this.$.env.paths.electronApp.nodeModules);
const toBeExtracted = binaryModulesDetector.detect();
let {
extract
} = this.$.desktop.getSettings();
if (!Array.isArray(extract)) {
extract = [];
}
const merge = {};
toBeExtracted.concat(extract).forEach(module => {
merge[module] = true;
});
extract = Object.keys(merge);
if (extract.length > 0) {
this.log.verbose(`resultant modules to extract list is: ${extract.join(', ')}`);
}
return extract;
}
/**
* Calculates a md5 from all dependencies.
*/
calculateCompatibilityVersion() {
this.log.verbose('calculating compatibility version');
const settings = this.$.desktop.getSettings();
if ('desktopHCPCompatibilityVersion' in settings) {
this.compatibilityVersion = `${settings.desktopHCPCompatibilityVersion}`;
this.log.warn(`compatibility version overridden to ${this.compatibilityVersion}`);
return;
}
const md5 = _crypto.default.createHash('md5');
let dependencies = this.depsManager.getDependencies();
const dependenciesSorted = Object.keys(dependencies).sort();
dependencies = dependenciesSorted.map(dependency => `${dependency}:${dependencies[dependency]}`);
const mainCompatibilityVersion = this.$.getVersion().split('.');
this.log.debug('meteor-desktop compatibility version is ', `${mainCompatibilityVersion[0]}`);
dependencies.push(`meteor-desktop:${mainCompatibilityVersion[0]}`);
const desktopCompatibilityVersion = settings.version.split('.')[0];
this.log.debug('.desktop compatibility version is ', desktopCompatibilityVersion);
dependencies.push(`desktop-app:${desktopCompatibilityVersion}`);
if (process.env.METEOR_DESKTOP_DEBUG_DESKTOP_COMPATIBILITY_VERSION || process.env.METEOR_DESKTOP_DEBUG) {
this.log.debug(`compatibility version calculated from ${JSON.stringify(dependencies)}`);
}
md5.update(JSON.stringify(dependencies));
this.compatibilityVersion = md5.digest('hex');
}
async init() {
try {
await this.$.electron.init();
await this.$.electronBuilder.init();
} catch (e) {
this.log.warn('error occurred while initialising electron and electron-builder integration', e);
process.exit(1);
}
}
/**
* Runs all necessary tasks to build the desktopified app.
*/
async build(run = false) {
// TODO: refactor to a task runner
this.log.info('scaffolding');
if (!this.$.desktop.check()) {
if (!this.$.env.options.scaffold) {
this.log.error('seems that you do not have a .desktop dir in your project or it is' + ' corrupted. Run \'npm run desktop -- init\' to get a new one.');
// Do not fail, so that npm will not print his error stuff to console.
process.exit(0);
} else {
this.$.desktop.scaffold();
this.$.meteorApp.updateGitIgnore();
}
}
await this.init();
try {
this.$.meteorApp.updateGitIgnore();
} catch (e) {
this.log.warn(`error occurred while adding ${this.$.env.paths.electronApp.rootName}` + 'to .gitignore: ', e);
}
try {
await this.$.meteorApp.removeDeprecatedPackages();
} catch (e) {
this.log.error('error while removing deprecated packages: ', e);
process.exit(1);
}
try {
await this.$.meteorApp.ensureDesktopHCPPackages();
} catch (e) {
this.log.error('error while checking for required packages: ', e);
process.exit(1);
}
try {
await this.scaffold.make();
} catch (e) {
this.log.error('error while scaffolding: ', e);
process.exit(1);
}
try {
const fileName = '.npmrc';
const dirName = '.meteor/desktop-build';
if (_fs.default.existsSync(dirName) && _fs.default.existsSync(fileName)) {
_fs.default.copyFileSync(fileName, `${dirName}/${fileName}`);
}
} catch (e) {
this.log.warn('error while copying .npmrc', e);
}
try {
await this.exposeElectronModules();
} catch (e) {
this.log.error('error while exposing electron modules: ', e);
process.exit(1);
}
try {
this.updatePackageJsonFields();
} catch (e) {
this.log.error('error while updating package.json: ', e);
}
try {
this.updateDependenciesList();
} catch (e) {
this.log.error('error while merging dependencies list: ', e);
}
try {
this.calculateCompatibilityVersion();
} catch (e) {
this.log.error('error while calculating compatibility version: ', e);
process.exit(1);
}
try {
await this.handleTemporaryNodeModules();
} catch (e) {
this.log.error('error occurred while handling temporary node_modules: ', e);
process.exit(1);
}
let nodeModulesRemoved;
try {
nodeModulesRemoved = await this.handleStateOfNodeModules();
} catch (e) {
this.log.error('error occurred while clearing node_modules: ', e);
process.exit(1);
}
try {
await this.rebuildDeps(true);
} catch (e) {
this.log.error('error occurred while installing node_modules: ', e);
process.exit(1);
}
if (!nodeModulesRemoved) {
try {
await this.rebuildDeps();
} catch (e) {
this.log.error('error occurred while rebuilding native node modules: ', e);
process.exit(1);
}
}
try {
await this.linkNpmPackages();
} catch (e) {
this.log.error(`linking packages failed: ${e}`);
process.exit(1);
}
try {
await this.installLocalNodeModules();
} catch (e) {
this.log.error('error occurred while installing local node modules: ', e);
process.exit(1);
}
try {
await this.ensureMeteorDependencies();
} catch (e) {
this.log.error('error occurred while ensuring meteor dependencies are installed: ', e);
process.exit(1);
}
if (this.$.env.isProductionBuild()) {
try {
await this.packSkeletonToAsar();
} catch (e) {
this.log.error('error while packing skeleton to asar: ', e);
process.exit(1);
}
}
// TODO: find a way to avoid copying .desktop to a temp location
try {
this.copyDesktopToDesktopTemp();
} catch (e) {
this.log.error('error while copying .desktop to a temporary location: ', e);
process.exit(1);
}
try {
await this.updateSettingsJsonFields();
} catch (e) {
this.log.error('error while updating settings.json: ', e);
process.exit(1);
}
try {
await this.excludeFilesFromArchive();
} catch (e) {
this.log.error('error while excluding files from packing to asar: ', e);
process.exit(1);
}
try {
await this.transpileAndMinify();
} catch (e) {
this.log.error('error while transpiling or minifying: ', e);
}
try {
await this.packDesktopToAsar();
} catch (e) {
this.log.error('error occurred while packing .desktop to asar: ', e);
process.exit(1);
}
try {
await this.getMeteorClientBuild();
} catch (e) {
this.log.error('error occurred during getting meteor mobile build: ', e);
}
if (run) {
this.log.info('running');
this.$.electron.run();
} else {
this.log.info('built');
}
}
/**
* Copies the `exposedModules` setting from `settings.json` into `preload.js` modifying its code
* so that the script will have it hardcoded.
*/
exposeElectronModules() {
const {
exposedModules
} = this.$.desktop.getSettings();
if (exposedModules && Array.isArray(exposedModules) && exposedModules.length > 0) {
let preload = _fs.default.readFileSync(this.$.env.paths.electronApp.preload, 'utf8');
const modules = this.$.desktop.getSettings().exposedModules.reduce(
// eslint-disable-next-line no-return-assign,no-param-reassign
(prev, module) => (prev += `'${module}', `, prev), '');
preload = preload.replace('const exposedModules = [', `const exposedModules = [${modules}`);
_fs.default.writeFileSync(this.$.env.paths.electronApp.preload, preload);
}
}
/**
* Ensures all required dependencies are added to the Meteor project.
* @returns {Promise.<void>}
*/
async ensureMeteorDependencies() {
let packages = [];
const packagesWithVersion = [];
let plugins = 'plugins [';
Object.keys(this.$.desktop.getDependencies().plugins).forEach(plugin => {
// Read package.json of the plugin.
const packageJson = JSON.parse(_fs.default.readFileSync(_path.default.join(this.$.env.paths.electronApp.nodeModules, plugin, 'package.json'), 'utf8'));
if ('meteorDependencies' in packageJson && typeof packageJson.meteorDependencies === 'object') {
plugins += `${plugin}, `;
packages.unshift(...Object.keys(packageJson.meteorDependencies));
packagesWithVersion.unshift(...packages.map(packageName => {
if (packageJson.meteorDependencies[packageName] === '@version') {
return `${packageName}@${packageJson.version}`;
}
return `${packageName}@${packageJson.meteorDependencies[packageName]}`;
}));
}
});
const packagesCount = packages.length;
packages = packages.filter(value => !this.deprectatedPlugins.includes(value));
if (packagesCount !== packages.length) {
this.log.warn('you have some deprecated meteor desktop plugins in your settings, please remove ' + `them (deprecated plugins: ${this.deprectatedPlugins.join(', ')})`);
}
if (packages.length > 0) {
plugins = `${plugins.substr(0, plugins.length - 2)}]`;
try {
await this.$.meteorApp.meteorManager.ensurePackages(packages, packagesWithVersion, plugins);
} catch (e) {
throw new Error(e);
}
}
}
/**
* Builds meteor app.
*/
async getMeteorClientBuild() {
await this.$.meteorApp.build();
}
/**
* Removes node_modules if needed.
* @returns {Promise<void>}
*/
async handleStateOfNodeModules() {
if (this.$.env.isProductionBuild() || this.$.env.options.ia32) {
if (!this.$.env.isProductionBuild()) {
this.log.info('clearing node_modules because we need to have it clear for ia32 rebuild');
} else {
this.log.info('clearing node_modules because this is a production build');
}
try {
await this.$.utils.rmWithRetries('-rf', this.$.env.paths.electronApp.nodeModules);
} catch (e) {
throw new Error(e);
}
return true;
}
return false;
}
/**
* If there is a temporary node_modules folder and no node_modules folder, we will
* restore it, as it might be a leftover from an interrupted flow.
* @returns {Promise<void>}
*/
async handleTemporaryNodeModules() {
if (this.$.utils.exists(this.$.env.paths.electronApp.tmpNodeModules)) {
if (!this.$.utils.exists(this.$.env.paths.electronApp.nodeModules)) {
this.log.debug('moving temp node_modules back');
_shelljs.default.mv(this.$.env.paths.electronApp.tmpNodeModules, this.$.env.paths.electronApp.nodeModules);
} else {
// If there is a node_modules folder, we should clear the temporary one.
this.log.debug('clearing temp node_modules because new one is already created');
try {
await this.$.utils.rmWithRetries('-rf', this.$.env.paths.electronApp.tmpNodeModules);
} catch (e) {
throw new Error(e);
}
}
}
}
/**
* Runs npm link for every package specified in settings.json->linkPackages.
*/
async linkNpmPackages() {
if (this.$.env.isProductionBuild()) {
return;
}
const settings = this.$.desktop.getSettings();
const promises = [];
if ('linkPackages' in this.$.desktop.getSettings()) {
if (Array.isArray(settings.linkPackages)) {
settings.linkPackages.forEach(packageName => promises.push(this.$.meteorApp.runNpm(['link', packageName], undefined, this.$.env.paths.electronApp.root)));
}
}
await Promise.all(promises);
}
/**
* Runs npm in the electron app to get the dependencies installed.
* @returns {Promise}
*/
async ensureDeps() {
this.log.info('installing dependencies');
if (this.$.utils.exists(this.$.env.paths.electronApp.nodeModules)) {
this.log.debug('running npm prune to wipe unneeded dependencies');
try {
await this.runNpm(['prune']);
} catch (e) {
throw new Error(e);
}
}
try {
await this.runNpm(['install'], this.$.env.stdio);
} catch (e) {
throw new Error(e);
}
}
/**
* Warns if plugins version are outdated in compare to the newest scaffold.
* @param {Object} pluginsVersions - current plugins versions from settings.json
*/
checkPluginsVersion(pluginsVersions) {
const settingsJson = JSON.parse(_fs.default.readFileSync(_path.default.join(this.$.env.paths.scaffold, 'settings.json')));
const scaffoldPluginsVersion = this.$.desktop.getDependencies(settingsJson, false).plugins;
Object.keys(pluginsVersions).forEach(pluginName => {
if (pluginName in scaffoldPluginsVersion && scaffoldPluginsVersion[pluginName] !== pluginsVersions[pluginName] && _semver.default.lt(pluginsVersions[pluginName], scaffoldPluginsVersion[pluginName])) {
this.log.warn(`you are using outdated version ${pluginsVersions[pluginName]} of ` + `${pluginName}, the suggested version to use is ` + `${scaffoldPluginsVersion[pluginName]}`);
}
});
}
/**
* Merges core dependency list with the dependencies from .desktop.
*/
updateDependenciesList() {
this.log.info('updating list of package.json\'s dependencies');
const desktopDependencies = this.$.desktop.getDependencies();
this.checkPluginsVersion(desktopDependencies.plugins);
this.log.debug('merging settings.json[dependencies]');
this.depsManager.mergeDependencies('settings.json[dependencies]', desktopDependencies.fromSettings);
this.log.debug('merging settings.json[plugins]');
this.depsManager.mergeDependencies('settings.json[plugins]', desktopDependencies.plugins);
this.log.debug('merging dependencies from modules');
Object.keys(desktopDependencies.modules).forEach(module => this.depsManager.mergeDependencies(`module[${module}]`, desktopDependencies.modules[module]));
this.packageJson.dependencies = this.depsManager.getRemoteDependencies();
this.packageJson.localDependencies = this.depsManager.getLocalDependencies();
this.log.debug('writing updated package.json');
_fs.default.writeFileSync(this.$.env.paths.electronApp.packageJson, JSON.stringify(this.packageJson, null, 2));
}
/**
* Install node modules from local paths using local-install.
*
* @param {string} arch
* @returns {Promise}
*/
installLocalNodeModules(arch = this.$.env.options.ia32 || process.arch === 'ia32' ? 'ia32' : 'x64') {
const localDependencies = _lodash.default.values(this.packageJson.localDependencies);
if (localDependencies.length === 0) {
return Promise.resolve();
}
this.log.info('installing local node modules');
const lastRebuild = this.$.electronBuilder.prepareLastRebuildObject(arch);
const env = this.$.electronBuilder.getGypEnv(lastRebuild.frameworkInfo, lastRebuild.platform, lastRebuild.arch);
const installer = new _installLocal.LocalInstaller({
[this.$.env.paths.electronApp.root]: localDependencies
}, {
npmEnv: env
});
(0, _installLocal.progress)(installer);
return installer.install();
}
/**
* Rebuild binary dependencies against Electron's node headers.
* @returns {Promise}
*/
rebuildDeps(install = false) {
if (install) {
this.log.info('issuing node_modules install from electron-builder');
} else {
this.log.info('issuing native modules rebuild from electron-builder');
}
const arch = this.$.env.options.ia32 || process.arch === 'ia32' ? 'ia32' : 'x64';
if (this.$.env.options.ia32) {
this.log.verbose('forcing rebuild for 32bit');
} else {
this.log.verbose(`rebuilding for ${arch}`);
}
return this.$.electronBuilder.installOrRebuild(arch, undefined, install);
}
/**
* Update package.json fields accordingly to what is set in settings.json.
*
* packageJson.name = settings.projectName
* packageJson.version = settings.version
* packageJson.* = settings.packageJsonFields
*/
updatePackageJsonFields() {
this.log.verbose('updating package.json fields');
const settings = this.$.desktop.getSettings();
/** @type {desktopSettings} */
const packageJson = this.scaffold.getDefaultPackageJson();
packageJson.version = settings.version;
if ('packageJsonFields' in settings) {
(0, _assignIn.default)(packageJson, settings.packageJsonFields);
}
(0, _assignIn.default)(packageJson, {
name: settings.projectName
});
this.log.debug('writing updated package.json');
_fs.default.writeFileSync(this.$.env.paths.electronApp.packageJson, JSON.stringify(packageJson, null, 4));
this.packageJson = packageJson;
}
/**
* Updates settings.json with env (prod/dev) information and versions.
*/
async updateSettingsJsonFields() {
this.log.debug('updating settings.json fields');
const settings = this.$.desktop.getSettings();
// Save versions.
settings.compatibilityVersion = this.compatibilityVersion;
// Pass information about build type to the settings.json.
settings.env = this.$.env.isProductionBuild() ? 'prod' : 'dev';
const version = await this.$.desktop.getHashVersion();
settings.desktopVersion = `${version}_${settings.env}`;
settings.meteorDesktopVersion = this.$.getVersion();
if (this.$.env.options.prodDebug) {
settings.prodDebug = true;
}
_fs.default.writeFileSync(this.$.env.paths.desktopTmp.settings, JSON.stringify(settings, null, 4));
}
/**
* Copies files from prepared .desktop to desktop.asar in electron app.
*/
packDesktopToAsar() {
this.log.info('packing .desktop to asar');
return new Promise((resolve, reject) => {
_asar.default.createPackage(this.$.env.paths.desktopTmp.root, this.$.env.paths.electronApp.desktopAsar).then(() => {
this.log.verbose('clearing temporary .desktop');
this.$.utils.rmWithRetries('-rf', this.$.env.paths.desktopTmp.root).then(() => {
resolve();
}).catch(e => {
reject(e);
});
resolve();
});
});
}
/**
* Makes a temporary copy of .desktop.
*/
copyDesktopToDesktopTemp() {
this.log.verbose('copying .desktop to temporary location');
_shelljs.default.cp('-rf', this.$.env.paths.desktop.root, this.$.env.paths.desktopTmp.root);
// Remove test files.
_del.default.sync([_path.default.join(this.$.env.paths.desktopTmp.root, '**', '*.test.js')], {
force: true
});
}
/**
* Runs babel and uglify over .desktop if requested.
*/
async transpileAndMinify() {
this.log.info('transpiling and uglifying');
const settings = this.$.desktop.getSettings();
const options = 'uglifyOptions' in settings ? settings.uglifyOptions : {};
const uglifyingEnabled = 'uglify' in settings && !!settings.uglify;
const preset = (0, _presetEnv.default)({
assertVersion: () => {}
}, {
targets: {
node: '12'
}
});
const {
data: files
} = await this.$.utils.readDir(this.$.env.paths.desktopTmp.root);
files.forEach(file => {
if (file.endsWith('.js')) {
let {
code
} = (0, _core.transformFileSync)(file, {
presets: [preset]
});
let error;
if (settings.env === 'prod' && uglifyingEnabled) {
({
code,
error
} = _terser.default.minify(code, options));
}
if (error) {
throw new Error(error);
}
_fs.default.writeFileSync(file, code);
}
});
}
/**
* Moves all the files that should not be packed into asar into a safe location which is the
* 'extracted' dir in the electron app.
*/
async excludeFilesFromArchive() {
this.log.info('excluding files from packing');
// Ensure empty `extracted` dir
try {
await this.$.utils.rmWithRetries('-rf', this.$.env.paths.electronApp.extracted);
} catch (e) {
throw new Error(e);
}
_shelljs.default.mkdir(this.$.env.paths.electronApp.extracted);
const configs = this.$.desktop.gatherModuleConfigs();
// Move files that should not be asar'ed.
configs.forEach(config => {
const moduleConfig = config;
if ('extract' in moduleConfig) {
if (!Array.isArray(moduleConfig.extract)) {
moduleConfig.extract = [moduleConfig.extract];
}
moduleConfig.extract.forEach(file => {
this.log.debug(`excluding ${file} from ${config.name}`);
const filePath = _path.default.join(this.$.env.paths.desktopTmp.modules, moduleConfig.dirName, file);
const destinationPath = _path.default.join(this.$.env.paths.electronApp.extracted, moduleConfig.dirName);
if (!this.$.utils.exists(destinationPath)) {
_shelljs.default.mkdir(destinationPath);
}
_shelljs.default.mv(filePath, destinationPath);
});
}
});
}
}
exports.default = ElectronApp;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcnVudGltZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwiX2FzYXIiLCJfYXNzaWduSW4iLCJfbG9kYXNoIiwiX2luc3RhbGxMb2NhbCIsIl9jb3JlIiwiX2NyeXB0byIsIl9kZWwiLCJfcHJlc2V0RW52IiwiX2ZzIiwiX3BhdGgiLCJfc2hlbGxqcyIsIl9zZW12ZXIiLCJfdGVyc2VyIiwiX2xvZyIsIl9lbGVjdHJvbkFwcFNjYWZmb2xkIiwiX2RlcGVuZGVuY2llc01hbmFnZXIiLCJfYmluYXJ5TW9kdWxlc0RldGVjdG9yIiwib2JqIiwiX19lc01vZHVsZSIsImRlZmF1bHQiLCJzaGVsbCIsImNvbmZpZyIsImZhdGFsIiwiRWxlY3Ryb25BcHAiLCJjb25zdHJ1Y3RvciIsIiQiLCJsb2ciLCJMb2ciLCJzY2FmZm9sZCIsIkVsZWN0cm9uQXBwU2NhZmZvbGQiLCJkZXBzTWFuYWdlciIsIkRlcGVuZGVuY2llc01hbmFnZXIiLCJnZXREZWZhdWx0UGFja2FnZUpzb24iLCJkZXBlbmRlbmNpZXMiLCJtZXRlb3JBcHAiLCJwYWNrYWdlSnNvbiIsInZlcnNpb24iLCJjb21wYXRpYmlsaXR5VmVyc2lvbiIsImRlcHJlY3RhdGVkUGx1Z2lucyIsInBhY2tTa2VsZXRvblRvQXNhciIsImV4Y2x1ZGVGcm9tRGVsIiwiaW5mbyIsIlByb21pc2UiLCJyZXNvbHZlIiwiZXh0cmFjdCIsImdldE1vZHVsZXNUb0V4dHJhY3QiLCJkZWJ1ZyIsImZzIiwicmVuYW1lU3luYyIsImVudiIsInBhdGhzIiwiZWxlY3Ryb25BcHAiLCJub2RlTW9kdWxlcyIsInBhdGgiLCJqb2luIiwiYXBwUm9vdCIsImV4dHJhY3RlZCIsImV4dHJhY3RNb2R1bGVzIiwiYXNhciIsImNyZWF0ZVBhY2thZ2UiLCJhcHBBc2FyIiwidGhlbiIsIm12IiwiZm9yRWFjaCIsIm1vZHVsZSIsImNwIiwiZXh0cmFjdGVkTm9kZU1vZHVsZXMiLCJ1dGlscyIsImV4aXN0cyIsImV4dHJhY3RlZE5vZGVNb2R1bGVzQmluIiwiZXhjbHVkZSIsImNvbmNhdCIsImRlbCIsInN5bmMiLCJyb290Iiwic2VwIiwibWFwIiwicGF0aFRvRXhjbHVkZSIsImZvcmNlIiwiZXh0IiwibGVuZ3RoIiwicm0iLCJta2RpclN5bmMiLCJleHRyYWN0QmluIiwiSlNPTiIsInBhcnNlIiwicmVhZEZpbGVTeW5jIiwiZSIsImJpbnMiLCJiaW4iLCJPYmplY3QiLCJrZXlzIiwiZXh0ZW5zaW9uIiwiYmluRmlsZVBhdGgiLCJzeW1saW5rRXhpc3RzIiwiYmluYXJ5TW9kdWxlc0RldGVjdG9yIiwiQmluYXJ5TW9kdWxlRGV0ZWN0b3IiLCJ0b0JlRXh0cmFjdGVkIiwiZGV0ZWN0IiwiZGVza3RvcCIsImdldFNldHRpbmdzIiwiQXJyYXkiLCJpc0FycmF5IiwibWVyZ2UiLCJ2ZXJib3NlIiwiY2FsY3VsYXRlQ29tcGF0aWJpbGl0eVZlcnNpb24iLCJzZXR0aW5ncyIsImRlc2t0b3BIQ1BDb21wYXRpYmlsaXR5VmVyc2lvbiIsIndhcm4iLCJtZDUiLCJjcnlwdG8iLCJjcmVhdGVIYXNoIiwiZ2V0RGVwZW5kZW5jaWVzIiwiZGVwZW5kZW5jaWVzU29ydGVkIiwic29ydCIsImRlcGVuZGVuY3kiLCJtYWluQ29tcGF0aWJpbGl0eVZlcnNpb24iLCJnZXRWZXJzaW9uIiwic3BsaXQiLCJwdXNoIiwiZGVza3RvcENvbXBhdGliaWxpdHlWZXJzaW9uIiwicHJvY2VzcyIsIk1FVEVPUl9ERVNLVE9QX0RFQlVHX0RFU0tUT1BfQ09NUEFUSUJJTElUWV9WRVJTSU9OIiwiTUVURU9SX0RFU0tUT1BfREVCVUciLCJzdHJpbmdpZnkiLCJ1cGRhdGUiLCJkaWdlc3QiLCJpbml0IiwiZWxlY3Ryb24iLCJlbGVjdHJvbkJ1aWxkZXIiLCJleGl0IiwiYnVpbGQiLCJydW4iLCJjaGVjayIsIm9wdGlvbnMiLCJlcnJvciIsInVwZGF0ZUdpdElnbm9yZSIsInJvb3ROYW1lIiwicmVtb3ZlRGVwcmVjYXRlZFBhY2thZ2VzIiwiZW5zdXJlRGVza3RvcEhDUFBhY2thZ2VzIiwibWFrZSIsImZpbGVOYW1lIiwiZGlyTmFtZSIsImV4aXN0c1N5bmMiLCJjb3B5RmlsZVN5bmMiLCJleHBvc2VFbGVjdHJvbk1vZHVsZXMiLCJ1cGRhdGVQYWNrYWdlSnNvbkZpZWxkcyIsInVwZGF0ZURlcGVuZGVuY2llc0xpc3QiLCJoYW5kbGVUZW1wb3JhcnlOb2RlTW9kdWxlcyIsIm5vZGVNb2R1bGVzUmVtb3ZlZCIsImhhbmRsZVN0YXRlT2ZOb2RlTW9kdWxlcyIsInJlYnVpbGREZXBzIiwibGlua05wbVBhY2thZ2VzIiwiaW5zdGFsbExvY2FsTm9kZU1vZHVsZXMiLCJlbnN1cmVNZXRlb3JEZXBlbmRlbmNpZXMiLCJpc1Byb2R1Y3Rpb25CdWlsZCIsImNvcHlEZXNrdG9wVG9EZXNrdG9wVGVtcCIsInVwZGF0ZVNldHRpbmdzSnNvbkZpZWxkcyIsImV4Y2x1ZGVGaWxlc0Zyb21BcmNoaXZlIiwidHJhbnNwaWxlQW5kTWluaWZ5IiwicGFja0Rlc2t0b3BUb0FzYXIiLCJnZXRNZXRlb3JDbGllbnRCdWlsZCIsImV4cG9zZWRNb2R1bGVzIiwicHJlbG9hZCIsIm1vZHVsZXMiLCJyZWR1Y2UiLCJwcmV2IiwicmVwbGFjZSIsIndyaXRlRmlsZVN5bmMiLCJwYWNrYWdlcyIsInBhY2thZ2VzV2l0aFZlcnNpb24iLCJwbHVnaW5zIiwicGx1Z2luIiwibWV0ZW9yRGVwZW5kZW5jaWVzIiwidW5zaGlmdCIsInBhY2thZ2VOYW1lIiwicGFja2FnZXNDb3VudCIsImZpbHRlciIsInZhbHVlIiwiaW5jbHVkZXMiLCJzdWJzdHIiLCJtZXRlb3JNYW5hZ2VyIiwiZW5zdXJlUGFja2FnZXMiLCJFcnJvciIsImlhMzIiLCJybVdpdGhSZXRyaWVzIiwidG1wTm9kZU1vZHVsZXMiLCJwcm9taXNlcyIsImxpbmtQYWNrYWdlcyIsInJ1bk5wbSIsInVuZGVmaW5lZCIsImFsbCIsImVuc3VyZURlcHMiLCJzdGRpbyIsImNoZWNrUGx1Z2luc1ZlcnNpb24iLCJwbHVnaW5zVmVyc2lvbnMiLCJzZXR0aW5nc0pzb24iLCJzY2FmZm9sZFBsdWdpbnNWZXJzaW9uIiwicGx1Z2luTmFtZSIsInNlbXZlciIsImx0IiwiZGVza3RvcERlcGVuZGVuY2llcyIsIm1lcmdlRGVwZW5kZW5jaWVzIiwiZnJvbVNldHRpbmdzIiwiZ2V0UmVtb3RlRGVwZW5kZW5jaWVzIiwibG9jYWxEZXBlbmRlbmNpZXMiLCJnZXRMb2NhbERlcGVuZGVuY2llcyIsImFyY2giLCJfIiwidmFsdWVzIiwibGFzdFJlYnVpbGQiLCJwcmVwYXJlTGFzdFJlYnVpbGRPYmplY3QiLCJnZXRHeXBFbnYiLCJmcmFtZXdvcmtJbmZvIiwicGxhdGZvcm0iLCJpbnN0YWxsZXIiLCJMb2NhbEluc3RhbGxlciIsIm5wbUVudiIsInByb2dyZXNzIiwiaW5zdGFsbCIsImluc3RhbGxPclJlYnVpbGQiLCJhc3NpZ25JbiIsInBhY2thZ2VKc29uRmllbGRzIiwibmFtZSIsInByb2plY3ROYW1lIiwiZ2V0SGFzaFZlcnNpb24iLCJkZXNrdG9wVmVyc2lvbiIsIm1ldGVvckRlc2t0b3BWZXJzaW9uIiwicHJvZERlYnVnIiwiZGVza3RvcFRtcCIsInJlamVjdCIsImRlc2t0b3BBc2FyIiwiY2F0Y2giLCJ1Z2xpZnlPcHRpb25zIiwidWdsaWZ5aW5nRW5hYmxlZCIsInVnbGlmeSIsInByZXNldCIsInByZXNldEVudiIsImFzc2VydFZlcnNpb24iLCJ0YXJnZXRzIiwibm9kZSIsImRhdGEiLCJmaWxlcyIsInJlYWREaXIiLCJmaWxlIiwiZW5kc1dpdGgiLCJjb2RlIiwidHJhbnNmb3JtRmlsZVN5bmMiLCJwcmVzZXRzIiwibWluaWZ5IiwibWtkaXIiLCJjb25maWdzIiwiZ2F0aGVyTW9kdWxlQ29uZmlncyIsIm1vZHVsZUNvbmZpZyIsImZpbGVQYXRoIiwiZGVzdGluYXRpb25QYXRoIiwiZXhwb3J0cyJdLCJzb3VyY2VzIjpbIi4uL2xpYi9lbGVjdHJvbkFwcC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW51c2VkLXZhcnNcbmltcG9ydCByZWdlbmVyYXRvclJ1bnRpbWUgZnJvbSAncmVnZW5lcmF0b3ItcnVudGltZS9ydW50aW1lJztcbmltcG9ydCBhc2FyIGZyb20gJ0BlbGVjdHJvbi9hc2FyJztcbmltcG9ydCBhc3NpZ25JbiBmcm9tICdsb2Rhc2gvYXNzaWduSW4nO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCB7IExvY2FsSW5zdGFsbGVyLCBwcm9ncmVzcyB9IGZyb20gJ2luc3RhbGwtbG9jYWwnO1xuaW1wb3J0IHsgdHJhbnNmb3JtRmlsZVN5bmMgfSBmcm9tICdAYmFiZWwvY29yZSc7XG5pbXBvcnQgY3J5cHRvIGZyb20gJ2NyeXB0byc7XG5pbXBvcnQgZGVsIGZyb20gJ2RlbCc7XG5pbXBvcnQgcHJlc2V0RW52IGZyb20gJ0BiYWJlbC9wcmVzZXQtZW52JztcbmltcG9ydCBmcyBmcm9tICdmcyc7XG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCBzaGVsbCBmcm9tICdzaGVsbGpzJztcbmltcG9ydCBzZW12ZXIgZnJvbSAnc2VtdmVyJztcbmltcG9ydCB1Z2xpZnkgZnJvbSAndGVyc2VyJztcblxuaW1wb3J0IExvZyBmcm9tICcuL2xvZyc7XG5pbXBvcnQgRWxlY3Ryb25BcHBTY2FmZm9sZCBmcm9tICcuL2VsZWN0cm9uQXBwU2NhZmZvbGQnO1xuaW1wb3J0IERlcGVuZGVuY2llc01hbmFnZXIgZnJvbSAnLi9kZXBlbmRlbmNpZXNNYW5hZ2VyJztcbmltcG9ydCBCaW5hcnlNb2R1bGVEZXRlY3RvciBmcm9tICcuL2JpbmFyeU1vZHVsZXNEZXRlY3Rvcic7XG5cbnNoZWxsLmNvbmZpZy5mYXRhbCA9IHRydWU7XG5cbi8qKlxuICogUmVwcmVzZW50cyB0aGUgLmRlc2t0b3AgZGlyIHNjYWZmb2xkLlxuICogQGNsYXNzXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEVsZWN0cm9uQXBwIHtcbiAgICAvKipcbiAgICAgKiBAcGFyYW0ge01ldGVvckRlc2t0b3B9ICQgLSBjb250ZXh0XG4gICAgICogQGNvbnN0cnVjdG9yXG4gICAgICovXG4gICAgY29uc3RydWN0b3IoJCkge1xuICAgICAgICB0aGlzLmxvZyA9IG5ldyBMb2coJ2VsZWN0cm9uQXBwJyk7XG4gICAgICAgIHRoaXMuc2NhZmZvbGQgPSBuZXcgRWxlY3Ryb25BcHBTY2FmZm9sZCgkKTtcbiAgICAgICAgdGhpcy5kZXBzTWFuYWdlciA9IG5ldyBEZXBlbmRlbmNpZXNNYW5hZ2VyKFxuICAgICAgICAgICAgJCxcbiAgICAgICAgICAgIHRoaXMuc2NhZmZvbGQuZ2V0RGVmYXVsdFBhY2thZ2VKc29uKCkuZGVwZW5kZW5jaWVzXG4gICAgICAgICk7XG4gICAgICAgIHRoaXMuJCA9ICQ7XG4gICAgICAgIHRoaXMubWV0ZW9yQXBwID0gdGhpcy4kLm1ldGVvckFwcDtcbiAgICAgICAgdGhpcy5wYWNrYWdlSnNvbiA9IG51bGw7XG4gICAgICAgIHRoaXMudmVyc2lvbiA9IG51bGw7XG4gICAgICAgIHRoaXMuY29tcGF0aWJpbGl0eVZlcnNpb24gPSBudWxsO1xuICAgICAgICB0aGlzLmRlcHJlY3RhdGVkUGx1Z2lucyA9IFsnbWV0ZW9yLWRlc2t0b3AtbG9jYWxzdG9yYWdlJ107XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTWFrZXMgYW4gYXBwLmFzYXIgZnJvbSB0aGUgc2tlbGV0b24gYXBwLlxuICAgICAqIEBwcm9wZXJ0eSB7QXJyYXl9IGV4Y2x1ZGVGcm9tRGVsIC0gbGlzdCBvZiBwYXRocyB0byBleGNsdWRlIGZyb20gZGVsZXRpbmdcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZX1cbiAgICAgKi9cbiAgICBwYWNrU2tlbGV0b25Ub0FzYXIoZXhjbHVkZUZyb21EZWwgPSBbXSkge1xuICAgICAgICB0aGlzLmxvZy5pbmZvKCdwYWNraW5nIHNrZWxldG9uIGFwcCBhbmQgbm9kZV9tb2R1bGVzIHRvIGFzYXIgYXJjaGl2ZScpO1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGV4dHJhY3QgPSB0aGlzLmdldE1vZHVsZXNUb0V4dHJhY3QoKTtcblxuICAgICAgICAgICAgLy8gV2Ugd2FudCB0byBwYWNrIHNrZWxldG9uIGFwcCBhbmQgbm9kZV9tb2R1bGVzIHRvZ2V0aGVyLCBzbyB3ZSBuZWVkIHRvIHRlbXBvcmFyaWx5XG4gICAgICAgICAgICAvLyBtb3ZlIG5vZGVfbW9kdWxlcyB0byBhcHAgZGlyLlxuICAgICAgICAgICAgdGhpcy5sb2cuZGVidWcoJ21vdmluZyBub2RlX21vZHVsZXMgdG8gYXBwIGRpcicpO1xuXG4gICAgICAgICAgICBmcy5yZW5hbWVTeW5jKFxuICAgICAgICAgICAgICAgIHRoaXMuJC5lbnYucGF0aHMuZWxlY3Ryb25BcHAubm9kZU1vZHVsZXMsXG4gICAgICAgICAgICAgICAgcGF0aC5qb2luKHRoaXMuJC5lbnYucGF0aHMuZWxlY3Ryb25BcHAuYXBwUm9vdCwgJ25vZGVfbW9kdWxlcycpXG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICBsZXQgZXh0cmFjdGVkID0gZmFsc2U7XG4gICAgICAgICAgICBleHRyYWN0ZWQgPSB0aGlzLmV4dHJhY3RNb2R1bGVzKGV4dHJhY3QpO1xuXG4gICAgICAgICAgICB0aGlzLmxvZy5kZWJ1ZygncGFja2luZycpO1xuICAgICAgICAgICAgYXNhci5jcmVhdGVQYWNrYWdlKFxuICAgICAgICAgICAgICAgIHRoaXMuJC5lbnYucGF0aHMuZWxlY3Ryb25BcHAuYXBwUm9vdCxcbiAgICAgICAgICAgICAgICB0aGlzLiQuZW52LnBhdGhzLmVsZWN0cm9uQXBwLmFwcEFzYXIsXG4gICAgICAgICAgICApLnRoZW4oKCkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIExldHMgbW92ZSB0aGUgbm9kZV9tb2R1bGVzIGJhY2suXG4gICAgICAgICAgICAgICAgdGhpcy5sb2cuZGVidWcoJ21vdmluZyBub2RlX21vZHVsZXMgYmFjayBmcm9tIGFwcCBkaXInKTtcblxuICAgICAgICAgICAgICAgIHNoZWxsLm12KFxuICAgICAgICAgICAgICAgICAgICBwYXRoLmpvaW4odGhpcy4kLmVudi5wYXRocy5lbGVjdHJvbkFwcC5hcHBSb290LCAnbm9kZV9tb2R1bGVzJyksXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuJC5lbnYucGF0aHMuZWxlY3Ryb25BcHAubm9kZU1vZHVsZXNcbiAgICAgICAgICAgICAgICApO1xuXG4gICAgICAgICAgICAgICAgaWYgKGV4dHJhY3RlZCkge1xuICAgICAgICAgICAgICAgICAgICAvLyBXZSBuZWVkIHRvIGNyZWF0ZSBhIGZ1bGwgbm9kZSBtb2R1bGVzIGJhY2suIEluIG90aGVyIHdvcmRzIHdlIHdhbnRcbiAgICAgICAgICAgICAgICAgICAgLy8gdGhlIGV4dHJhY3RlZCBtb2R1bGVzIGJhY2suXG4gICAgICAgICAgICAgICAgICAgIGV4dHJhY3QuZm9yRWFjaChtb2R1bGUgPT4gc2hlbGwuY3AoXG4gICAgICAgICAgICAgICAgICAgICAgICAnLXJmJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHBhdGguam9pbih0aGlzLiQuZW52LnBhdGhzLmVsZWN0cm9uQXBwLmV4dHJhY3RlZE5vZGVNb2R1bGVzLCBtb2R1bGUpLFxuICAgICAgICAgICAgICAgICAgICAgICAgcGF0aC5qb2luKHRoaXMuJC5lbnYucGF0aHMuZWxlY3Ryb25BcHAubm9kZU1vZHVsZXMsIG1vZHVsZSlcbiAgICAgICAgICAgICAgICAgICAgKSk7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gR2V0IHRoZSAuYmluIGJhY2suXG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLiQudXRpbHMuZXhpc3RzKFxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy4kLmVudi5wYXRocy5lbGVjdHJvbkFwcC5leHRyYWN0ZWROb2RlTW9kdWxlc0JpblxuICAgICAgICAgICAgICAgICAgICApKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzaGVsbC5jcChcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXRoLmpvaW4odGhpcy4kLmVudi5wYXRocy5lbGVjdHJvbkFwcC5leHRyYWN0ZWROb2RlTW9kdWxlc0JpbiwgJyonKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXRoLmpvaW4odGhpcy4kLmVudi5wYXRocy5lbGVjdHJvbkFwcC5ub2RlTW9kdWxlcywgJy5iaW4nKVxuICAgICAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHRoaXMubG9nLmRlYnVnKCdkZWxldGluZyBzb3VyY2UgZmlsZXMnKTtcbiAgICAgICAgICAgICAgICBjb25zdCBleGNsdWRlID0gW3RoaXMuJC5lbnYucGF0aHMuZWxlY3Ryb25BcHAubm9kZU1vZHVsZXNdLmNvbmNhdChcbiAgICAgICAgICAgICAgICAgICAgW1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy4kLmVudi5wYXRocy5lbGVjdHJvbkFwcC5hcHBBc2FyLFxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy4kLmVudi5wYXRocy5lbGVjdHJvbkFwcC5wYWNrYWdlSnNvblxuICAgICAgICAgICAgICAgICAgICBdLFxuICAgICAgICAgICAgICAgICAgICBleGNsdWRlRnJvbURlbFxuICAgICAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgICAgICBkZWwuc3luYyhcbiAgICAgICAgICAgICAgICAgICAgW2Ake3RoaXMuJC5lbnYucGF0aHMuZWxlY3Ryb25BcHAucm9vdH0ke3BhdGguc2VwfSpgXS5jb25jYXQoXG4gICAgICAgICAgICAgICAgICAgICAgICBleGNsdWRlLm1hcChwYXRoVG9FeGNsdWRlID0+IGAhJHtwYXRoVG9FeGNsdWRlfWApXG4gICAgICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgICAgIHsgZm9yY2U6IHRydWUgfVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIE1vdmVzIHNwZWNpZmllZCBub2RlIG1vZHVsZXMgdG8gYSBzZXBhcmF0ZSBkaXJlY3RvcnkuXG4gICAgICogQHBhcmFtIHtBcnJheX0gZXh0cmFjdFxuICAgICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgICAqL1xuICAgIGV4dHJhY3RNb2R1bGVzKGV4dHJhY3QpIHtcbiAgICAgICAgY29uc3QgZXh0ID0gWycuanMnLCAnLmJhdCcsICcuc2gnLCAnLmNtZCcsICcnXTtcblxuICAgICAgICBpZiAoZXh0cmFjdC5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBpZiAodGhpcy4kLnV0aWxzLmV4aXN0cyh0aGlzLiQuZW52LnBhdGhzLmVsZWN0cm9uQXBwLmV4dHJhY3RlZE5vZGVNb2R1bGVzKSkge1xuICAgICAgICAgICAgICAgIHNoZWxsLnJtKCctcmYnLCB0aGlzLiQuZW52LnBhdGhzLmVsZWN0cm9uQXBwLmV4dHJhY3RlZE5vZGVNb2R1bGVzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGZzLm1rZGlyU3luYyh0aGlzLiQuZW52LnBhdGhzLmVsZWN0cm9uQXBwLmV4dHJhY3RlZE5vZGVNb2R1bGVzKTtcbiAgICAgICAgICAgIGZzLm1rZGlyU3luYyh0aGlzLiQuZW52LnBhdGhzLmVsZWN0cm9uQXBwLmV4dHJhY3RlZE5vZGVNb2R1bGVzQmluKTtcblxuICAgICAgICAgICAgZXh0cmFjdC5mb3JFYWNoKChtb2R1bGUpID0+IHtcbiAgICAgICAgICAgICAgICBmcy5yZW5hbWVTeW5jKFxuICAgICAgICAgICAgICAgICAgICBwYXRoLmpvaW4odGhpcy4kLmVudi5wYXRocy5lbGVjdHJvbkFwcC5hcHBSb290LCAnbm9kZV9tb2R1bGVzJywgbW9kdWxlKSxcbiAgICAgICAgICAgICAgICAgICAgcGF0aC5qb2luKHRoaXMuJC5lbnYucGF0aHMuZWxlY3Ryb25BcHAuZXh0cmFjdGVkTm9kZU1vZHVsZXMsIG1vZHVsZSksXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAvLyBNb3ZlIGJpbnMuXG4gICAgICAgICAgICAgICAgdGhpcy5leHRyYWN0QmluKG1vZHVsZSwgZXh0KTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRXh0cmFjdHMgdGhlIGJpbiBmaWxlcyBhc3NvY2lhdGVkIHdpdGggYSBjZXJ0YWluIG5vZGUgbW9kdWxlcy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBtb2R1bGVcbiAgICAgKiBAcGFyYW0gZXh0XG4gICAgICovXG4gICAgZXh0cmFjdEJpbihtb2R1bGUsIGV4dCkge1xuICAgICAgICBsZXQgcGFja2FnZUpzb247XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBwYWNrYWdlSnNvbiA9IEpTT04ucGFyc2UoXG4gICAgICAgICAgICAgICAgZnMucmVhZEZpbGVTeW5jKFxuICAgICAgICAgICAgICAgICAgICBwYXRoLmpvaW4oXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLiQuZW52LnBhdGhzLmVsZWN0cm9uQXBwLmV4dHJhY3RlZE5vZGVNb2R1bGVzLCBtb2R1bGUsICdwYWNrYWdlLmpzb24nXG4gICAgICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgICAgICd1dGY4J1xuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHBhY2thZ2VKc29uID0ge307XG4gICAgICAgIH1cblxuXG4gICAgICAgIGNvbnN0IGJpbnMgPSAoJ2JpbicgaW4gcGFja2FnZUpzb24gJiYgdHlwZW9mIHBhY2thZ2VKc29uLmJpbiA9PT0gJ29iamVjdCcpID8gT2JqZWN0LmtleXMocGFja2FnZUpzb24uYmluKSA6IFtdO1xuXG4gICAgICAgIGlmIChiaW5zLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGJpbnMuZm9yRWFjaCgoYmluKSA9PiB7XG4gICAgICAgICAgICAgICAgZXh0LmZvckVhY2goKGV4dGVuc2lvbikgPT4ge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBiaW5GaWxlUGF0aCA9IHBhdGguam9pbihcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuJC5lbnYucGF0aHMuZWxlY3Ryb25BcHAuYXBwUm9vdCxcbiAgICAgICAgICAgICAgICAgICAgICAgICdub2RlX21vZHVsZXMnLFxuICAgICAgICAgICAgICAgICAgICAgICAgJy5iaW4nLFxuICAgICAgICAgICAgICAgICAgICAgICAgYCR7YmlufSR7ZXh0ZW5zaW9ufWBcbiAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuJC51dGlscy5leGlzdHMoYmluRmlsZVBhdGgpIHx8XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLiQudXRpbHMuc3ltbGlua0V4aXN0cyhiaW5GaWxlUGF0aClcbiAgICAgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBmcy5yZW5hbWVTeW5jKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJpbkZpbGVQYXRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdGguam9pbihcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy4kLmVudi5wYXRocy5lbGVjdHJvbkFwcC5leHRyYWN0ZWROb2RlTW9kdWxlc0JpbixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYCR7YmlufSR7ZXh0ZW5zaW9ufWBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIE1lcmdlcyB0aGUgYGV4dHJhY3RgIGZpZWxkIHdpdGggYXV0b21hdGljYWxseSBkZXRlY3RlZCBtb2R1bGVzLlxuICAgICAqL1xuICAgIGdldE1vZHVsZXNUb0V4dHJhY3QoKSB7XG4gICAgICAgIGNvbnN0IGJpbmFyeU1vZHVsZXNEZXRlY3RvciA9XG4gICAgICAgICAgICBuZXcgQmluYXJ5TW9kdWxlRGV0ZWN0b3IodGhpcy4kLmVudi5wYXRocy5lbGVjdHJvbkFwcC5ub2RlTW9kdWxlcyk7XG4gICAgICAgIGNvbnN0IHRvQmVFeHRyYWN0ZWQgPSBiaW5hcnlNb2R1bGVzRGV0ZWN0b3IuZGV0ZWN0KCk7XG5cbiAgICAgICAgbGV0IHsgZXh0cmFjdCB9ID0gdGhpcy4kLmRlc2t0b3AuZ2V0U2V0dGluZ3MoKTtcblxuICAgICAgICBpZiAoIUFycmF5LmlzQXJyYXkoZXh0cmFjdCkpIHtcbiAgICAgICAgICAgIGV4dHJhY3QgPSBbXTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IG1lcmdlID0ge307XG4gICAgICAgIHRvQmVFeHRyYWN0ZWQuY29uY2F0KGV4dHJhY3QpLmZvckVhY2goKG1vZHVsZSkgPT4ge1xuICAgICAgICAgICAgbWVyZ2VbbW9kdWxlXSA9IHRydWU7XG4gICAgICAgIH0pO1xuICAgICAgICBleHRyYWN0ID0gT2JqZWN0LmtleXMobWVyZ2UpO1xuICAgICAgICBpZiAoZXh0cmFjdC5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICB0aGlzLmxvZy52ZXJib3NlKGByZXN1bHRhbnQgbW9kdWxlcyB0byBleHRyYWN0IGxpc3QgaXM6ICR7ZXh0cmFjdC5qb2luKCcsICcpfWApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBleHRyYWN0O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENhbGN1bGF0ZXMgYSBtZDUgZnJvbSBhbGwgZGVwZW5kZW5jaWVzLlxuICAgICAqL1xuICAgIGNhbGN1bGF0ZUNvbXBhdGliaWxpdHlWZXJzaW9uKCkge1xuICAgICAgICB0aGlzLmxvZy52ZXJib3NlKCdjYWxjdWxhdGluZyBjb21wYXRpYmlsaXR5IHZlcnNpb24nKTtcbiAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSB0aGlzLiQuZGVza3RvcC5nZXRTZXR0aW5ncygpO1xuXG4gICAgICAgIGlmICgoJ2Rlc2t0b3BIQ1BDb21wYXRpYmlsaXR5VmVyc2lvbicgaW4gc2V0dGluZ3MpKSB7XG4gICAgICAgICAgICB0aGlzLmNvbXBhdGliaWxpdHlWZXJzaW9uID0gYCR7c2V0dGluZ3MuZGVza3RvcEhDUENvbXBhdGliaWxpdHlWZXJzaW9ufWA7XG4gICAgICAgICAgICB0aGlzLmxvZy53YXJuKGBjb21wYXRpYmlsaXR5IHZlcnNpb24gb3ZlcnJpZGRlbiB0byAke3RoaXMuY29tcGF0aWJpbGl0eVZlcnNpb259YCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBtZDUgPSBjcnlwdG8uY3JlYXRlSGFzaCgnbWQ1Jyk7XG4gICAgICAgIGxldCBkZXBlbmRlbmNpZXMgPSB0aGlzLmRlcHNNYW5hZ2VyLmdldERlcGVuZGVuY2llcygpO1xuICAgICAgICBjb25zdCBkZXBlbmRlbmNpZXNTb3J0ZWQgPSBPYmplY3Qua2V5cyhkZXBlbmRlbmNpZXMpLnNvcnQoKTtcbiAgICAgICAgZGVwZW5kZW5jaWVzID0gZGVwZW5kZW5jaWVzU29ydGVkLm1hcChkZXBlbmRlbmN5ID0+XG4gICAgICAgICAgICBgJHtkZXBlbmRlbmN5fToke2RlcGVuZGVuY2llc1tkZXBlbmRlbmN5XX1gKTtcbiAgICAgICAgY29uc3QgbWFpbkNvbXBhdGliaWxpdHlWZXJzaW9uID0gdGhpcy4kLmdldFZlcnNpb24oKS5zcGxpdCgnLicpO1xuICAgICAgICB0aGlzLmxvZy5kZWJ1ZygnbWV0ZW9yLWRlc2t0b3AgY29tcGF0aWJpbGl0eSB2ZXJzaW9uIGlzICcsXG4gICAgICAgICAgICBgJHttYWluQ29tcGF0aWJpbGl0eVZlcnNpb25bMF19YCk7XG4gICAgICAgIGRlcGVuZGVuY2llcy5wdXNoKFxuICAgICAgICAgICAgYG1ldGVvci1kZXNrdG9wOiR7bWFpbkNvbXBhdGliaWxpdHlWZXJzaW9uWzBdfWBcbiAgICAgICAgKTtcblxuICAgICAgICBjb25zdCBkZXNrdG9wQ29tcGF0aWJpbGl0eVZlcnNpb24gPSBzZXR0aW5ncy52ZXJzaW9uLnNwbGl0KCcuJylbMF07XG4gICAgICAgIHRoaXMubG9nLmRlYnVnKCcuZGVza3RvcCBjb21wYXRpYmlsaXR5IHZlcnNpb24gaXMgJywgZGVza3RvcENvbXBhdGliaWxpdHlWZXJzaW9uKTtcbiAgICAgICAgZGVwZW5kZW5jaWVzLnB1c2goXG4gICAgICAgICAgICBgZGVza3RvcC1hcHA6JHtkZXNrdG9wQ29tcGF0aWJpbGl0eVZlcnNpb259YFxuICAgICAgICApO1xuXG4gICAgICAgIGlmIChwcm9jZXNzLmVudi5NRVRFT1JfREVTS1RPUF9ERUJVR19ERVNLVE9QX0NPTVBBVElCSUxJVFlfVkVSU0lPTiB8fFxuICAgICAgICAgICAgcHJvY2Vzcy5lbnYuTUVURU9SX0RFU0tUT1BfREVCVUdcbiAgICAgICAgKSB7XG4gICAgICAgICAgICB0aGlzLmxvZy5kZWJ1ZyhgY29tcGF0aWJpbGl0eSB2ZXJzaW9uIGNhbGN1bGF0ZWQgZnJvbSAke0pTT04uc3RyaW5naWZ5KGRlcGVuZGVuY2llcyl9YCk7XG4gICAgICAgIH1cblxuICAgICAgICBtZDUudXBkYXRlKEpTT04uc3RyaW5naWZ5KGRlcGVuZGVuY2llcykpO1xuXG4gICAgICAgIHRoaXMuY29tcGF0aWJpbGl0eVZlcnNpb24gPSBtZDUuZGlnZXN0KCdoZXgnKTtcbiAgICB9XG5cbiAgICBhc3luYyBpbml0KCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy4kLmVsZWN0cm9uLmluaXQoKTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuJC5lbGVjdHJvbkJ1aWxkZXIuaW5pdCgpO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICB0aGlzLmxvZy53YXJuKCdlcnJvciBvY2N1cnJlZCB3aGlsZSBpbml0aWFsaXNpbmcgZWxlY3Ryb24gYW5kIGVsZWN0cm9uLWJ1aWxkZXIgaW50ZWdyYXRpb24nLCBlKTtcbiAgICAgICAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJ1bnMgYWxsIG5lY2Vzc2FyeSB0YXNrcyB0byBidWlsZCB0aGUgZGVza3RvcGlmaWVkIGFwcC5cbiAgICAgKi9cbiAgICBhc3luYyBidWlsZChydW4gPSBmYWxzZSkge1xuICAgICAgICAvLyBUT0RPOiByZWZhY3RvciB0byBhIHRhc2sgcnVubmVyXG4gICAgICAgIHRoaXMubG9nLmluZm8oJ3NjYWZmb2xkaW5nJyk7XG5cbiAgICAgICAgaWYgKCF0aGlzLiQuZGVza3RvcC5jaGVjaygpKSB7XG4gICAgICAgICAgICBpZiAoIXRoaXMuJC5lbnYub3B0aW9ucy5zY2FmZm9sZCkge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nLmVycm9yKCdzZWVtcyB0aGF0IHlvdSBkbyBub3QgaGF2ZSBhIC5kZXNrdG9wIGRpciBpbiB5b3VyIHByb2plY3Qgb3IgaXQgaXMnICtcbiAgICAgICAgICAgICAgICAgICAgJyBjb3JydXB0ZWQuIFJ1biBcXCducG0gcnVuIGRlc2t0b3AgLS0gaW5pdFxcJyB0byBnZXQgYSBuZXcgb25lLicpO1xuICAgICAgICAgICAgICAgIC8vIERvIG5vdCBmYWlsLCBzbyB0aGF0IG5wbSB3aWxsIG5vdCBwcmludCBoaXMgZXJyb3Igc3R1ZmYgdG8gY29uc29sZS5cbiAgICAgICAgICAgICAgICBwcm9jZXNzLmV4aXQoMCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMuJC5kZXNrdG9wLnNjYWZmb2xkKCk7XG4gICAgICAgICAgICAgICAgdGhpcy4kLm1ldGVvckFwcC51cGRhdGVHaXRJZ25vcmUoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGF3YWl0IHRoaXMuaW5pdCgpO1xuXG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHRoaXMuJC5tZXRlb3JBcHAudXBkYXRlR2l0SWdub3JlKCk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHRoaXMubG9nLndhcm4oYGVycm9yIG9jY3VycmVkIHdoaWxlIGFkZGluZyAke3RoaXMuJC5lbnYucGF0aHMuZWxlY3Ryb25BcHAucm9vdE5hbWV9YCArXG4gICAgICAgICAgICAgICAgJ3RvIC5naXRpZ25vcmU6ICcsIGUpO1xuICAgICAgICB9XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuJC5tZXRlb3JBcHAucmVtb3ZlRGVwcmVjYXRlZFBhY2thZ2VzKCk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHRoaXMubG9nLmVycm9yKCdlcnJvciB3aGlsZSByZW1vdmluZyBkZXByZWNhdGVkIHBhY2thZ2VzOiAnLCBlKTtcbiAgICAgICAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLiQubWV0ZW9yQXBwLmVuc3VyZURlc2t0b3BIQ1BQYWNrYWdlcygpO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICB0aGlzLmxvZy5lcnJvcignZXJyb3Igd2hpbGUgY2hlY2tpbmcgZm9yIHJlcXVpcmVkIHBhY2thZ2VzOiAnLCBlKTtcbiAgICAgICAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnNjYWZmb2xkLm1ha2UoKTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgdGhpcy5sb2cuZXJyb3IoJ2Vycm9yIHdoaWxlIHNjYWZmb2xkaW5nOiAnLCBlKTtcbiAgICAgICAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBmaWxlTmFtZSA9ICcubnBtcmMnO1xuICAgICAgICAgICAgY29uc3QgZGlyTmFtZSA9ICcubWV0ZW9yL2Rlc2t0b3AtYnVpbGQnO1xuICAgICAgICAgICAgaWYgKGZzLmV4aXN0c1N5bmMoZGlyTmFtZSkgJiYgZnMuZXhpc3RzU3luYyhmaWxlTmFtZSkpIHtcbiAgICAgICAgICAgICAgICBmcy5jb3B5RmlsZVN5bmMoZmlsZU5hbWUsIGAke2Rpck5hbWV9LyR7ZmlsZU5hbWV9YCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHRoaXMubG9nLndhcm4oJ2Vycm9yIHdoaWxlIGNvcHlpbmcgLm5wbXJjJywgZSk7XG4gICAgICAgIH1cblxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5leHBvc2VFbGVjdHJvbk1vZHVsZXMoKTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgdGhpcy5sb2cuZXJyb3IoJ2Vycm9yIHdoaWxlIGV4cG9zaW5nIGVsZWN0cm9uIG1vZHVsZXM6ICcsIGUpO1xuICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgICAgICB9XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHRoaXMudXBkYXRlUGFja2FnZUpzb25GaWVsZHMoKTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgdGhpcy5sb2cuZXJyb3IoJ2Vycm9yIHdoaWxlIHVwZGF0aW5nIHBhY2thZ2UuanNvbjogJywgZSk7XG4gICAgICAgIH1cblxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgdGhpcy51cGRhdGVEZXBlbmRlbmNpZXNMaXN0KCk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHRoaXMubG9nLmVycm9yKCdlcnJvciB3aGlsZSBtZXJnaW5nIGRlcGVuZGVuY2llcyBsaXN0OiAnLCBlKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICB0aGlzLmNhbGN1bGF0ZUNvbXBhdGliaWxpdHlWZXJzaW9uKCk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHRoaXMubG9nLmVycm9yKCdlcnJvciB3aGlsZSBjYWxjdWxhdGluZyBjb21wYXRpYmlsaXR5IHZlcnNpb246ICcsIGUpO1xuICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgICAgICB9XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuaGFuZGxlVGVtcG9yYXJ5Tm9kZU1vZHVsZXMoKTtcbiAgICAgICAgfSBjYX