main.js 15.2 KB
Newer Older
J
Joao Moreno 已提交
1 2 3 4
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
5 6
'use strict';

J
Joao Moreno 已提交
7
const perf = require('./vs/base/common/performance');
8 9
perf.mark('main:started');

J
Joao Moreno 已提交
10
// Perf measurements
11
global.perfStartTime = Date.now();
J
Joao Moreno 已提交
12

B
Benjamin Pasero 已提交
13 14
Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API)

J
Joao Moreno 已提交
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
const fs = require('fs');
const path = require('path');
const product = require('../product.json');
const appRoot = path.dirname(__dirname);

function getApplicationPath() {
	if (process.env['VSCODE_DEV']) {
		return appRoot;
	} else if (process.platform === 'darwin') {
		return path.dirname(path.dirname(path.dirname(appRoot)));
	} else {
		return path.dirname(path.dirname(appRoot));
	}
}

J
Joao Moreno 已提交
30 31 32 33 34 35 36 37 38 39
const portableDataName = product.portable || `${product.applicationName}-portable-data`;
const portableDataPath = process.env['VSCODE_PORTABLE'] || path.join(path.dirname(getApplicationPath()), portableDataName);
const isPortable = fs.existsSync(portableDataPath);
const portableTempPath = path.join(portableDataPath, 'tmp');
const isTempPortable = isPortable && fs.existsSync(portableTempPath);

if (isPortable) {
	process.env['VSCODE_PORTABLE'] = portableDataPath;
} else {
	delete process.env['VSCODE_PORTABLE'];
J
Joao Moreno 已提交
40 41
}

J
Joao Moreno 已提交
42 43
if (isTempPortable) {
	process.env[process.platform === 'win32' ? 'TEMP' : 'TMPDIR'] = portableTempPath;
J
Joao Moreno 已提交
44 45
}

A
Alex Dima 已提交
46 47 48 49 50 51 52 53
//#region Add support for using node_modules.asar
(function () {
	const path = require('path');
	const Module = require('module');
	const NODE_MODULES_PATH = path.join(__dirname, '../node_modules');
	const NODE_MODULES_ASAR_PATH = NODE_MODULES_PATH + '.asar';

	const originalResolveLookupPaths = Module._resolveLookupPaths;
A
Alex Dima 已提交
54 55
	Module._resolveLookupPaths = function (request, parent, newReturn) {
		const result = originalResolveLookupPaths(request, parent, newReturn);
A
Alex Dima 已提交
56

A
Alex Dima 已提交
57
		const paths = newReturn ? result : result[1];
A
Alex Dima 已提交
58 59 60 61 62 63 64 65 66 67 68 69
		for (let i = 0, len = paths.length; i < len; i++) {
			if (paths[i] === NODE_MODULES_PATH) {
				paths.splice(i, 0, NODE_MODULES_ASAR_PATH);
				break;
			}
		}

		return result;
	};
})();
//#endregion

J
Joao Moreno 已提交
70
const app = require('electron').app;
B
Benjamin Pasero 已提交
71 72 73 74

// TODO@Ben Electron 2.0.x: prevent localStorage migration from SQLite to LevelDB due to issues
app.commandLine.appendSwitch('disable-mojo-local-storage');

75 76 77
// TODO@Ben Electron 2.0.x: force srgb color profile (for https://github.com/Microsoft/vscode/issues/51791)
app.commandLine.appendSwitch('force-color-profile', 'srgb');

J
Joao Moreno 已提交
78 79
const minimist = require('minimist');
const paths = require('./paths');
J
Joao Moreno 已提交
80

J
Joao Moreno 已提交
81
const args = minimist(process.argv, {
J
Joao Moreno 已提交
82 83 84 85 86 87
	string: [
		'user-data-dir',
		'locale',
		'js-flags',
		'max-memory'
	]
J
Joao Moreno 已提交
88 89
});

J
Joao Moreno 已提交
90
//#region NLS
91
function stripComments(content) {
D
Dirk Baeumer 已提交
92 93
	let regexp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
	let result = content.replace(regexp, function (match, m1, m2, m3, m4) {
94 95 96 97
		// Only one of m1, m2, m3, m4 matches
		if (m3) {
			// A block comment. Replace with nothing
			return '';
D
Dirk Baeumer 已提交
98
		} else if (m4) {
99
			// A line comment. If it ends in \r?\n then keep it.
D
Dirk Baeumer 已提交
100
			let length_1 = m4.length;
101 102 103 104 105 106
			if (length_1 > 2 && m4[length_1 - 1] === '\n') {
				return m4[length_1 - 2] === '\r' ? '\r\n' : '\n';
			}
			else {
				return '';
			}
D
Dirk Baeumer 已提交
107
		} else {
108 109 110 111 112
			// We match a string
			return match;
		}
	});
	return result;
J
lint  
Joao Moreno 已提交
113
}
114

J
Joao Moreno 已提交
115 116 117 118 119
const mkdir = dir => new Promise((c, e) => fs.mkdir(dir, err => (err && err.code !== 'EEXIST') ? e(err) : c()));
const exists = file => new Promise(c => fs.exists(file, c));
const readFile = file => new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data)));
const writeFile = (file, content) => new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c()));
const touch = file => new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); });
D
Dirk Baeumer 已提交
120 121

function mkdirp(dir) {
J
Joao Moreno 已提交
122 123 124
	return mkdir(dir).then(null, err => {
		if (err && err.code === 'ENOENT') {
			const parent = path.dirname(dir);
D
Dirk Baeumer 已提交
125

J
Joao Moreno 已提交
126 127
			if (parent !== dir) { // if not arrived at root
				return mkdirp(parent).then(() => mkdir(dir));
D
Dirk Baeumer 已提交
128
			}
J
Joao Moreno 已提交
129
		}
D
Dirk Baeumer 已提交
130

J
Joao Moreno 已提交
131
		throw err;
D
Dirk Baeumer 已提交
132 133 134
	});
}

135
function resolveJSFlags() {
J
Joao Moreno 已提交
136 137
	const jsFlags = [];

138
	if (args['js-flags']) {
P
Peng Lyu 已提交
139
		jsFlags.push(args['js-flags']);
140
	}
J
Joao Moreno 已提交
141

P
Peng Lyu 已提交
142 143
	if (args['max-memory'] && !/max_old_space_size=(\d+)/g.exec(args['js-flags'])) {
		jsFlags.push(`--max_old_space_size=${args['max-memory']}`);
144
	}
J
Joao Moreno 已提交
145 146

	return jsFlags.length > 0 ? jsFlags.join(' ') : null;
147 148
}

D
Dirk Baeumer 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
// Language tags are case insensitve however an amd loader is case sensitive
// To make this work on case preserving & insensitive FS we do the following:
// the language bundles have lower case language tags and we always lower case
// the locale we receive from the user or OS.

function getUserDefinedLocale() {
	let locale = args['locale'];
	if (locale) {
		return Promise.resolve(locale.toLowerCase());
	}

	let userData = app.getPath('userData');
	let localeConfig = path.join(userData, 'User', 'locale.json');
	return exists(localeConfig).then((result) => {
		if (result) {
			return readFile(localeConfig).then((content) => {
				content = stripComments(content);
				try {
					let value = JSON.parse(content).locale;
					return value && typeof value === 'string' ? value.toLowerCase() : undefined;
				} catch (e) {
					return undefined;
				}
			});
		} else {
			return undefined;
		}
	});
}

function getLanguagePackConfigurations() {
	let userData = app.getPath('userData');
	let configFile = path.join(userData, 'languagepacks.json');
	try {
		return require(configFile);
	} catch (err) {
		// Do nothing. If we can't read the file we have no
		// language pack config.
	}
	return undefined;
}

function resolveLanguagePackLocale(config, locale) {
	try {
		while (locale) {
			if (config[locale]) {
				return locale;
			} else {
				let index = locale.lastIndexOf('-');
				if (index > 0) {
					locale = locale.substring(0, index);
				} else {
					return undefined;
202 203 204
				}
			}
		}
D
Dirk Baeumer 已提交
205 206
	} catch (err) {
		console.error('Resolving language pack configuration failed.', err);
207
	}
D
Dirk Baeumer 已提交
208 209
	return undefined;
}
210

D
Dirk Baeumer 已提交
211
function getNLSConfiguration(locale) {
J
Joao Moreno 已提交
212
	if (locale === 'pseudo') {
D
Dirk Baeumer 已提交
213
		return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true });
J
Joao Moreno 已提交
214
	}
D
Dirk Baeumer 已提交
215

J
Joao Moreno 已提交
216
	if (process.env['VSCODE_DEV']) {
D
Dirk Baeumer 已提交
217
		return Promise.resolve({ locale: locale, availableLanguages: {} });
J
Joao Moreno 已提交
218
	}
219

D
Dirk Baeumer 已提交
220 221
	let userData = app.getPath('userData');

J
Joao Moreno 已提交
222 223
	// We have a built version so we have extracted nls file. Try to find
	// the right file to use.
224 225 226 227

	// Check if we have an English locale. If so fall to default since that is our
	// English translation (we don't ship *.nls.en.json files)
	if (locale && (locale == 'en' || locale.startsWith('en-'))) {
D
Dirk Baeumer 已提交
228
		return Promise.resolve({ locale: locale, availableLanguages: {} });
229 230
	}

D
Dirk Baeumer 已提交
231 232
	let initialLocale = locale;

233 234
	function resolveLocale(locale) {
		while (locale) {
D
Dirk Baeumer 已提交
235
			let candidate = path.join(__dirname, 'vs', 'code', 'electron-main', 'main.nls.') + locale + '.js';
236 237
			if (fs.existsSync(candidate)) {
				return { locale: initialLocale, availableLanguages: { '*': locale } };
J
Joao Moreno 已提交
238
			} else {
D
Dirk Baeumer 已提交
239
				let index = locale.lastIndexOf('-');
240 241 242
				if (index > 0) {
					locale = locale.substring(0, index);
				} else {
D
Dirk Baeumer 已提交
243
					locale = undefined;
244
				}
J
Joao Moreno 已提交
245 246
			}
		}
D
Dirk Baeumer 已提交
247
		return undefined;
J
Joao Moreno 已提交
248 249
	}

250
	perf.mark('nlsGeneration:start');
J
Joao Moreno 已提交
251
	let defaultResult = function (locale) {
252 253 254 255 256 257 258 259 260 261
		let isCoreLanguage = true;
		if (locale) {
			isCoreLanguage = ['de', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'zh-cn', 'zh-tw'].some((language) => {
				return locale === language || locale.startsWith(language + '-');
			});
		}
		if (isCoreLanguage) {
			let result = resolveLocale(locale);
			perf.mark('nlsGeneration:end');
			return Promise.resolve(result);
J
Joao Moreno 已提交
262
		} else {
D
Dirk Baeumer 已提交
263 264
			perf.mark('nlsGeneration:end');
			return Promise.resolve({ locale: locale, availableLanguages: {} });
265 266 267
		}
	};
	try {
J
Joao Moreno 已提交
268
		let commit = product.commit;
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
		if (!commit) {
			return defaultResult(locale);
		}
		let configs = getLanguagePackConfigurations();
		if (!configs) {
			return defaultResult(locale);
		}
		let initialLocale = locale;
		locale = resolveLanguagePackLocale(configs, locale);
		if (!locale) {
			return defaultResult(initialLocale);
		}
		let packConfig = configs[locale];
		let mainPack;
		if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') {
			return defaultResult(locale);
		}
		return exists(mainPack).then((fileExists) => {
			if (!fileExists) {
				return defaultResult(locale);
D
Dirk Baeumer 已提交
289
			}
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
			let packId = packConfig.hash + '.' + locale;
			let cacheRoot = path.join(userData, 'clp', packId);
			let coreLocation = path.join(cacheRoot, commit);
			let translationsConfigFile = path.join(cacheRoot, 'tcf.json');
			let result = {
				locale: initialLocale,
				availableLanguages: { '*': locale },
				_languagePackId: packId,
				_translationsConfigFile: translationsConfigFile,
				_cacheRoot: cacheRoot,
				_resolvedLanguagePackCoreLocation: coreLocation
			};
			return exists(coreLocation).then((fileExists) => {
				if (fileExists) {
					// We don't wait for this. No big harm if we can't touch
J
Joao Moreno 已提交
305
					touch(coreLocation).catch(() => { });
306 307
					perf.mark('nlsGeneration:end');
					return result;
D
Dirk Baeumer 已提交
308
				}
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
				return mkdirp(coreLocation).then(() => {
					return Promise.all([readFile(path.join(__dirname, 'nls.metadata.json')), readFile(mainPack)]);
				}).then((values) => {
					let metadata = JSON.parse(values[0]);
					let packData = JSON.parse(values[1]).contents;
					let bundles = Object.keys(metadata.bundles);
					let writes = [];
					for (let bundle of bundles) {
						let modules = metadata.bundles[bundle];
						let target = Object.create(null);
						for (let module of modules) {
							let keys = metadata.keys[module];
							let defaultMessages = metadata.messages[module];
							let translations = packData[module];
							let targetStrings;
							if (translations) {
								targetStrings = [];
								for (let i = 0; i < keys.length; i++) {
									let elem = keys[i];
									let key = typeof elem === 'string' ? elem : elem.key;
									let translatedMessage = translations[key];
									if (translatedMessage === undefined) {
										translatedMessage = defaultMessages[i];
D
Dirk Baeumer 已提交
332
									}
333
									targetStrings.push(translatedMessage);
D
Dirk Baeumer 已提交
334
								}
335 336
							} else {
								targetStrings = defaultMessages;
D
Dirk Baeumer 已提交
337
							}
338
							target[module] = targetStrings;
D
Dirk Baeumer 已提交
339
						}
340 341 342 343 344 345 346 347 348 349
						writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target)));
					}
					writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations)));
					return Promise.all(writes);
				}).then(() => {
					perf.mark('nlsGeneration:end');
					return result;
				}).catch((err) => {
					console.error('Generating translation files failed.', err);
					return defaultResult(locale);
D
Dirk Baeumer 已提交
350 351
				});
			});
352 353 354 355
		});
	} catch (err) {
		console.error('Generating translation files failed.', err);
		return defaultResult(locale);
356
	}
J
Joao Moreno 已提交
357
}
J
Joao Moreno 已提交
358
//#endregion
J
Joao Moreno 已提交
359

J
Joao Moreno 已提交
360
//#region Cached Data Dir
361
function getNodeCachedDataDir() {
362 363 364 365
	// flag to disable cached data support
	if (process.argv.indexOf('--no-cached-data') > 0) {
		return Promise.resolve(undefined);
	}
366 367 368

	// IEnvironmentService.isBuilt
	if (process.env['VSCODE_DEV']) {
369
		return Promise.resolve(undefined);
370 371
	}

372
	// find commit id
J
Joao Moreno 已提交
373
	let commit = product.commit;
D
Dirk Baeumer 已提交
374
	if (!commit) {
375 376 377
		return Promise.resolve(undefined);
	}

D
Dirk Baeumer 已提交
378
	let dir = path.join(app.getPath('userData'), 'CachedData', commit);
379

J
Joao Moreno 已提交
380
	return mkdirp(dir).then(undefined, function () { /*ignore*/ });
381
}
J
Joao Moreno 已提交
382
//#endregion
383

J
Joao Moreno 已提交
384
function getUserDataPath() {
J
Joao Moreno 已提交
385 386
	if (isPortable) {
		return path.join(portableDataPath, 'user-data');
J
Joao Moreno 已提交
387 388 389 390
	}

	return path.resolve(args['user-data-dir'] || paths.getDefaultUserDataPath(process.platform));
}
391

392
// Set userData path before app 'ready' event and call to process.chdir
J
Joao Moreno 已提交
393
app.setPath('userData', getUserDataPath());
394

B
polish  
Benjamin Pasero 已提交
395
// Update cwd based on environment and platform
J
Joao Moreno 已提交
396
try {
B
polish  
Benjamin Pasero 已提交
397
	if (process.platform === 'win32') {
D
Dirk Baeumer 已提交
398
		process.env['VSCODE_CWD'] = process.cwd(); // remember as environment letiable
B
polish  
Benjamin Pasero 已提交
399
		process.chdir(path.dirname(app.getPath('exe'))); // always set application folder as cwd
J
Joao Moreno 已提交
400 401
	} else if (process.env['VSCODE_CWD']) {
		process.chdir(process.env['VSCODE_CWD']);
J
Joao Moreno 已提交
402 403
	}
} catch (err) {
B
polish  
Benjamin Pasero 已提交
404
	console.error(err);
J
Joao Moreno 已提交
405 406 407 408 409
}

// Mac: when someone drops a file to the not-yet running VSCode, the open-file event fires even before
// the app-ready event. We listen very early for open-file and remember this upon startup as path to open.
global.macOpenFiles = [];
B
polish  
Benjamin Pasero 已提交
410
app.on('open-file', function (event, path) {
J
Joao Moreno 已提交
411 412 413
	global.macOpenFiles.push(path);
});

D
Dirk Baeumer 已提交
414 415
let openUrls = [];
let onOpenUrl = function (event, url) {
J
Joao Moreno 已提交
416 417 418 419
	event.preventDefault();
	openUrls.push(url);
};

420 421 422
app.on('will-finish-launching', function () {
	app.on('open-url', onOpenUrl);
});
J
Joao Moreno 已提交
423

424
global.getOpenUrls = function () {
J
Joao Moreno 已提交
425 426 427 428
	app.removeListener('open-url', onOpenUrl);
	return openUrls;
};

429 430
// use '<UserData>/CachedData'-directory to store
// node/v8 cached data.
D
Dirk Baeumer 已提交
431
let nodeCachedDataDir = getNodeCachedDataDir().then(function (value) {
432
	if (value) {
433
		// store the data directory
434
		process.env['VSCODE_NODE_CACHED_DATA_DIR_' + process.pid] = value;
435 436 437

		// tell v8 to not be lazy when parsing JavaScript. Generally this makes startup slower
		// but because we generate cached data it makes subsequent startups much faster
438 439
		let existingJSFlags = resolveJSFlags();
		app.commandLine.appendSwitch('--js-flags', existingJSFlags ? existingJSFlags + ' --nolazy' : '--nolazy');
440
	}
D
Dirk Baeumer 已提交
441 442 443 444 445 446 447 448 449
	return value;
});

let nlsConfiguration = undefined;
let userDefinedLocale = getUserDefinedLocale();
userDefinedLocale.then((locale) => {
	if (locale && !nlsConfiguration) {
		nlsConfiguration = getNLSConfiguration(locale);
	}
450 451
});

452 453 454 455 456
let jsFlags = resolveJSFlags();
if (jsFlags) {
	app.commandLine.appendSwitch('--js-flags', jsFlags);
}

457 458
// Load our code once ready
app.once('ready', function () {
459
	perf.mark('main:appReady');
D
Dirk Baeumer 已提交
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
	Promise.all([nodeCachedDataDir, userDefinedLocale]).then((values) => {
		let locale = values[1];
		if (locale && !nlsConfiguration) {
			nlsConfiguration = getNLSConfiguration(locale);
		}
		if (!nlsConfiguration) {
			nlsConfiguration = Promise.resolve(undefined);
		}
		// We first need to test a user defined locale. If it fails we try the app locale.
		// If that fails we fall back to English.
		nlsConfiguration.then((nlsConfig) => {
			let boot = (nlsConfig) => {
				process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig);
				require('./bootstrap-amd').bootstrap('vs/code/electron-main/main');
			};
			// We recevied a valid nlsConfig from a user defined locale
			if (nlsConfig) {
				boot(nlsConfig);
			} else {
				// Try to use the app locale. Please note that the app locale is only
				// valid after we have received the app ready event. This is why the
				// code is here.
				let appLocale = app.getLocale();
				if (!appLocale) {
					boot({ locale: 'en', availableLanguages: {} });
				} else {
					// See above the comment about the loader and case sensitiviness
487
					appLocale = appLocale.toLowerCase();
D
Dirk Baeumer 已提交
488 489 490 491 492 493 494 495 496
					getNLSConfiguration(appLocale).then((nlsConfig) => {
						if (!nlsConfig) {
							nlsConfig = { locale: appLocale, availableLanguages: {} };
						}
						boot(nlsConfig);
					});
				}
			}
		});
497
	}, console.error);
498
});