@mjcctech/meteor-desktop
Version:
Build a Meteor's desktop client with hot code push.
271 lines (217 loc) • 26.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _runtime = _interopRequireDefault(require("regenerator-runtime/runtime"));
var _shelljs = _interopRequireDefault(require("shelljs"));
var _fs = _interopRequireDefault(require("fs"));
var _path = _interopRequireDefault(require("path"));
var _log = _interopRequireDefault(require("./log"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// eslint-disable-next-line no-unused-vars
_shelljs.default.config.fatal = true;
/**
* Checks if the path is empty.
* @param {string} searchPath
* @returns {boolean}
*/
function isEmptySync(searchPath) {
let stat;
try {
stat = _fs.default.statSync(searchPath);
} catch (e) {
return true;
}
if (stat.isDirectory()) {
const items = _fs.default.readdirSync(searchPath);
return !items || !items.length;
}
return false;
}
/**
* Represents the .desktop directory.
* @class
* @property {desktopSettings} settings
*/
class Desktop {
/**
* @param {MeteorDesktop} $ - context
*
* @constructor
*/
constructor($) {
this.$ = $;
this.log = new _log.default('desktop');
this.settings = null;
this.dependencies = null;
}
/**
* Tries to read and returns settings.json contents from .desktop dir.
*
* @returns {desktopSettings|null}
*/
getSettings() {
if (!this.settings) {
try {
this.settings = JSON.parse(_fs.default.readFileSync(this.$.env.paths.desktop.settings, 'UTF-8'));
} catch (e) {
this.log.error('error while trying to read \'.desktop/settings.json\': ', e);
process.exit(1);
}
}
return this.settings;
}
/**
* Returns a version hash representing current .desktop contents.
* @returns {string}
*/
async getHashVersion() {
this.log.info('calculating hash version from .desktop contents');
const version = await this.$.utils.readFilesAndComputeHash(this.$.env.paths.desktop.root);
this.log.verbose(`calculated .desktop hash version is ${version.hash}`);
return version.hash;
}
/**
* Tries to read a module.json file from a module at provided path.
*
* @param {string} modulePath - path to the module dir
* @returns {Object}
*/
getModuleConfig(modulePath) {
let moduleConfig = {};
try {
moduleConfig = JSON.parse(_fs.default.readFileSync(_path.default.join(modulePath, 'module.json'), 'UTF-8'));
} catch (e) {
this.log.error(`error while trying to read 'module.json' from '${modulePath}' module: `, e);
process.exit(1);
}
if (!('name' in moduleConfig)) {
this.log.error(`no 'name' field defined in 'module.json' in '${modulePath}' module.`);
process.exit(1);
}
return moduleConfig;
}
/**
* Scans all modules for module.json and gathers this configuration altogether.
*
* @returns {[]}
*/
gatherModuleConfigs() {
const configs = [];
if (!isEmptySync(this.$.env.paths.desktop.modules)) {
_shelljs.default.ls('-d', _path.default.join(this.$.env.paths.desktop.modules, '*')).forEach(module => {
if (_fs.default.lstatSync(module).isDirectory()) {
const moduleConfig = this.getModuleConfig(module);
moduleConfig.dirName = _path.default.parse(module).name;
configs.push(moduleConfig);
}
});
}
return configs;
}
/**
* Summarizes all dependencies defined in .desktop.
*
* @params {Object} settings - settings.json
* @params {boolean} checkModules - whether to gather modules dependencies
* @params {boolean} refresh - recompute
* @returns {{fromSettings: {}, plugins: {}, modules: {}}}
*/
getDependencies(settings = null, checkModules = true, refresh = false) {
if (!refresh && this.dependencies) {
return this.dependencies;
}
const dependencies = {
fromSettings: {},
plugins: {},
modules: {}
};
/** @type {desktopSettings} * */
const settingsJson = settings || this.getSettings(); // Settings can have a 'dependencies' field.
if ('dependencies' in settingsJson) {
dependencies.fromSettings = settingsJson.dependencies;
} // Plugins are also a npm packages.
if ('plugins' in settingsJson) {
dependencies.plugins = Object.keys(settingsJson.plugins).reduce((plugins, plugin) => {
/* eslint-disable no-param-reassign */
if (typeof settingsJson.plugins[plugin] === 'object') {
plugins[plugin] = settingsJson.plugins[plugin].version;
} else {
plugins[plugin] = settingsJson.plugins[plugin];
}
return plugins;
}, {});
} // Each module can have its own dependencies defined.
const moduleDependencies = {};
if (checkModules) {
const configs = this.gatherModuleConfigs();
configs.forEach(moduleConfig => {
if (!('dependencies' in moduleConfig)) {
moduleConfig.dependencies = {};
}
if (moduleConfig.name in moduleDependencies) {
this.log.error(`duplicate name '${moduleConfig.name}' in 'module.json' in ` + `'${moduleConfig.dirName}' - another module already registered the same name.`);
process.exit(1);
}
moduleDependencies[moduleConfig.name] = moduleConfig.dependencies;
});
}
dependencies.modules = moduleDependencies;
this.dependencies = dependencies;
return dependencies;
}
/**
* Copies the .desktop scaffold into the meteor app dir.
* Adds entry to .meteor/.gitignore.
*/
scaffold() {
this.log.info('creating .desktop scaffold in your project');
if (this.$.utils.exists(this.$.env.paths.desktop.root)) {
this.log.warn('.desktop already exists - delete it if you want a new one to be ' + 'created');
return;
}
_shelljs.default.cp('-r', this.$.env.paths.scaffold, this.$.env.paths.desktop.root);
_shelljs.default.mkdir(this.$.env.paths.desktop.import);
this.log.info('.desktop directory prepared');
}
/**
* Verifies if all mandatory files are present in the .desktop.
*
* @returns {boolean}
*/
check() {
this.log.verbose('checking .desktop existence');
return !!(this.$.utils.exists(this.$.env.paths.desktop.root) && this.$.utils.exists(this.$.env.paths.desktop.settings) && this.$.utils.exists(this.$.env.paths.desktop.desktop));
}
}
/**
* @typedef {Object} desktopSettings
* @property {string} name
* @property {string} projectName
* @property {boolean} devTools
* @property {boolean} devtron
* @property {boolean} desktopHCP
* @property {Object} squirrel
* @property {string} squirrel.autoUpdateFeedUrl
* @property {Object} squirrel.autoUpdateFeedHeaders
* @property {Object} squirrel.autoUpdateCheckOnStart
* @property {Object} desktopHCPSettings
* @property {boolean} desktopHCPSettings.ignoreCompatibilityVersion
* @property {boolean} desktopHCPSettings.blockAppUpdateOnDesktopIncompatibility
* @property {number} webAppStartupTimeout
* @property {Array} linkPackages
* @property {Array} exposedModules
* @property {Object} window
* @property {Object} windowDev
* @property {Object} packageJsonFields
* @property {Object} builderOptions
* @property {Object} builderCliOptions
* @property {Object} packagerOptions
* @property {Object} plugins
* @property {Object} dependencies
* @property {boolean} uglify
* @property {string} version
* */
exports.default = Desktop;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL2xpYi9kZXNrdG9wLmpzIl0sIm5hbWVzIjpbInNoZWxsIiwiY29uZmlnIiwiZmF0YWwiLCJpc0VtcHR5U3luYyIsInNlYXJjaFBhdGgiLCJzdGF0IiwiZnMiLCJzdGF0U3luYyIsImUiLCJpc0RpcmVjdG9yeSIsIml0ZW1zIiwicmVhZGRpclN5bmMiLCJsZW5ndGgiLCJEZXNrdG9wIiwiY29uc3RydWN0b3IiLCIkIiwibG9nIiwiTG9nIiwic2V0dGluZ3MiLCJkZXBlbmRlbmNpZXMiLCJnZXRTZXR0aW5ncyIsIkpTT04iLCJwYXJzZSIsInJlYWRGaWxlU3luYyIsImVudiIsInBhdGhzIiwiZGVza3RvcCIsImVycm9yIiwicHJvY2VzcyIsImV4aXQiLCJnZXRIYXNoVmVyc2lvbiIsImluZm8iLCJ2ZXJzaW9uIiwidXRpbHMiLCJyZWFkRmlsZXNBbmRDb21wdXRlSGFzaCIsInJvb3QiLCJ2ZXJib3NlIiwiaGFzaCIsImdldE1vZHVsZUNvbmZpZyIsIm1vZHVsZVBhdGgiLCJtb2R1bGVDb25maWciLCJwYXRoIiwiam9pbiIsImdhdGhlck1vZHVsZUNvbmZpZ3MiLCJjb25maWdzIiwibW9kdWxlcyIsImxzIiwiZm9yRWFjaCIsIm1vZHVsZSIsImxzdGF0U3luYyIsImRpck5hbWUiLCJuYW1lIiwicHVzaCIsImdldERlcGVuZGVuY2llcyIsImNoZWNrTW9kdWxlcyIsInJlZnJlc2giLCJmcm9tU2V0dGluZ3MiLCJwbHVnaW5zIiwic2V0dGluZ3NKc29uIiwiT2JqZWN0Iiwia2V5cyIsInJlZHVjZSIsInBsdWdpbiIsIm1vZHVsZURlcGVuZGVuY2llcyIsInNjYWZmb2xkIiwiZXhpc3RzIiwid2FybiIsImNwIiwibWtkaXIiLCJpbXBvcnQiLCJjaGVjayJdLCJtYXBwaW5ncyI6Ijs7Ozs7OztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUVBOzs7O0FBTkE7QUFRQUEsaUJBQU1DLE1BQU4sQ0FBYUMsS0FBYixHQUFxQixJQUFyQjtBQUVBOzs7Ozs7QUFLQSxT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TCxDQUFPa0IsS0FBUCxDQUFhZ0MsTUFBYixDQUFvQixLQUFLbEQsQ0FBTCxDQUFPUyxHQUFQLENBQVdDLEtBQVgsQ0FBaUJDLE9BQWpCLENBQXlCUyxJQUE3QyxLQUNOLEtBQUtwQixDQUFMLENBQU9rQixLQUFQLENBQWFnQyxNQUFiLENBQW9CLEtBQUtsRCxDQUFMLENBQU9TLEdBQVAsQ0FBV0MsS0FBWCxDQUFpQkMsT0FBakIsQ0FBeUJSLFFBQTdDLENBRE0sSUFFTixLQUFLSCxDQUFMLENBQU9rQixLQUFQLENBQWFnQyxNQUFiLENBQW9CLEtBQUtsRCxDQUFMLENBQU9TLEdBQVAsQ0FBV0MsS0FBWCxDQUFpQkMsT0FBakIsQ0FBeUJBLE9BQTdDLENBRkksQ0FBUjtBQUdIOztBQXpMd0I7QUE0TDdCIiwic291cmNlc0NvbnRlbnQiOlsiLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVudXNlZC12YXJzXG5pbXBvcnQgcmVnZW5lcmF0b3JSdW50aW1lIGZyb20gJ3JlZ2VuZXJhdG9yLXJ1bnRpbWUvcnVudGltZSc7XG5pbXBvcnQgc2hlbGwgZnJvbSAnc2hlbGxqcyc7XG5pbXBvcnQgZnMgZnJvbSAnZnMnO1xuaW1wb3J0IHBhdGggZnJvbSAncGF0aCc7XG5cbmltcG9ydCBMb2cgZnJvbSAnLi9sb2cnO1xuXG5zaGVsbC5jb25maWcuZmF0YWwgPSB0cnVlO1xuXG4vKipcbiAqIENoZWNrcyBpZiB0aGUgcGF0aCBpcyBlbXB0eS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBzZWFyY2hQYXRoXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAqL1xuZnVuY3Rpb24gaXNFbXB0eVN5bmMoc2VhcmNoUGF0aCkge1xuICAgIGxldCBzdGF0O1xuICAgIHRyeSB7XG4gICAgICAgIHN0YXQgPSBmcy5zdGF0U3luYyhzZWFyY2hQYXRoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICBpZiAoc3RhdC5pc0RpcmVjdG9yeSgpKSB7XG4gICAgICAgIGNvbnN0IGl0ZW1zID0gZnMucmVhZGRpclN5bmMoc2VhcmNoUGF0aCk7XG4gICAgICAgIHJldHVybiAhaXRlbXMgfHwgIWl0ZW1zLmxlbmd0aDtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xufVxuXG4vKipcbiAqIFJlcHJlc2VudHMgdGhlIC5kZXNrdG9wIGRpcmVjdG9yeS5cbiAqIEBjbGFzc1xuICogQHByb3BlcnR5IHtkZXNrdG9wU2V0dGluZ3N9IHNldHRpbmdzXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIERlc2t0b3Age1xuICAgIC8qKlxuICAgICAqIEBwYXJhbSB7TWV0ZW9yRGVza3RvcH0gJCAtIGNvbnRleHRcbiAgICAgKlxuICAgICAqIEBjb25zdHJ1Y3RvclxuICAgICAqL1xuICAgIGNvbnN0cnVjdG9yKCQpIHtcbiAgICAgICAgdGhpcy4kID0gJDtcbiAgICAgICAgdGhpcy5sb2cgPSBuZXcgTG9nKCdkZXNrdG9wJyk7XG4gICAgICAgIHRoaXMuc2V0dGluZ3MgPSBudWxsO1xuICAgICAgICB0aGlzLmRlcGVuZGVuY2llcyA9IG51bGw7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVHJpZXMgdG8gcmVhZCBhbmQgcmV0dXJucyBzZXR0aW5ncy5qc29uIGNvbnRlbnRzIGZyb20gLmRlc2t0b3AgZGlyLlxuICAgICAqXG4gICAgICogQHJldHVybnMge2Rlc2t0b3BTZXR0aW5nc3xudWxsfVxuICAgICAqL1xuICAgIGdldFNldHRpbmdzKCkge1xuICAgICAgICBpZiAoIXRoaXMuc2V0dGluZ3MpIHtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgdGhpcy5zZXR0aW5ncyA9IEpTT04ucGFyc2UoXG4gICAgICAgICAgICAgICAgICAgIGZzLnJlYWRGaWxlU3luYyh0aGlzLiQuZW52LnBhdGhzLmRlc2t0b3Auc2V0dGluZ3MsICdVVEYtOCcpXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvZy5lcnJvcignZXJyb3Igd2hpbGUgdHJ5aW5nIHRvIHJlYWQgXFwnLmRlc2t0b3Avc2V0dGluZ3MuanNvblxcJzogJywgZSk7XG4gICAgICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLnNldHRpbmdzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgYSB2ZXJzaW9uIGhhc2ggcmVwcmVzZW50aW5nIGN1cnJlbnQgLmRlc2t0b3AgY29udGVudHMuXG4gICAgICogQHJldHVybnMge3N0cmluZ31cbiAgICAgKi9cbiAgICBhc3luYyBnZXRIYXNoVmVyc2lvbigpIHtcbiAgICAgICAgdGhpcy5sb2cuaW5mbygnY2FsY3VsYXRpbmcgaGFzaCB2ZXJzaW9uIGZyb20gLmRlc2t0b3AgY29udGVudHMnKTtcblxuICAgICAgICBjb25zdCB2ZXJzaW9uID0gYXdhaXQgdGhpcy4kLnV0aWxzLnJlYWRGaWxlc0FuZENvbXB1dGVIYXNoKHRoaXMuJC5lbnYucGF0aHMuZGVza3RvcC5yb290KTtcblxuICAgICAgICB0aGlzLmxvZy52ZXJib3NlKGBjYWxjdWxhdGVkIC5kZXNrdG9wIGhhc2ggdmVyc2lvbiBpcyAke3ZlcnNpb24uaGFzaH1gKTtcbiAgICAgICAgcmV0dXJuIHZlcnNpb24uaGFzaDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBUcmllcyB0byByZWFkIGEgbW9kdWxlLmpzb24gZmlsZSBmcm9tIGEgbW9kdWxlIGF0IHByb3ZpZGVkIHBhdGguXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gbW9kdWxlUGF0aCAtIHBhdGggdG8gdGhlIG1vZHVsZSBkaXJcbiAgICAgKiBAcmV0dXJucyB7T2JqZWN0fVxuICAgICAqL1xuICAgIGdldE1vZHVsZUNvbmZpZyhtb2R1bGVQYXRoKSB7XG4gICAgICAgIGxldCBtb2R1bGVDb25maWcgPSB7fTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIG1vZHVsZUNvbmZpZyA9IEpTT04ucGFyc2UoXG4gICAgICAgICAgICAgICAgZnMucmVhZEZpbGVTeW5jKHBhdGguam9pbihtb2R1bGVQYXRoLCAnbW9kdWxlLmpzb24nKSwgJ1VURi04JylcbiAgICAgICAgICAgICk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHRoaXMubG9nLmVycm9yKFxuICAgICAgICAgICAgICAgIGBlcnJvciB3aGlsZSB0cnlpbmcgdG8gcmVhZCAnbW9kdWxlLmpzb24nIGZyb20gJyR7bW9kdWxlUGF0aH0nIG1vZHVsZTogYCxcbiAgICAgICAgICAgICAgICBlXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgICAgICB9XG4gICAgICAgIGlmICghKCduYW1lJyBpbiBtb2R1bGVDb25maWcpKSB7XG4gICAgICAgICAgICB0aGlzLmxvZy5lcnJvcihgbm8gJ25hbWUnIGZpZWxkIGRlZmluZWQgaW4gJ21vZHVsZS5qc29uJyBpbiAnJHttb2R1bGVQYXRofScgbW9kdWxlLmApO1xuICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBtb2R1bGVDb25maWc7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2NhbnMgYWxsIG1vZHVsZXMgZm9yIG1vZHVsZS5qc29uIGFuZCBnYXRoZXJzIHRoaXMgY29uZmlndXJhdGlvbiBhbHRvZ2V0aGVyLlxuICAgICAqXG4gICAgICogQHJldHVybnMge1tdfVxuICAgICAqL1xuICAgIGdhdGhlck1vZHVsZUNvbmZpZ3MoKSB7XG4gICAgICAgIGNvbnN0IGNvbmZpZ3MgPSBbXTtcblxuICAgICAgICBpZiAoIWlzRW1wdHlTeW5jKHRoaXMuJC5lbnYucGF0aHMuZGVza3RvcC5tb2R1bGVzKSkge1xuICAgICAgICAgICAgc2hlbGwubHMoJy1kJywgcGF0aC5qb2luKHRoaXMuJC5lbnYucGF0aHMuZGVza3RvcC5tb2R1bGVzLCAnKicpKS5mb3JFYWNoKFxuICAgICAgICAgICAgICAgIChtb2R1bGUpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGZzLmxzdGF0U3luYyhtb2R1bGUpLmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IG1vZHVsZUNvbmZpZyA9IHRoaXMuZ2V0TW9kdWxlQ29uZmlnKG1vZHVsZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVDb25maWcuZGlyTmFtZSA9IHBhdGgucGFyc2UobW9kdWxlKS5uYW1lO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlncy5wdXNoKG1vZHVsZUNvbmZpZyk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBjb25maWdzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFN1bW1hcml6ZXMgYWxsIGRlcGVuZGVuY2llcyBkZWZpbmVkIGluIC5kZXNrdG9wLlxuICAgICAqXG4gICAgICogQHBhcmFtcyB7T2JqZWN0fSBzZXR0aW5ncyAgICAgIC0gc2V0dGluZ3MuanNvblxuICAgICAqIEBwYXJhbXMge2Jvb2xlYW59IGNoZWNrTW9kdWxlcyAtIHdoZXRoZXIgdG8gZ2F0aGVyIG1vZHVsZXMgZGVwZW5kZW5jaWVzXG4gICAgICogQHBhcmFtcyB7Ym9vbGVhbn0gcmVmcmVzaCAgICAgIC0gcmVjb21wdXRlXG4gICAgICogQHJldHVybnMge3tmcm9tU2V0dGluZ3M6IHt9LCBwbHVnaW5zOiB7fSwgbW9kdWxlczoge319fVxuICAgICAqL1xuICAgIGdldERlcGVuZGVuY2llcyhzZXR0aW5ncyA9IG51bGwsIGNoZWNrTW9kdWxlcyA9IHRydWUsIHJlZnJlc2ggPSBmYWxzZSkge1xuICAgICAgICBpZiAoIXJlZnJlc2ggJiYgdGhpcy5kZXBlbmRlbmNpZXMpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmRlcGVuZGVuY2llcztcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGRlcGVuZGVuY2llcyA9IHtcbiAgICAgICAgICAgIGZyb21TZXR0aW5nczoge30sXG4gICAgICAgICAgICBwbHVnaW5zOiB7fSxcbiAgICAgICAgICAgIG1vZHVsZXM6IHt9XG4gICAgICAgIH07XG4gICAgICAgIC8qKiBAdHlwZSB7ZGVza3RvcFNldHRpbmdzfSAqICovXG4gICAgICAgIGNvbnN0IHNldHRpbmdzSnNvbiA9IHNldHRpbmdzIHx8IHRoaXMuZ2V0U2V0dGluZ3MoKTtcblxuICAgICAgICAvLyBTZXR0aW5ncyBjYW4gaGF2ZSBhICdkZXBlbmRlbmNpZXMnIGZpZWxkLlxuICAgICAgICBpZiAoJ2RlcGVuZGVuY2llcycgaW4gc2V0dGluZ3NKc29uKSB7XG4gICAgICAgICAgICBkZXBlbmRlbmNpZXMuZnJvbVNldHRpbmdzID0gc2V0dGluZ3NKc29uLmRlcGVuZGVuY2llcztcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFBsdWdpbnMgYXJlIGFsc28gYSBucG0gcGFja2FnZXMuXG4gICAgICAgIGlmICgncGx1Z2lucycgaW4gc2V0dGluZ3NKc29uKSB7XG4gICAgICAgICAgICBkZXBlbmRlbmNpZXMucGx1Z2lucyA9IE9iamVjdC5rZXlzKHNldHRpbmdzSnNvbi5wbHVnaW5zKS5yZWR1Y2UoKHBsdWdpbnMsIHBsdWdpbikgPT4ge1xuICAgICAgICAgICAgICAgIC8qIGVzbGludC1kaXNhYmxlIG5vLXBhcmFtLXJlYXNzaWduICovXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBzZXR0aW5nc0pzb24ucGx1Z2luc1twbHVnaW5dID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgICAgICAgICBwbHVnaW5zW3BsdWdpbl0gPSBzZXR0aW5nc0pzb24ucGx1Z2luc1twbHVnaW5dLnZlcnNpb247XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgcGx1Z2luc1twbHVnaW5dID0gc2V0dGluZ3NKc29uLnBsdWdpbnNbcGx1Z2luXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuIHBsdWdpbnM7XG4gICAgICAgICAgICB9LCB7fSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBFYWNoIG1vZHVsZSBjYW4gaGF2ZSBpdHMgb3duIGRlcGVuZGVuY2llcyBkZWZpbmVkLlxuICAgICAgICBjb25zdCBtb2R1bGVEZXBlbmRlbmNpZXMgPSB7fTtcbiAgICAgICAgaWYgKGNoZWNrTW9kdWxlcykge1xuICAgICAgICAgICAgY29uc3QgY29uZmlncyA9IHRoaXMuZ2F0aGVyTW9kdWxlQ29uZmlncygpO1xuXG4gICAgICAgICAgICBjb25maWdzLmZvckVhY2goXG4gICAgICAgICAgICAgICAgKG1vZHVsZUNvbmZpZykgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAoISgnZGVwZW5kZW5jaWVzJyBpbiBtb2R1bGVDb25maWcpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVDb25maWcuZGVwZW5kZW5jaWVzID0ge307XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgaWYgKG1vZHVsZUNvbmZpZy5uYW1lIGluIG1vZHVsZURlcGVuZGVuY2llcykge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2cuZXJyb3IoYGR1cGxpY2F0ZSBuYW1lICcke21vZHVsZUNvbmZpZy5uYW1lfScgaW4gJ21vZHVsZS5qc29uJyBpbiBgICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBgJyR7bW9kdWxlQ29uZmlnLmRpck5hbWV9JyAtIGFub3RoZXIgbW9kdWxlIGFscmVhZHkgcmVnaXN0ZXJlZCB0aGUgc2FtZSBuYW1lLmApO1xuICAgICAgICAgICAgICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIG1vZHVsZURlcGVuZGVuY2llc1ttb2R1bGVDb25maWcubmFtZV0gPSBtb2R1bGVDb25maWcuZGVwZW5kZW5jaWVzO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBkZXBlbmRlbmNpZXMubW9kdWxlcyA9IG1vZHVsZURlcGVuZGVuY2llcztcbiAgICAgICAgdGhpcy5kZXBlbmRlbmNpZXMgPSBkZXBlbmRlbmNpZXM7XG4gICAgICAgIHJldHVybiBkZXBlbmRlbmNpZXM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ29waWVzIHRoZSAuZGVza3RvcCBzY2FmZm9sZCBpbnRvIHRoZSBtZXRlb3IgYXBwIGRpci5cbiAgICAgKiBBZGRzIGVudHJ5IHRvIC5tZXRlb3IvLmdpdGlnbm9yZS5cbiAgICAgKi9cbiAgICBzY2FmZm9sZCgpIHtcbiAgICAgICAgdGhpcy5sb2cuaW5mbygnY3JlYXRpbmcgLmRlc2t0b3Agc2NhZmZvbGQgaW4geW91ciBwcm9qZWN0Jyk7XG5cbiAgICAgICAgaWYgKHRoaXMuJC51dGlscy5leGlzdHModGhpcy4kLmVudi5wYXRocy5kZXNrdG9wLnJvb3QpKSB7XG4gICAgICAgICAgICB0aGlzLmxvZy53YXJuKCcuZGVza3RvcCBhbHJlYWR5IGV4aXN0cyAtIGRlbGV0ZSBpdCBpZiB5b3Ugd2FudCBhIG5ldyBvbmUgdG8gYmUgJyArXG4gICAgICAgICAgICAgICAgJ2NyZWF0ZWQnKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHNoZWxsLmNwKCctcicsIHRoaXMuJC5lbnYucGF0aHMuc2NhZmZvbGQsIHRoaXMuJC5lbnYucGF0aHMuZGVza3RvcC5yb290KTtcbiAgICAgICAgc2hlbGwubWtkaXIodGhpcy4kLmVudi5wYXRocy5kZXNrdG9wLmltcG9ydCk7XG4gICAgICAgIHRoaXMubG9nLmluZm8oJy5kZXNrdG9wIGRpcmVjdG9yeSBwcmVwYXJlZCcpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFZlcmlmaWVzIGlmIGFsbCBtYW5kYXRvcnkgZmlsZXMgYXJlIHByZXNlbnQgaW4gdGhlIC5kZXNrdG9wLlxuICAgICAqXG4gICAgICogQHJldHVybnMge2Jvb2xlYW59XG4gICAgICovXG4gICAgY2hlY2soKSB7XG4gICAgICAgIHRoaXMubG9nLnZlcmJvc2UoJ2NoZWNraW5nIC5kZXNrdG9wIGV4aXN0ZW5jZScpO1xuICAgICAgICByZXR1cm4gISEodGhpcy4kLnV0aWxzLmV4aXN0cyh0aGlzLiQuZW52LnBhdGhzLmRlc2t0b3Aucm9vdCkgJiZcbiAgICAgICAgICAgIHRoaXMuJC51dGlscy5leGlzdHModGhpcy4kLmVudi5wYXRocy5kZXNrdG9wLnNldHRpbmdzKSAmJlxuICAgICAgICAgICAgdGhpcy4kLnV0aWxzLmV4aXN0cyh0aGlzLiQuZW52LnBhdGhzLmRlc2t0b3AuZGVza3RvcCkpO1xuICAgIH1cbn1cblxuLyoqXG4gKiBAdHlwZWRlZiB7T2JqZWN0fSBkZXNrdG9wU2V0dGluZ3NcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBuYW1lXG4gKiBAcHJvcGVydHkge3N0cmluZ30gcHJvamVjdE5hbWVcbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gZGV2VG9vbHNcbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gZGV2dHJvblxuICogQHByb3BlcnR5IHtib29sZWFufSBkZXNrdG9wSENQXG4gKiBAcHJvcGVydHkge09iamVjdH0gc3F1aXJyZWxcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBzcXVpcnJlbC5hdXRvVXBkYXRlRmVlZFVybFxuICogQHByb3BlcnR5IHtPYmplY3R9IHNxdWlycmVsLmF1dG9VcGRhdGVGZWVkSGVhZGVyc1xuICogQHByb3BlcnR5IHtPYmplY3R9IHNxdWlycmVsLmF1dG9VcGRhdGVDaGVja09uU3RhcnRcbiAqIEBwcm9wZXJ0eSB7T2JqZWN0fSBkZXNrdG9wSENQU2V0dGluZ3NcbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gZGVza3RvcEhDUFNldHRpbmdzLmlnbm9yZUNvbXBhdGliaWxpdHlWZXJzaW9uXG4gKiBAcHJvcGVydHkge2Jvb2xlYW59IGRlc2t0b3BIQ1BTZXR0aW5ncy5ibG9ja0FwcFVwZGF0ZU9uRGVza3RvcEluY29tcGF0aWJpbGl0eVxuICogQHByb3BlcnR5IHtudW1iZXJ9IHdlYkFwcFN0YXJ0dXBUaW1lb3V0XG4gKiBAcHJvcGVydHkge0FycmF5fSBsaW5rUGFja2FnZXNcbiAqIEBwcm9wZXJ0eSB7QXJyYXl9IGV4cG9zZWRNb2R1bGVzXG4gKiBAcHJvcGVydHkge09iamVjdH0gd2luZG93XG4gKiBAcHJvcGVydHkge09iamVjdH0gd2luZG93RGV2XG4gKiBAcHJvcGVydHkge09iamVjdH0gcGFja2FnZUpzb25GaWVsZHNcbiAqIEBwcm9wZXJ0eSB7T2JqZWN0fSBidWlsZGVyT3B0aW9uc1xuICogQHByb3BlcnR5IHtPYmplY3R9IGJ1aWxkZXJDbGlPcHRpb25zXG4gKiBAcHJvcGVydHkge09iamVjdH0gcGFja2FnZXJPcHRpb25zXG4gKiBAcHJvcGVydHkge09iamVjdH0gcGx1Z2luc1xuICogQHByb3BlcnR5IHtPYmplY3R9IGRlcGVuZGVuY2llc1xuICogQHByb3BlcnR5IHtib29sZWFufSB1Z2xpZnlcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSB2ZXJzaW9uXG4gKiAqL1xuIl19