main.js 14.0 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';

D
Dirk Baeumer 已提交
7
let 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)

A
Alex Dima 已提交
15 16 17 18 19 20 21 22
//#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 已提交
23 24
	Module._resolveLookupPaths = function (request, parent, newReturn) {
		const result = originalResolveLookupPaths(request, parent, newReturn);
A
Alex Dima 已提交
25

A
Alex Dima 已提交
26
		const paths = newReturn ? result : result[1];
A
Alex Dima 已提交
27 28 29 30 31 32 33 34 35 36 37 38
		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

D
Dirk Baeumer 已提交
39
let app = require('electron').app;
B
Benjamin Pasero 已提交
40 41 42 43

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

D
Dirk Baeumer 已提交
44 45 46 47
let fs = require('fs');
let path = require('path');
let minimist = require('minimist');
let paths = require('./paths');
J
Joao Moreno 已提交
48
let product = require('../product.json');
J
Joao Moreno 已提交
49

D
Dirk Baeumer 已提交
50
let args = minimist(process.argv, {
J
Joao Moreno 已提交
51 52 53 54 55 56
	string: [
		'user-data-dir',
		'locale',
		'js-flags',
		'max-memory'
	]
J
Joao Moreno 已提交
57 58
});

J
Joao Moreno 已提交
59
//#region NLS
60
function stripComments(content) {
D
Dirk Baeumer 已提交
61 62
	let regexp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
	let result = content.replace(regexp, function (match, m1, m2, m3, m4) {
63 64 65 66
		// Only one of m1, m2, m3, m4 matches
		if (m3) {
			// A block comment. Replace with nothing
			return '';
D
Dirk Baeumer 已提交
67
		} else if (m4) {
68
			// A line comment. If it ends in \r?\n then keep it.
D
Dirk Baeumer 已提交
69
			let length_1 = m4.length;
70 71 72 73 74 75
			if (length_1 > 2 && m4[length_1 - 1] === '\n') {
				return m4[length_1 - 2] === '\r' ? '\r\n' : '\n';
			}
			else {
				return '';
			}
D
Dirk Baeumer 已提交
76
		} else {
77 78 79 80 81
			// We match a string
			return match;
		}
	});
	return result;
J
lint  
Joao Moreno 已提交
82
}
83

J
Joao Moreno 已提交
84 85 86 87 88
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 已提交
89 90

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

J
Joao Moreno 已提交
95 96
			if (parent !== dir) { // if not arrived at root
				return mkdirp(parent).then(() => mkdir(dir));
D
Dirk Baeumer 已提交
97
			}
J
Joao Moreno 已提交
98
		}
D
Dirk Baeumer 已提交
99

J
Joao Moreno 已提交
100
		throw err;
D
Dirk Baeumer 已提交
101 102 103
	});
}

104
function resolveJSFlags() {
J
Joao Moreno 已提交
105 106
	const jsFlags = [];

107
	if (args['js-flags']) {
P
Peng Lyu 已提交
108
		jsFlags.push(args['js-flags']);
109
	}
J
Joao Moreno 已提交
110

P
Peng Lyu 已提交
111 112
	if (args['max-memory'] && !/max_old_space_size=(\d+)/g.exec(args['js-flags'])) {
		jsFlags.push(`--max_old_space_size=${args['max-memory']}`);
113
	}
J
Joao Moreno 已提交
114 115

	return jsFlags.length > 0 ? jsFlags.join(' ') : null;
116 117
}

D
Dirk Baeumer 已提交
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
// 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;
171 172 173
				}
			}
		}
D
Dirk Baeumer 已提交
174 175
	} catch (err) {
		console.error('Resolving language pack configuration failed.', err);
176
	}
D
Dirk Baeumer 已提交
177 178
	return undefined;
}
179

D
Dirk Baeumer 已提交
180
function getNLSConfiguration(locale) {
J
Joao Moreno 已提交
181
	if (locale === 'pseudo') {
D
Dirk Baeumer 已提交
182
		return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true });
J
Joao Moreno 已提交
183
	}
D
Dirk Baeumer 已提交
184

J
Joao Moreno 已提交
185
	if (process.env['VSCODE_DEV']) {
D
Dirk Baeumer 已提交
186
		return Promise.resolve({ locale: locale, availableLanguages: {} });
J
Joao Moreno 已提交
187
	}
188

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

J
Joao Moreno 已提交
191 192
	// We have a built version so we have extracted nls file. Try to find
	// the right file to use.
193 194 195 196

	// 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 已提交
197
		return Promise.resolve({ locale: locale, availableLanguages: {} });
198 199
	}

D
Dirk Baeumer 已提交
200 201
	let initialLocale = locale;

202 203
	function resolveLocale(locale) {
		while (locale) {
D
Dirk Baeumer 已提交
204
			let candidate = path.join(__dirname, 'vs', 'code', 'electron-main', 'main.nls.') + locale + '.js';
205 206
			if (fs.existsSync(candidate)) {
				return { locale: initialLocale, availableLanguages: { '*': locale } };
J
Joao Moreno 已提交
207
			} else {
D
Dirk Baeumer 已提交
208
				let index = locale.lastIndexOf('-');
209 210 211
				if (index > 0) {
					locale = locale.substring(0, index);
				} else {
D
Dirk Baeumer 已提交
212
					locale = undefined;
213
				}
J
Joao Moreno 已提交
214 215
			}
		}
D
Dirk Baeumer 已提交
216
		return undefined;
J
Joao Moreno 已提交
217 218
	}

219
	perf.mark('nlsGeneration:start');
J
Joao Moreno 已提交
220
	let defaultResult = function (locale) {
221 222 223 224 225 226 227 228 229 230
		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 已提交
231
		} else {
D
Dirk Baeumer 已提交
232 233
			perf.mark('nlsGeneration:end');
			return Promise.resolve({ locale: locale, availableLanguages: {} });
234 235 236
		}
	};
	try {
J
Joao Moreno 已提交
237
		let commit = product.commit;
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
		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 已提交
258
			}
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
			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 已提交
274
					touch(coreLocation).catch(() => { });
275 276
					perf.mark('nlsGeneration:end');
					return result;
D
Dirk Baeumer 已提交
277
				}
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
				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 已提交
301
									}
302
									targetStrings.push(translatedMessage);
D
Dirk Baeumer 已提交
303
								}
304 305
							} else {
								targetStrings = defaultMessages;
D
Dirk Baeumer 已提交
306
							}
307
							target[module] = targetStrings;
D
Dirk Baeumer 已提交
308
						}
309 310 311 312 313 314 315 316 317 318
						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 已提交
319 320
				});
			});
321 322 323 324
		});
	} catch (err) {
		console.error('Generating translation files failed.', err);
		return defaultResult(locale);
325
	}
J
Joao Moreno 已提交
326
}
J
Joao Moreno 已提交
327
//#endregion
J
Joao Moreno 已提交
328

J
Joao Moreno 已提交
329
//#region Cached Data Dir
330
function getNodeCachedDataDir() {
331 332 333 334
	// flag to disable cached data support
	if (process.argv.indexOf('--no-cached-data') > 0) {
		return Promise.resolve(undefined);
	}
335 336 337

	// IEnvironmentService.isBuilt
	if (process.env['VSCODE_DEV']) {
338
		return Promise.resolve(undefined);
339 340
	}

341
	// find commit id
J
Joao Moreno 已提交
342
	let commit = product.commit;
D
Dirk Baeumer 已提交
343
	if (!commit) {
344 345 346
		return Promise.resolve(undefined);
	}

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

J
Joao Moreno 已提交
349
	return mkdirp(dir).then(undefined, function () { /*ignore*/ });
350
}
J
Joao Moreno 已提交
351
//#endregion
352

353
// Set userData path before app 'ready' event and call to process.chdir
D
Dirk Baeumer 已提交
354
let userData = path.resolve(args['user-data-dir'] || paths.getDefaultUserDataPath(process.platform));
355 356
app.setPath('userData', userData);

B
polish  
Benjamin Pasero 已提交
357
// Update cwd based on environment and platform
J
Joao Moreno 已提交
358
try {
B
polish  
Benjamin Pasero 已提交
359
	if (process.platform === 'win32') {
D
Dirk Baeumer 已提交
360
		process.env['VSCODE_CWD'] = process.cwd(); // remember as environment letiable
B
polish  
Benjamin Pasero 已提交
361
		process.chdir(path.dirname(app.getPath('exe'))); // always set application folder as cwd
J
Joao Moreno 已提交
362 363
	} else if (process.env['VSCODE_CWD']) {
		process.chdir(process.env['VSCODE_CWD']);
J
Joao Moreno 已提交
364 365
	}
} catch (err) {
B
polish  
Benjamin Pasero 已提交
366
	console.error(err);
J
Joao Moreno 已提交
367 368 369 370 371
}

// 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 已提交
372
app.on('open-file', function (event, path) {
J
Joao Moreno 已提交
373 374 375
	global.macOpenFiles.push(path);
});

D
Dirk Baeumer 已提交
376 377
let openUrls = [];
let onOpenUrl = function (event, url) {
J
Joao Moreno 已提交
378 379 380 381
	event.preventDefault();
	openUrls.push(url);
};

382 383 384
app.on('will-finish-launching', function () {
	app.on('open-url', onOpenUrl);
});
J
Joao Moreno 已提交
385

386
global.getOpenUrls = function () {
J
Joao Moreno 已提交
387 388 389 390
	app.removeListener('open-url', onOpenUrl);
	return openUrls;
};

391 392
// use '<UserData>/CachedData'-directory to store
// node/v8 cached data.
D
Dirk Baeumer 已提交
393
let nodeCachedDataDir = getNodeCachedDataDir().then(function (value) {
394
	if (value) {
395
		// store the data directory
396
		process.env['VSCODE_NODE_CACHED_DATA_DIR_' + process.pid] = value;
397 398 399

		// 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
400 401
		let existingJSFlags = resolveJSFlags();
		app.commandLine.appendSwitch('--js-flags', existingJSFlags ? existingJSFlags + ' --nolazy' : '--nolazy');
402
	}
D
Dirk Baeumer 已提交
403 404 405 406 407 408 409 410 411
	return value;
});

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

414 415 416 417 418
let jsFlags = resolveJSFlags();
if (jsFlags) {
	app.commandLine.appendSwitch('--js-flags', jsFlags);
}

419 420
// Load our code once ready
app.once('ready', function () {
421
	perf.mark('main:appReady');
D
Dirk Baeumer 已提交
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
	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
449
					appLocale = appLocale.toLowerCase();
D
Dirk Baeumer 已提交
450 451 452 453 454 455 456 457 458
					getNLSConfiguration(appLocale).then((nlsConfig) => {
						if (!nlsConfig) {
							nlsConfig = { locale: appLocale, availableLanguages: {} };
						}
						boot(nlsConfig);
					});
				}
			}
		});
459
	}, console.error);
460
});