meteor-desktop
Version:
Build a Meteor's desktop client with hot code push.
923 lines (735 loc) • 114 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _runtime = _interopRequireDefault(require("regenerator-runtime/runtime"));
var _fs = _interopRequireDefault(require("fs"));
var _crossSpawn = _interopRequireDefault(require("cross-spawn"));
var _semver = _interopRequireDefault(require("semver"));
var _shelljs = _interopRequireDefault(require("shelljs"));
var _path = _interopRequireDefault(require("path"));
var _singleLineLog = _interopRequireDefault(require("single-line-log"));
var _asar = _interopRequireDefault(require("asar"));
var _nodeFetch = _interopRequireDefault(require("node-fetch"));
var _isDesktopInjector = _interopRequireDefault(require("../skeleton/modules/autoupdate/isDesktopInjector"));
var _log = _interopRequireDefault(require("./log"));
var _meteorManager = _interopRequireDefault(require("./meteorManager"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// eslint-disable-next-line no-unused-vars
const {
join
} = _path.default;
const sll = _singleLineLog.default.stdout; // TODO: refactor all strategy ifs to one place
/**
* Represents the Meteor app.
* @property {MeteorDesktop} $
* @class
*/
class MeteorApp {
/**
* @param {MeteorDesktop} $ - context
* @constructor
*/
constructor($) {
this.log = new _log.default('meteorApp');
this.$ = $;
this.meteorManager = new _meteorManager.default($);
this.mobilePlatform = null;
this.oldManifest = null;
this.injector = new _isDesktopInjector.default();
this.matcher = new RegExp('__meteor_runtime_config__ = JSON.parse\\(decodeURIComponent\\("([^"]*)"\\)\\)');
this.replacer = new RegExp('(__meteor_runtime_config__ = JSON.parse\\(decodeURIComponent\\()"([^"]*)"(\\)\\))');
this.meteorVersion = null;
this.indexHTMLstrategy = null;
this.indexHTMLStrategies = {
INDEX_FROM_CORDOVA_BUILD: 1,
INDEX_FROM_RUNNING_SERVER: 2
};
this.deprectatedPackages = ['omega:meteor-desktop-localstorage'];
}
/**
* Remove any deprecated packages from meteor project.
* @returns {Promise<void>}
*/
async removeDeprecatedPackages() {
try {
if (this.meteorManager.checkPackages(this.deprectatedPackages)) {
this.log.info('deprecated meteor plugins found, removing them');
await this.meteorManager.deletePackages(this.deprectatedPackages);
}
} catch (e) {
throw new Error(e);
}
}
/**
* Ensures that required packages are added to the Meteor app.
*/
async ensureDesktopHCPPackages() {
const desktopHCPPackages = ['omega:meteor-desktop-watcher', 'omega:meteor-desktop-bundler'];
if (this.$.desktop.getSettings().desktopHCP) {
this.log.verbose('desktopHCP is enabled, checking for required packages');
const packagesWithVersion = desktopHCPPackages.map(packageName => `${packageName}@${this.$.getVersion()}`);
try {
await this.meteorManager.ensurePackages(desktopHCPPackages, packagesWithVersion, 'desktopHCP');
} catch (e) {
throw new Error(e);
}
} else {
this.log.verbose('desktopHCP is not enabled, removing required packages');
try {
if (this.meteorManager.checkPackages(desktopHCPPackages)) {
await this.meteorManager.deletePackages(desktopHCPPackages);
}
} catch (e) {
throw new Error(e);
}
}
}
/**
* Adds entry to .meteor/.gitignore if necessary.
*/
updateGitIgnore() {
this.log.verbose('updating .meteor/.gitignore'); // Lets read the .meteor/.gitignore and filter out blank lines.
const gitIgnore = _fs.default.readFileSync(this.$.env.paths.meteorApp.gitIgnore, 'UTF-8').split('\n').filter(ignoredPath => ignoredPath.trim() !== '');
if (!~gitIgnore.indexOf(this.$.env.paths.electronApp.rootName)) {
this.log.verbose(`adding ${this.$.env.paths.electronApp.rootName} to .meteor/.gitignore`);
gitIgnore.push(this.$.env.paths.electronApp.rootName);
_fs.default.writeFileSync(this.$.env.paths.meteorApp.gitIgnore, gitIgnore.join('\n'), 'UTF-8');
}
}
/**
* Reads the Meteor release version used in the app.
* @returns {string}
*/
getMeteorRelease() {
let release = _fs.default.readFileSync(this.$.env.paths.meteorApp.release, 'UTF-8').replace(/\r/gm, '').split('\n')[0];
[, release] = release.split('@'); // We do not care if it is beta.
if (~release.indexOf('-')) {
[release] = release.split('-');
}
return release;
}
/**
* Cast Meteor release to semver version.
* @returns {string}
*/
castMeteorReleaseToSemver() {
return `${this.getMeteorRelease()}.0.0`.match(/(^\d+\.\d+\.\d+)/gmi)[0];
}
/**
* Validate meteor version against a versionRange.
* @param {string} versionRange - semver version range
*/
checkMeteorVersion(versionRange) {
const release = this.castMeteorReleaseToSemver();
if (!_semver.default.satisfies(release, versionRange)) {
if (this.$.env.options.skipMobileBuild) {
this.log.error(`wrong meteor version (${release}) in project - only ` + `${versionRange} is supported`);
} else {
this.log.error(`wrong meteor version (${release}) in project - only ` + `${versionRange} is supported for automatic meteor builds (you can always ` + 'try with `--skip-mobile-build` if you are using meteor >= 1.2.1');
}
process.exit(1);
}
}
/**
* Decides which strategy to use while trying to get client build out of Meteor project.
* @returns {number}
*/
chooseStrategy() {
if (this.$.env.options.forceCordovaBuild) {
return this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD;
}
const release = this.castMeteorReleaseToSemver();
if (_semver.default.satisfies(release, '> 1.3.4')) {
return this.indexHTMLStrategies.INDEX_FROM_RUNNING_SERVER;
}
if (_semver.default.satisfies(release, '1.3.4')) {
const explodedVersion = this.getMeteorRelease().split('.');
if (explodedVersion.length >= 4) {
if (explodedVersion[3] > 1) {
return this.indexHTMLStrategies.INDEX_FROM_RUNNING_SERVER;
}
return this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD;
}
}
return this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD;
}
/**
* Checks required preconditions.
* - Meteor version
* - is mobile platform added
*/
async checkPreconditions() {
if (this.$.env.options.skipMobileBuild) {
this.checkMeteorVersion('>= 1.2.1');
} else {
this.checkMeteorVersion('>= 1.3.3');
this.indexHTMLstrategy = this.chooseStrategy();
if (this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD) {
this.log.debug('meteor version is < 1.3.4.2 so the index.html from cordova-build will' + ' be used');
} else {
this.log.debug('meteor version is >= 1.3.4.2 so the index.html will be downloaded ' + 'from __cordova/index.html');
}
}
if (!this.$.env.options.skipMobileBuild) {
const platforms = _fs.default.readFileSync(this.$.env.paths.meteorApp.platforms, 'UTF-8');
if (!~platforms.indexOf('android') && !~platforms.indexOf('ios')) {
if (!this.$.env.options.android) {
this.mobilePlatform = 'ios';
} else {
this.mobilePlatform = 'android';
}
this.log.warn(`no mobile target detected - will add '${this.mobilePlatform}' ` + 'just to get a mobile build');
try {
await this.addMobilePlatform(this.mobilePlatform);
} catch (e) {
this.log.error('failed to add a mobile platform - please try to do it manually');
process.exit(1);
}
}
}
}
/**
* Tries to add a mobile platform to meteor project.
* @param {string} platform - platform to add
* @returns {Promise}
*/
addMobilePlatform(platform) {
return new Promise((resolve, reject) => {
this.log.verbose(`adding mobile platform: ${platform}`);
(0, _crossSpawn.default)('meteor', ['add-platform', platform], {
cwd: this.$.env.paths.meteorApp.root,
stdio: this.$.env.stdio
}).on('exit', () => {
const platforms = _fs.default.readFileSync(this.$.env.paths.meteorApp.platforms, 'UTF-8');
if (!~platforms.indexOf('android') && !~platforms.indexOf('ios')) {
reject();
} else {
resolve();
}
});
});
}
/**
* Tries to remove a mobile platform from meteor project.
* @param {string} platform - platform to remove
* @returns {Promise}
*/
removeMobilePlatform(platform) {
if (this.$.env.options.skipRemoveMobilePlatform) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
this.log.verbose(`removing mobile platform: ${platform}`);
(0, _crossSpawn.default)('meteor', ['remove-platform', platform], {
cwd: this.$.env.paths.meteorApp.root,
stdio: this.$.env.stdio,
env: Object.assign({
METEOR_PRETTY_OUTPUT: 0
}, process.env)
}).on('exit', () => {
const platforms = _fs.default.readFileSync(this.$.env.paths.meteorApp.platforms, 'UTF-8');
if (~platforms.indexOf(platform)) {
reject();
} else {
resolve();
}
});
});
}
/**
* Just checks for index.html and program.json existence.
* @returns {boolean}
*/
isCordovaBuildReady() {
if (this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD) {
return this.$.utils.exists(this.$.env.paths.meteorApp.cordovaBuildIndex) && this.$.utils.exists(this.$.env.paths.meteorApp.cordovaBuildProgramJson) && (!this.oldManifest || this.oldManifest && this.oldManifest !== _fs.default.readFileSync(this.$.env.paths.meteorApp.cordovaBuildProgramJson, 'UTF-8'));
}
return this.$.utils.exists(this.$.env.paths.meteorApp.webCordovaProgramJson) && (!this.oldManifest || this.oldManifest && this.oldManifest !== _fs.default.readFileSync(this.$.env.paths.meteorApp.webCordovaProgramJson, 'UTF-8'));
}
/**
* Fetches index.html from running project.
* @returns {Promise.<*>}
*/
async acquireIndex() {
const port = this.$.env.options.port ? this.$.env.options.port : 3080;
this.log.info('acquiring index.html');
const res = await (0, _nodeFetch.default)(`http://127.0.0.1:${port}/__cordova/index.html`);
const text = await res.text(); // Simple test if we really download index.html for web.cordova.
if (~text.indexOf('src="/cordova.js"')) {
return text;
}
return false;
}
/**
* Fetches mainfest.json from running project.
* @returns {Promise.<void>}
*/
async acquireManifest() {
const port = this.$.env.options.port ? this.$.env.options.port : 3080;
this.log.info('acquiring manifest.json');
const res = await (0, _nodeFetch.default)(`http://127.0.0.1:${port}/__cordova/manifest.json?meteor_dont_serve_index=true`);
const text = await res.text();
return JSON.parse(text);
}
/**
* Tries to get a mobile build from meteor app.
* In case of failure leaves a meteor.log.
* A lot of stuff is happening here - but the main aim is to get a mobile build from
* .meteor/local/cordova-build/www/application and exit as soon as possible.
*
* @returns {Promise}
*/
buildMobileTarget() {
const programJson = this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD ? this.$.env.paths.meteorApp.cordovaBuildProgramJson : this.$.env.paths.meteorApp.webCordovaProgramJson;
if (this.$.utils.exists(programJson)) {
this.oldManifest = _fs.default.readFileSync(programJson, 'UTF-8');
}
return new Promise((resolve, reject) => {
const self = this;
let log = '';
let desiredExit = false;
let buildTimeout = null;
let errorTimeout = null;
let messageTimeout = null;
let killTimeout = null;
let cordovaCheckInterval = null;
let portProblem = false;
function windowsKill(pid) {
self.log.debug(`killing pid: ${pid}`);
_crossSpawn.default.sync('taskkill', ['/pid', pid, '/f', '/t']); // We will look for other process which might have been created outside the
// process tree.
// Lets list all node.exe processes.
const out = _crossSpawn.default.sync('wmic', ['process', 'where', 'caption="node.exe"', 'get', 'commandline,processid']).stdout.toString('utf-8').split('\n');
const args = self.prepareArguments(); // Lets mount regex.
const regexV1 = new RegExp(`${args.join('\\s+')}\\s+(\\d+)`, 'gm');
const regexV2 = new RegExp(`"${args.join('"\\s+"')}"\\s+(\\d+)`, 'gm'); // No we will check for those with the matching params.
out.forEach(line => {
const match = regexV1.exec(line) || regexV2.exec(line) || false;
if (match) {
self.log.debug(`killing pid: ${match[1]}`);
_crossSpawn.default.sync('taskkill', ['/pid', match[1], '/f', '/t']);
}
regexV1.lastIndex = 0;
regexV2.lastIndex = 0;
});
}
function writeLog() {
_fs.default.writeFileSync('meteor.log', log, 'UTF-8');
}
function clearTimeoutsAndIntervals() {
clearInterval(cordovaCheckInterval);
clearTimeout(buildTimeout);
clearTimeout(errorTimeout);
clearTimeout(messageTimeout);
clearTimeout(killTimeout);
}
const args = this.prepareArguments();
this.log.info(`running "meteor ${args.join(' ')}"... this might take a while`);
const env = {
METEOR_PRETTY_OUTPUT: 0,
METEOR_NO_RELEASE_CHECK: 1
};
if (this.$.env.options.prodDebug) {
env.METEOR_DESKOP_PROD_DEBUG = true;
} // Lets spawn meteor.
const child = (0, _crossSpawn.default)('meteor', args, {
env: Object.assign(env, process.env),
cwd: this.$.env.paths.meteorApp.root
}, {
shell: true
}); // Kills the currently running meteor command.
function kill() {
sll('');
child.kill('SIGKILL');
if (self.$.env.os.isWindows) {
windowsKill(child.pid);
}
}
function exit() {
killTimeout = setTimeout(() => {
clearTimeoutsAndIntervals();
desiredExit = true;
kill();
resolve();
}, 500);
}
function copyBuild() {
self.copyBuild().then(() => {
exit();
}).catch(() => {
clearTimeoutsAndIntervals();
kill();
writeLog();
reject('copy');
});
}
cordovaCheckInterval = setInterval(() => {
// Check if we already have cordova-build ready.
if (this.isCordovaBuildReady()) {
// If so, then exit immediately.
if (this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD) {
copyBuild();
}
}
}, 1000);
child.stderr.on('data', chunk => {
const line = chunk.toString('UTF-8');
log += `${line}\n`;
if (errorTimeout) {
clearTimeout(errorTimeout);
} // Do not exit if this is the warning for using --production.
// Output exceeds -> https://github.com/meteor/meteor/issues/8592
if (!~line.indexOf('--production') && !~line.indexOf('Output exceeds ') && !~line.indexOf('Node#moveTo') && !~line.indexOf('Browserslist') && Array.isArray(self.$.env.options.ignoreStderr) && self.$.env.options.ignoreStderr.every(str => !~line.indexOf(str))) {
self.log.warn('STDERR:', line); // We will exit 1s after last error in stderr.
errorTimeout = setTimeout(() => {
clearTimeoutsAndIntervals();
kill();
writeLog();
reject('error');
}, 1000);
}
});
child.stdout.on('data', chunk => {
const line = chunk.toString('UTF-8');
if (!desiredExit && line.trim().replace(/[\n\r\t\v\f]+/gm, '') !== '') {
const linesToDisplay = line.trim().split('\n\r'); // Only display last line from the chunk.
const sanitizedLine = linesToDisplay.pop().replace(/[\n\r\t\v\f]+/gm, '');
sll(sanitizedLine);
}
log += `${line}\n`;
if (~line.indexOf('after_platform_add')) {
sll('');
this.log.info('done... 10%');
}
if (~line.indexOf('Local package version')) {
if (messageTimeout) {
clearTimeout(messageTimeout);
}
messageTimeout = setTimeout(() => {
sll('');
this.log.info('building in progress...');
}, 1500);
}
if (~line.indexOf('Preparing Cordova project')) {
sll('');
this.log.info('done... 60%');
}
if (~line.indexOf('Can\'t listen on port')) {
portProblem = true;
}
if (~line.indexOf('Your application has errors')) {
if (errorTimeout) {
clearTimeout(errorTimeout);
}
errorTimeout = setTimeout(() => {
clearTimeoutsAndIntervals();
kill();
writeLog();
reject('errorInApp');
}, 1000);
}
if (~line.indexOf('App running at')) {
copyBuild();
}
}); // When Meteor exits
child.on('exit', () => {
sll('');
clearTimeoutsAndIntervals();
if (!desiredExit) {
writeLog();
if (portProblem) {
reject('port');
} else {
reject('exit');
}
}
});
buildTimeout = setTimeout(() => {
kill();
writeLog();
reject('timeout');
}, this.$.env.options.buildTimeout ? this.$.env.options.buildTimeout * 1000 : 600000);
});
}
/**
* Replaces the DDP url that was used originally when Meteor was building the client.
* @param {string} indexHtml - path to index.html from the client
*/
updateDdpUrl(indexHtml) {
let content;
let runtimeConfig;
try {
content = _fs.default.readFileSync(indexHtml, 'UTF-8');
} catch (e) {
this.log.error(`error loading index.html file: ${e.message}`);
process.exit(1);
}
if (!this.matcher.test(content)) {
this.log.error('could not find runtime config in index file');
process.exit(1);
}
try {
const matches = content.match(this.matcher);
runtimeConfig = JSON.parse(decodeURIComponent(matches[1]));
} catch (e) {
this.log.error('could not find runtime config in index file');
process.exit(1);
}
if (this.$.env.options.ddpUrl.substr(-1, 1) !== '/') {
this.$.env.options.ddpUrl += '/';
}
runtimeConfig.ROOT_URL = this.$.env.options.ddpUrl;
runtimeConfig.DDP_DEFAULT_CONNECTION_URL = this.$.env.options.ddpUrl;
content = content.replace(this.replacer, `$1"${encodeURIComponent(JSON.stringify(runtimeConfig))}"$3`);
try {
_fs.default.writeFileSync(indexHtml, content);
} catch (e) {
this.log.error(`error writing index.html file: ${e.message}`);
process.exit(1);
}
this.log.info('successfully updated ddp string in the runtime config of a mobile build' + ` to ${this.$.env.options.ddpUrl}`);
}
/**
* Prepares the arguments passed to `meteor` command.
* @returns {string[]}
*/
prepareArguments() {
const args = ['run', '--verbose', `--mobile-server=${this.$.env.options.ddpUrl}`];
if (this.$.env.isProductionBuild()) {
args.push('--production');
}
args.push('-p');
if (this.$.env.options.port) {
args.push(this.$.env.options.port);
} else {
args.push('3080');
}
if (this.$.env.options.meteorSettings) {
args.push('--settings', this.$.env.options.meteorSettings);
}
return args;
}
/**
* Validates the mobile build and copies it into electron app.
*/
async copyBuild() {
this.log.debug('clearing build dir');
try {
await this.$.utils.rmWithRetries('-rf', this.$.env.paths.electronApp.meteorApp);
} catch (e) {
throw new Error(e);
}
let prefix = 'cordovaBuild';
let copyPathPostfix = '';
if (this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_RUNNING_SERVER) {
prefix = 'webCordova';
copyPathPostfix = `${_path.default.sep}*`;
let indexHtml;
try {
_fs.default.mkdirSync(this.$.env.paths.electronApp.meteorApp);
indexHtml = await this.acquireIndex();
_fs.default.writeFileSync(this.$.env.paths.electronApp.meteorAppIndex, indexHtml);
this.log.info('successfully downloaded index.html from running meteor app');
} catch (e) {
this.log.error('error while trying to download index.html for web.cordova, ' + 'be sure that you are running a mobile target or with' + ' --mobile-server: ', e);
throw e;
}
}
const cordovaBuild = this.$.env.paths.meteorApp[prefix];
const {
cordovaBuildIndex
} = this.$.env.paths.meteorApp;
const cordovaBuildProgramJson = this.$.env.paths.meteorApp[`${prefix}ProgramJson`];
if (!this.$.utils.exists(cordovaBuild)) {
this.log.error(`no mobile build found at ${cordovaBuild}`);
this.log.error('are you sure you did run meteor with --mobile-server?');
throw new Error('required file not present');
}
if (!this.$.utils.exists(cordovaBuildProgramJson)) {
this.log.error('no program.json found in mobile build found at ' + `${cordovaBuild}`);
this.log.error('are you sure you did run meteor with --mobile-server?');
throw new Error('required file not present');
}
if (this.indexHTMLstrategy !== this.indexHTMLStrategies.INDEX_FROM_RUNNING_SERVER) {
if (!this.$.utils.exists(cordovaBuildIndex)) {
this.log.error('no index.html found in cordova build found at ' + `${cordovaBuild}`);
this.log.error('are you sure you did run meteor with --mobile-server?');
throw new Error('required file not present');
}
}
this.log.verbose('copying mobile build');
_shelljs.default.cp('-R', `${cordovaBuild}${copyPathPostfix}`, this.$.env.paths.electronApp.meteorApp); // Because of various permission problems here we try to clear te path by clearing
// all possible restrictions.
_shelljs.default.chmod('-R', '777', this.$.env.paths.electronApp.meteorApp);
if (this.$.env.os.isWindows) {
_shelljs.default.exec(`attrib -r ${this.$.env.paths.electronApp.meteorApp}${_path.default.sep}*.* /s`);
}
if (this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_RUNNING_SERVER) {
let programJson;
try {
programJson = await this.acquireManifest();
_fs.default.writeFileSync(this.$.env.paths.electronApp.meteorAppProgramJson, JSON.stringify(programJson, null, 4));
this.log.info('successfully downloaded manifest.json from running meteor app');
} catch (e) {
this.log.error('error while trying to download manifest.json for web.cordova,' + ' be sure that you are running a mobile target or with' + ' --mobile-server: ', e);
throw e;
}
}
this.log.info('mobile build copied to electron app');
this.log.debug('copy cordova.js to meteor build');
_shelljs.default.cp(join(__dirname, '..', 'skeleton', 'cordova.js'), this.$.env.paths.electronApp.meteorApp);
}
/**
* Injects Meteor.isDesktop
*/
injectIsDesktop() {
this.log.info('injecting isDesktop');
let manifestJsonPath = this.$.env.paths.meteorApp.cordovaBuildProgramJson;
if (this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_RUNNING_SERVER) {
manifestJsonPath = this.$.env.paths.meteorApp.webCordovaProgramJson;
}
try {
const {
manifest
} = JSON.parse(_fs.default.readFileSync(manifestJsonPath, 'UTF-8'));
let injected = false;
let injectedStartupDidComplete = false;
let result = null; // We will search in every .js file in the manifest.
// We could probably detect whether this is a dev or production build and only search in
// the correct files, but for now this should be fine.
manifest.forEach(file => {
let fileContents; // Hacky way of setting isDesktop.
if (file.type === 'js') {
fileContents = _fs.default.readFileSync(join(this.$.env.paths.electronApp.meteorApp, file.path), 'UTF-8');
result = this.injector.processFileContents(fileContents);
({
fileContents
} = result);
injectedStartupDidComplete = result.injectedStartupDidComplete ? true : injectedStartupDidComplete;
injected = result.injected ? true : injected;
_fs.default.writeFileSync(join(this.$.env.paths.electronApp.meteorApp, file.path), fileContents);
}
});
if (!injected) {
this.log.error('error injecting isDesktop global var.');
process.exit(1);
}
if (!injectedStartupDidComplete) {
this.log.error('error injecting isDesktop for startupDidComplete');
process.exit(1);
}
} catch (e) {
this.log.error('error occurred while injecting isDesktop: ', e);
process.exit(1);
}
this.log.info('injected successfully');
}
/**
* Builds, modifies and copies the meteor app to electron app.
*/
async build() {
this.log.info('checking for any mobile platform');
try {
await this.checkPreconditions();
} catch (e) {
this.log.error('error occurred during checking preconditions: ', e);
process.exit(1);
}
this.log.info('building meteor app');
if (!this.$.env.options.skipMobileBuild) {
try {
await this.buildMobileTarget();
} catch (reason) {
switch (reason) {
case 'timeout':
this.log.error('timeout while building, log has been written to meteor.log');
break;
case 'error':
this.log.error('build was terminated by meteor-desktop as some errors were reported to stderr, you ' + 'should see it above, also check meteor.log for more info, to ignore it use the ' + '--ignore-stderr "<string>"');
break;
case 'errorInApp':
this.log.error('your meteor app has errors - look into meteor.log for more' + ' info');
break;
case 'port':
this.log.error('your port 3080 is currently used (you probably have this or other ' + 'meteor project running?), use `-t` or `--meteor-port` to use ' + 'different port while building');
break;
case 'exit':
this.log.error('meteor cmd exited unexpectedly, log has been written to meteor.log');
break;
case 'copy':
this.log.error('error encountered when copying the build');
break;
default:
this.log.error('error occurred during building mobile target', reason);
}
if (this.mobilePlatform) {
await this.removeMobilePlatform(this.mobilePlatform);
}
process.exit(1);
}
} else {
this.indexHTMLstrategy = this.chooseStrategy();
try {
await this.copyBuild();
} catch (e) {
process.exit(1);
}
}
this.injectIsDesktop();
this.changeDdpUrl();
try {
await this.packToAsar();
} catch (e) {
this.log.error('error while packing meteor app to asar');
process.exit(1);
}
this.log.info('meteor build finished');
if (this.mobilePlatform) {
await this.removeMobilePlatform(this.mobilePlatform);
}
}
changeDdpUrl() {
if (this.$.env.options.ddpUrl !== null) {
try {
this.updateDdpUrl(this.$.env.paths.electronApp.meteorAppIndex);
} catch (e) {
this.log.error(`error while trying to change the ddp url: ${e.message}`);
}
}
}
packToAsar() {
this.log.info('packing meteor app to asar archive');
return new Promise((resolve, reject) => _asar.default.createPackage(this.$.env.paths.electronApp.meteorApp, _path.default.join(this.$.env.paths.electronApp.root, 'meteor.asar'), () => {
// On Windows some files might still be blocked. Giving a tick for them to be
// ready for deletion.
setImmediate(() => {
this.log.verbose('clearing meteor app after packing');
this.$.utils.rmWithRetries('-rf', this.$.env.paths.electronApp.meteorApp).then(() => {
resolve();
}).catch(e => {
reject(e);
});
});
}));
}
/**
* Wrapper for spawning npm.
* @param {Array} commands - commands for spawn
* @param {string} stdio
* @param {string} cwd
* @return {Promise}
*/
runNpm(commands, stdio = 'ignore', cwd = this.$.env.paths.meteorApp.root) {
return new Promise((resolve, reject) => {
this.log.verbose(`executing meteor npm ${commands.join(' ')}`);
(0, _crossSpawn.default)('meteor', ['npm', ...commands], {
cwd,
stdio
}).on('exit', code => code === 0 ? resolve() : reject(new Error(`npm exit code was ${code}`)));
});
}
}
exports.default = MeteorApp;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL2xpYi9tZXRlb3JBcHAuanMiXSwibmFtZXMiOlsiam9pbiIsInBhdGgiLCJzbGwiLCJzaW5nbGVMaW5lTG9nIiwic3Rkb3V0IiwiTWV0ZW9yQXBwIiwiY29uc3RydWN0b3IiLCIkIiwibG9nIiwiTG9nIiwibWV0ZW9yTWFuYWdlciIsIk1ldGVvck1hbmFnZXIiLCJtb2JpbGVQbGF0Zm9ybSIsIm9sZE1hbmlmZXN0IiwiaW5qZWN0b3IiLCJJc0Rlc2t0b3BJbmplY3RvciIsIm1hdGNoZXIiLCJSZWdFeHAiLCJyZXBsYWNlciIsIm1ldGVvclZlcnNpb24iLCJpbmRleEhUTUxzdHJhdGVneSIsImluZGV4SFRNTFN0cmF0ZWdpZXMiLCJJTkRFWF9GUk9NX0NPUkRPVkFfQlVJTEQiLCJJTkRFWF9GUk9NX1JVTk5JTkdfU0VSVkVSIiwiZGVwcmVjdGF0ZWRQYWNrYWdlcyIsInJlbW92ZURlcHJlY2F0ZWRQYWNrYWdlcyIsImNoZWNrUGFja2FnZXMiLCJpbmZvIiwiZGVsZXRlUGFja2FnZXMiLCJlIiwiRXJyb3IiLCJlbnN1cmVEZXNrdG9wSENQUGFja2FnZXMiLCJkZXNrdG9wSENQUGFja2FnZXMiLCJkZXNrdG9wIiwiZ2V0U2V0dGluZ3MiLCJkZXNrdG9wSENQIiwidmVyYm9zZSIsInBhY2thZ2VzV2l0aFZlcnNpb24iLCJtYXAiLCJwYWNrYWdlTmFtZSIsImdldFZlcnNpb24iLCJlbnN1cmVQYWNrYWdlcyIsInVwZGF0ZUdpdElnbm9yZSIsImdpdElnbm9yZSIsImZzIiwicmVhZEZpbGVTeW5jIiwiZW52IiwicGF0aHMiLCJtZXRlb3JBcHAiLCJzcGxpdCIsImZpbHRlciIsImlnbm9yZWRQYXRoIiwidHJpbSIsImluZGV4T2YiLCJlbGVjdHJvbkFwcCIsInJvb3ROYW1lIiwicHVzaCIsIndyaXRlRmlsZVN5bmMiLCJnZXRNZXRlb3JSZWxlYXNlIiwicmVsZWFzZSIsInJlcGxhY2UiLCJjYXN0TWV0ZW9yUmVsZWFzZVRvU2VtdmVyIiwibWF0Y2giLCJjaGVja01ldGVvclZlcnNpb24iLCJ2ZXJzaW9uUmFuZ2UiLCJzZW12ZXIiLCJzYXRpc2ZpZXMiLCJvcHRpb25zIiwic2tpcE1vYmlsZUJ1aWxkIiwiZXJyb3IiLCJwcm9jZXNzIiwiZXhpdCIsImNob29zZVN0cmF0ZWd5IiwiZm9yY2VDb3Jkb3ZhQnVpbGQiLCJleHBsb2RlZFZlcnNpb24iLCJsZW5ndGgiLCJjaGVja1ByZWNvbmRpdGlvbnMiLCJkZWJ1ZyIsInBsYXRmb3JtcyIsImFuZHJvaWQiLCJ3YXJuIiwiYWRkTW9iaWxlUGxhdGZvcm0iLCJwbGF0Zm9ybSIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwiY3dkIiwicm9vdCIsInN0ZGlvIiwib24iLCJyZW1vdmVNb2JpbGVQbGF0Zm9ybSIsInNraXBSZW1vdmVNb2JpbGVQbGF0Zm9ybSIsIk9iamVjdCIsImFzc2lnbiIsIk1FVEVPUl9QUkVUVFlfT1VUUFVUIiwiaXNDb3Jkb3ZhQnVpbGRSZWFkeSIsInV0aWxzIiwiZXhpc3RzIiwiY29yZG92YUJ1aWxkSW5kZXgiLCJjb3Jkb3ZhQnVpbGRQcm9ncmFtSnNvbiIsIndlYkNvcmRvdmFQcm9ncmFtSnNvbiIsImFjcXVpcmVJbmRleCIsInBvcnQiLCJyZXMiLCJ0ZXh0IiwiYWNxdWlyZU1hbmlmZXN0IiwiSlNPTiIsInBhcnNlIiwiYnVpbGRNb2JpbGVUYXJnZXQiLCJwcm9ncmFtSnNvbiIsInNlbGYiLCJkZXNpcmVkRXhpdCIsImJ1aWxkVGltZW91dCIsImVycm9yVGltZW91dCIsIm1lc3NhZ2VUaW1lb3V0Iiwia2lsbFRpbWVvdXQiLCJjb3Jkb3ZhQ2hlY2tJbnRlcnZhbCIsInBvcnRQcm9ibGVtIiwid2luZG93c0tpbGwiLCJwaWQiLCJzcGF3biIsInN5bmMiLCJvdXQiLCJ0b1N0cmluZyIsImFyZ3MiLCJwcmVwYXJlQXJndW1lbnRzIiwicmVnZXhWMSIsInJlZ2V4VjIiLCJmb3JFYWNoIiwibGluZSIsImV4ZWMiLCJsYXN0SW5kZXgiLCJ3cml0ZUxvZyIsImNsZWFyVGltZW91dHNBbmRJbnRlcnZhbHMiLCJjbGVhckludGVydmFsIiwiY2xlYXJUaW1lb3V0IiwiTUVURU9SX05PX1JFTEVBU0VfQ0hFQ0siLCJwcm9kRGVidWciLCJNRVRFT1JfREVTS09QX1BST0RfREVCVUciLCJjaGlsZCIsInNoZWxsIiwia2lsbCIsIm9zIiwiaXNXaW5kb3dzIiwic2V0VGltZW91dCIsImNvcHlCdWlsZCIsInRoZW4iLCJjYXRjaCIsInNldEludGVydmFsIiwic3RkZXJyIiwiY2h1bmsiLCJBcnJheSIsImlzQXJyYXkiLCJpZ25vcmVTdGRlcnIiLCJldmVyeSIsInN0ciIsImxpbmVzVG9EaXNwbGF5Iiwic2FuaXRpemVkTGluZSIsInBvcCIsInVwZGF0ZURkcFVybCIsImluZGV4SHRtbCIsImNvbnRlbnQiLCJydW50aW1lQ29uZmlnIiwibWVzc2FnZSIsInRlc3QiLCJtYXRjaGVzIiwiZGVjb2RlVVJJQ29tcG9uZW50IiwiZGRwVXJsIiwic3Vic3RyIiwiUk9PVF9VUkwiLCJERFBfREVGQVVMVF9DT05ORUNUSU9OX1VSTCIsImVuY29kZVVSSUNvbXBvbmVudCIsInN0cmluZ2lmeSIsImlzUHJvZHVjdGlvbkJ1aWxkIiwibWV0ZW9yU2V0dGluZ3MiLCJybVdpdGhSZXRyaWVzIiwicHJlZml4IiwiY29weVBhdGhQb3N0Zml4Iiwic2VwIiwibWtkaXJTeW5jIiwibWV0ZW9yQXBwSW5kZXgiLCJjb3Jkb3ZhQnVpbGQiLCJjcCIsImNobW9kIiwibWV0ZW9yQXBwUHJvZ3JhbUpzb24iLCJfX2Rpcm5hbWUiLCJpbmplY3RJc0Rlc2t0b3AiLCJtYW5pZmVzdEpzb25QYXRoIiwibWFuaWZlc3QiLCJpbmplY3RlZCIsImluamVjdGVkU3RhcnR1cERpZENvbXBsZXRlIiwicmVzdWx0IiwiZmlsZSIsImZpbGVDb250ZW50cyIsInR5cGUiLCJwcm9jZXNzRmlsZUNvbnRlbnRzIiwiYnVpbGQiLCJyZWFzb24iLCJjaGFuZ2VEZHBVcmwiLCJwYWNrVG9Bc2FyIiwiYXNhciIsImNyZWF0ZVBhY2thZ2UiLCJzZXRJbW1lZGlhdGUi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