This commit is contained in:
Artem Egorov 2018-05-30 13:50:32 +03:00 коммит произвёл GitHub
Родитель e5db4fa56e
Коммит 68c8bfd5da
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
21 изменённых файлов: 1011 добавлений и 1019 удалений

Просмотреть файл

@ -98,7 +98,7 @@ gulp.task('tslint-test', function () {
.pipe(tslint.report());
});
gulp.task('build-src', ['compile-src'/*, 'tslint-src'*/]);
gulp.task('build-src', ['compile-src', 'tslint-src']);
gulp.task('build-test', ['compile-test'/*, 'tslint-test'*/]);
gulp.task('build', ['build-src'/*, 'build-test'*/]);
gulp.task('tslint', ['tslint-src', 'tslint-test']);

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import {Hash} from "../utils/hash"
import {Hash} from "../utils/hash";
import * as Q from "q";
import * as net from "net";
@ -18,7 +18,7 @@ export enum ExtensionMessage {
SIMULATE,
START_SIMULATE_SERVER,
GET_RUN_ARGUMENTS,
GET_SIMULATOR_IN_EXTERNAL_BROWSER_SETTING
GET_SIMULATOR_IN_EXTERNAL_BROWSER_SETTING,
}
export interface MessageWithArguments {

Просмотреть файл

@ -4,5 +4,5 @@
export interface SimulationInfo {
appHostUrl: string;
simHostUrl: string;
urlRoot: string
urlRoot: string;
}

Просмотреть файл

@ -1,29 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import * as fs from 'fs';
import * as path from 'path';
import {SimulateOptions} from 'cordova-simulate';
import * as vscode from 'vscode';
import * as fs from "fs";
import * as path from "path";
import {SimulateOptions} from "cordova-simulate";
import * as vscode from "vscode";
import {CordovaProjectHelper} from './utils/cordovaProjectHelper';
import {CordovaCommandHelper} from './utils/cordovaCommandHelper';
import {ExtensionServer} from './extension/extensionServer';
import * as Q from 'q';
import * as semver from 'semver';
import {PluginSimulator} from './extension/simulate';
import {Telemetry} from './utils/telemetry';
import {TelemetryHelper} from './utils/telemetryHelper';
import {IProjectType} from './utils/cordovaProjectHelper';
import { TsdHelper } from './utils/tsdHelper';
import {CordovaProjectHelper} from "./utils/cordovaProjectHelper";
import {CordovaCommandHelper} from "./utils/cordovaCommandHelper";
import {ExtensionServer} from "./extension/extensionServer";
import * as Q from "q";
import * as semver from "semver";
import {PluginSimulator} from "./extension/simulate";
import {Telemetry} from "./utils/telemetry";
import {TelemetryHelper} from "./utils/telemetryHelper";
import {TsdHelper} from "./utils/tsdHelper";
import { IonicCompletionProvider } from './extension/completionProviders';
import {IonicCompletionProvider} from "./extension/completionProviders";
let PLUGIN_TYPE_DEFS_FILENAME = 'pluginTypings.json';
let PLUGIN_TYPE_DEFS_PATH = path.resolve(__dirname, '..', '..', PLUGIN_TYPE_DEFS_FILENAME);
let CORDOVA_TYPINGS_QUERYSTRING = 'cordova';
let JSCONFIG_FILENAME = 'jsconfig.json';
let TSCONFIG_FILENAME = 'tsconfig.json';
let PLUGIN_TYPE_DEFS_FILENAME = "pluginTypings.json";
let PLUGIN_TYPE_DEFS_PATH = path.resolve(__dirname, "..", "..", PLUGIN_TYPE_DEFS_FILENAME);
let CORDOVA_TYPINGS_QUERYSTRING = "cordova";
let JSCONFIG_FILENAME = "jsconfig.json";
let TSCONFIG_FILENAME = "tsconfig.json";
let projectsCache: {[key: string]: any} = {};
@ -40,14 +39,14 @@ export function activate(context: vscode.ExtensionContext): void {
}
}
export function deactivate(context: vscode.ExtensionContext): void {
console.log('Extension has been deactivated');
export function deactivate(): void {
console.log("Extension has been deactivated");
}
function onChangeWorkspaceFolders(context: vscode.ExtensionContext, event: vscode.WorkspaceFoldersChangeEvent) {
if (event.removed.length) {
event.removed.forEach((folder) => {
onFolderRemoved(context, folder);
onFolderRemoved(folder);
});
}
@ -63,23 +62,23 @@ function onFolderAdded(context: vscode.ExtensionContext, folder: vscode.Workspac
let cordovaProjectRoot = CordovaProjectHelper.getCordovaProjectRoot(workspaceRoot);
// Asynchronously enable telemetry
Telemetry.init('cordova-tools', require('./../../package.json').version, { isExtensionProcess: true, projectRoot: workspaceRoot });
Telemetry.init("cordova-tools", require("./../../package.json").version, { isExtensionProcess: true, projectRoot: workspaceRoot });
if (!cordovaProjectRoot) {
return;
}
if (path.resolve(cordovaProjectRoot) !== path.resolve(workspaceRoot)) {
vscode.window.showWarningMessage('VSCode Cordova extension requires the workspace root to be your Cordova project\'s root. The extension hasn\'t been activated.');
vscode.window.showWarningMessage("VSCode Cordova extension requires the workspace root to be your Cordova project's root. The extension hasn't been activated.");
return;
}
let activateExtensionEvent = TelemetryHelper.createTelemetryEvent('activate');
let activateExtensionEvent = TelemetryHelper.createTelemetryEvent("activate");
TelemetryHelper.determineProjectTypes(cordovaProjectRoot)
.then((projType) => {
activateExtensionEvent.properties['projectType'] = projType;
activateExtensionEvent.properties["projectType"] = projType;
})
.finally(() => {
Telemetry.send(activateExtensionEvent);
@ -90,10 +89,10 @@ function onFolderAdded(context: vscode.ExtensionContext, folder: vscode.Workspac
// setup a file system watcher to watch changes to plugins in the Cordova project
// Note that watching plugins/fetch.json file would suffice
let watcher = vscode.workspace.createFileSystemWatcher('**/plugins/fetch.json', false /*ignoreCreateEvents*/, false /*ignoreChangeEvents*/, false /*ignoreDeleteEvents*/);
watcher.onDidChange((e: vscode.Uri) => updatePluginTypeDefinitions(cordovaProjectRoot));
watcher.onDidDelete((e: vscode.Uri) => updatePluginTypeDefinitions(cordovaProjectRoot));
watcher.onDidCreate((e: vscode.Uri) => updatePluginTypeDefinitions(cordovaProjectRoot));
let watcher = vscode.workspace.createFileSystemWatcher("**/plugins/fetch.json", false /*ignoreCreateEvents*/, false /*ignoreChangeEvents*/, false /*ignoreDeleteEvents*/);
watcher.onDidChange(() => updatePluginTypeDefinitions(cordovaProjectRoot));
watcher.onDidDelete(() => updatePluginTypeDefinitions(cordovaProjectRoot));
watcher.onDidCreate(() => updatePluginTypeDefinitions(cordovaProjectRoot));
context.subscriptions.push(watcher);
let simulator: PluginSimulator = new PluginSimulator();
@ -103,7 +102,7 @@ function onFolderAdded(context: vscode.ExtensionContext, folder: vscode.Workspac
projectsCache[workspaceRoot] = {
extensionServer,
cordovaProjectRoot,
folder
folder,
};
// extensionServer takes care of disposing the simulator instance
@ -114,24 +113,24 @@ function onFolderAdded(context: vscode.ExtensionContext, folder: vscode.Workspac
context.subscriptions.push(
vscode.languages.registerCompletionItemProvider(
IonicCompletionProvider.JS_DOCUMENT_SELECTOR,
new IonicCompletionProvider(path.resolve(__dirname, '../../snippets/ionicJs.json'))));
new IonicCompletionProvider(path.resolve(__dirname, "../../snippets/ionicJs.json"))));
context.subscriptions.push(
vscode.languages.registerCompletionItemProvider(
IonicCompletionProvider.HTML_DOCUMENT_SELECTOR,
new IonicCompletionProvider(path.resolve(__dirname, '../../snippets/ionicHtml.json'))));
new IonicCompletionProvider(path.resolve(__dirname, "../../snippets/ionicHtml.json"))));
}
// Install Ionic type definitions if necessary
if (CordovaProjectHelper.isIonicProject(cordovaProjectRoot)) {
let ionicTypings: string[] = [
path.join('jquery', 'jquery.d.ts'),
path.join('cordova-ionic', 'plugins', 'keyboard.d.ts')
path.join("jquery", "jquery.d.ts"),
path.join("cordova-ionic", "plugins", "keyboard.d.ts"),
];
if (CordovaProjectHelper.isIonic1Project(cordovaProjectRoot)) {
ionicTypings = ionicTypings.concat([
path.join('angularjs', 'angular.d.ts'),
path.join('ionic', 'ionic.d.ts')
path.join("angularjs", "angular.d.ts"),
path.join("ionic", "ionic.d.ts"),
]);
}
TsdHelper.installTypings(CordovaProjectHelper.getOrCreateTypingsTargetPath(cordovaProjectRoot), ionicTypings, cordovaProjectRoot);
@ -155,7 +154,7 @@ function onFolderAdded(context: vscode.ExtensionContext, folder: vscode.Workspac
// Install type definition files for the currently installed plugins
updatePluginTypeDefinitions(cordovaProjectRoot);
let pluginFilePath = path.join(cordovaProjectRoot, '.vscode', 'plugins.json');
let pluginFilePath = path.join(cordovaProjectRoot, ".vscode", "plugins.json");
if (fs.existsSync(pluginFilePath)) {
fs.unlinkSync(pluginFilePath);
}
@ -168,15 +167,15 @@ function onFolderAdded(context: vscode.ExtensionContext, folder: vscode.Workspac
Q.all([Q.nfcall(fs.exists, jsconfigPath), Q.nfcall(fs.exists, tsconfigPath)]).spread((jsExists: boolean, tsExists: boolean) => {
if (!jsExists && !tsExists) {
Q.nfcall(fs.writeFile, jsconfigPath, '{}').then(() => {
Q.nfcall(fs.writeFile, jsconfigPath, "{}").then(() => {
// Any open file must be reloaded to enable intellisense on them, so inform the user
vscode.window.showInformationMessage('A \'jsconfig.json\' file was created to enable IntelliSense. You may need to reload your open JS file(s).');
vscode.window.showInformationMessage("A 'jsconfig.json' file was created to enable IntelliSense. You may need to reload your open JS file(s).");
});
}
});
}
function onFolderRemoved(context: vscode.ExtensionContext, folder: vscode.WorkspaceFolder): void {
function onFolderRemoved(folder: vscode.WorkspaceFolder): void {
delete projectsCache[folder.uri.fsPath];
}
@ -185,7 +184,7 @@ function getPluginTypingsJson(): any {
return require(PLUGIN_TYPE_DEFS_PATH);
}
console.error('Cordova plugin type declaration mapping file \'pluginTypings.json\' is missing from the extension folder.');
console.error("Cordova plugin type declaration mapping file 'pluginTypings.json' is missing from the extension folder.");
return null;
}
@ -211,8 +210,8 @@ function addPluginTypeDefinitions(projectRoot: string, installedPlugins: string[
}
// If we do not know the plugin, collect it anonymously for future prioritisation
let unknownPluginEvent = TelemetryHelper.createTelemetryEvent('unknownPlugin');
unknownPluginEvent.setPiiProperty('plugin', pluginName);
let unknownPluginEvent = TelemetryHelper.createTelemetryEvent("unknownPlugin");
unknownPluginEvent.setPiiProperty("plugin", pluginName);
Telemetry.send(unknownPluginEvent);
return false;
}).map((pluginName: string) => {
@ -226,13 +225,13 @@ function addPluginTypeDefinitions(projectRoot: string, installedPlugins: string[
function removePluginTypeDefinitions(projectRoot: string, currentTypeDefs: string[], newTypeDefs: string[]): void {
// Find the type definition files that need to be removed
let typeDefsToRemove = currentTypeDefs
.filter((typeDef: string) => newTypeDefs.indexOf(typeDef) < 0)
.filter((typeDef: string) => newTypeDefs.indexOf(typeDef) < 0);
TsdHelper.removeTypings(CordovaProjectHelper.getOrCreateTypingsTargetPath(projectRoot), typeDefsToRemove, projectRoot);
}
function getRelativeTypeDefinitionFilePath(projectRoot: string, parentPath: string, typeDefinitionFile: string) {
return path.relative(CordovaProjectHelper.getOrCreateTypingsTargetPath(projectRoot), path.resolve(parentPath, typeDefinitionFile)).replace(/\\/g, '\/')
return path.relative(CordovaProjectHelper.getOrCreateTypingsTargetPath(projectRoot), path.resolve(parentPath, typeDefinitionFile)).replace(/\\/g, "\/");
}
function updatePluginTypeDefinitions(cordovaProjectRoot: string): void {
@ -248,8 +247,8 @@ function updatePluginTypeDefinitions(cordovaProjectRoot: string): void {
let installedPlugins: string[] = CordovaProjectHelper.getInstalledPlugins(cordovaProjectRoot);
const nodeModulesDir = path.resolve(cordovaProjectRoot, 'node_modules');
if (semver.gte(vscode.version, '1.7.2-insider') && fs.existsSync(nodeModulesDir)) {
const nodeModulesDir = path.resolve(cordovaProjectRoot, "node_modules");
if (semver.gte(vscode.version, "1.7.2-insider") && fs.existsSync(nodeModulesDir)) {
// Read installed node modules and filter out plugins that have been already installed in node_modules
// This happens if user has used '--fetch' option to install plugin. In this case VSCode will provide
// own intellisense for these plugins using ATA (automatic typings acquisition)
@ -282,12 +281,19 @@ function updatePluginTypeDefinitions(cordovaProjectRoot: string): void {
// Now read the type definitions of Cordova plugins
fs.readdir(cordovaPluginTypesFolder, (err: Error, cordovaTypeDefs: string[]) => {
if (err) {
// ignore
}
if (cordovaTypeDefs) {
currentTypeDefs = cordovaTypeDefs.map(typeDef => getRelativeTypeDefinitionFilePath(cordovaProjectRoot, cordovaPluginTypesFolder, typeDef));
}
// Now read the type definitions of Ionic plugins
fs.readdir(ionicPluginTypesFolder, (err: Error, ionicTypeDefs: string[]) => {
if (err) {
// ignore
}
if (ionicTypeDefs) {
currentTypeDefs.concat(ionicTypeDefs.map(typeDef => getRelativeTypeDefinitionFilePath(cordovaProjectRoot, ionicPluginTypesFolder, typeDef)));
}
@ -300,13 +306,13 @@ function updatePluginTypeDefinitions(cordovaProjectRoot: string): void {
/* Launches a simulate command and records telemetry for it */
function launchSimulateCommand(cordovaProjectRoot: string, options: SimulateOptions): Q.Promise<void> {
return TelemetryHelper.generate('simulateCommand', (generator) => {
return TelemetryHelper.generate("simulateCommand", (generator) => {
return TelemetryHelper.determineProjectTypes(cordovaProjectRoot)
.then((projectType) => {
generator.add('simulateOptions', options, false);
generator.add('projectType', projectType, false);
generator.add("simulateOptions", options, false);
generator.add("projectType", projectType, false);
// visibleTextEditors is null proof (returns empty array if no editors visible)
generator.add('visibleTextEditorsCount', vscode.window.visibleTextEditors.length, false);
generator.add("visibleTextEditorsCount", vscode.window.visibleTextEditors.length, false);
return projectType;
});
}).then((projectType) => {
@ -314,25 +320,25 @@ function launchSimulateCommand(cordovaProjectRoot: string, options: SimulateOpti
const workspaceFolder = <vscode.WorkspaceFolder>vscode.workspace.getWorkspaceFolder(uri);
return projectsCache[workspaceFolder.uri.fsPath].extensionServer.pluginSimulator.simulate(cordovaProjectRoot, options, projectType);
});
};
}
function registerCordovaCommands(context: vscode.ExtensionContext): void {
context.subscriptions.push(vscode.commands.registerCommand('cordova.prepare', () => commandWrapper(CordovaCommandHelper.executeCordovaCommand, ['prepare'])));
context.subscriptions.push(vscode.commands.registerCommand('cordova.build', () => commandWrapper(CordovaCommandHelper.executeCordovaCommand, ['build'])));
context.subscriptions.push(vscode.commands.registerCommand('cordova.run', () => commandWrapper(CordovaCommandHelper.executeCordovaCommand, ['run'])));
context.subscriptions.push(vscode.commands.registerCommand('ionic.prepare', () => commandWrapper(CordovaCommandHelper.executeCordovaCommand, ['prepare', true])));
context.subscriptions.push(vscode.commands.registerCommand('ionic.build', () => commandWrapper(CordovaCommandHelper.executeCordovaCommand, ['build', true])));
context.subscriptions.push(vscode.commands.registerCommand('ionic.run', () => commandWrapper(CordovaCommandHelper.executeCordovaCommand, ['run', true])));
context.subscriptions.push(vscode.commands.registerCommand('cordova.simulate.android', () => {
context.subscriptions.push(vscode.commands.registerCommand("cordova.prepare", () => commandWrapper(CordovaCommandHelper.executeCordovaCommand, ["prepare"])));
context.subscriptions.push(vscode.commands.registerCommand("cordova.build", () => commandWrapper(CordovaCommandHelper.executeCordovaCommand, ["build"])));
context.subscriptions.push(vscode.commands.registerCommand("cordova.run", () => commandWrapper(CordovaCommandHelper.executeCordovaCommand, ["run"])));
context.subscriptions.push(vscode.commands.registerCommand("ionic.prepare", () => commandWrapper(CordovaCommandHelper.executeCordovaCommand, ["prepare", true])));
context.subscriptions.push(vscode.commands.registerCommand("ionic.build", () => commandWrapper(CordovaCommandHelper.executeCordovaCommand, ["build", true])));
context.subscriptions.push(vscode.commands.registerCommand("ionic.run", () => commandWrapper(CordovaCommandHelper.executeCordovaCommand, ["run", true])));
context.subscriptions.push(vscode.commands.registerCommand("cordova.simulate.android", () => {
return selectProject()
.then((project) => {
return launchSimulateCommand(project.cordovaProjectRoot, { dir: project.folder.uri.fsPath, target: 'chrome', platform: 'android' });
return launchSimulateCommand(project.cordovaProjectRoot, { dir: project.folder.uri.fsPath, target: "chrome", platform: "android" });
});
}));
context.subscriptions.push(vscode.commands.registerCommand('cordova.simulate.ios', () => {
context.subscriptions.push(vscode.commands.registerCommand("cordova.simulate.ios", () => {
return selectProject()
.then((project) => {
return launchSimulateCommand(project.cordovaProjectRoot, { dir: project.folder.uri.fsPath, target: 'chrome', platform: 'ios' });
return launchSimulateCommand(project.cordovaProjectRoot, { dir: project.folder.uri.fsPath, target: "chrome", platform: "ios" });
});
}));
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,14 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
'use strict';
"use strict";
import * as child_process from 'child_process';
import * as fs from 'fs';
import * as net from 'net';
import * as path from 'path';
import * as pl from 'plist';
import * as Q from 'q';
import * as child_process from "child_process";
import * as fs from "fs";
import * as net from "net";
import * as path from "path";
import * as pl from "plist";
import * as Q from "q";
let promiseExec = Q.denodeify(child_process.exec);
@ -18,7 +18,7 @@ export class CordovaIosDeviceLauncher {
public static cleanup(): void {
if (CordovaIosDeviceLauncher.nativeDebuggerProxyInstance) {
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance.kill('SIGHUP');
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance.kill("SIGHUP");
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance = null;
}
if (CordovaIosDeviceLauncher.webDebuggerProxyInstance) {
@ -28,29 +28,29 @@ export class CordovaIosDeviceLauncher {
}
public static getBundleIdentifier(projectRoot: string): Q.Promise<string> {
return Q.nfcall(fs.readdir, path.join(projectRoot, 'platforms', 'ios')).then((files: string[]) => {
return Q.nfcall(fs.readdir, path.join(projectRoot, "platforms", "ios")).then((files: string[]) => {
let xcodeprojfiles = files.filter((file: string) => /\.xcodeproj$/.test(file));
if (xcodeprojfiles.length === 0) {
throw new Error('Unable to find xcodeproj file');
throw new Error("Unable to find xcodeproj file");
}
let xcodeprojfile = xcodeprojfiles[0];
let projectName = /^(.*)\.xcodeproj/.exec(xcodeprojfile)[1];
let filepath = path.join(projectRoot, 'platforms', 'ios', projectName, projectName + '-Info.plist');
let plist = pl.parse(fs.readFileSync(filepath, 'utf8'));
let filepath = path.join(projectRoot, "platforms", "ios", projectName, projectName + "-Info.plist");
let plist = pl.parse(fs.readFileSync(filepath, "utf8"));
return plist.CFBundleIdentifier;
});
}
public static startDebugProxy(proxyPort: number): Q.Promise<child_process.ChildProcess> {
if (CordovaIosDeviceLauncher.nativeDebuggerProxyInstance) {
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance.kill('SIGHUP'); // idevicedebugserver does not exit from SIGTERM
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance.kill("SIGHUP"); // idevicedebugserver does not exit from SIGTERM
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance = null;
}
return CordovaIosDeviceLauncher.mountDeveloperImage().then(function (): Q.Promise<child_process.ChildProcess> {
let deferred = Q.defer<child_process.ChildProcess>();
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance = child_process.spawn('idevicedebugserverproxy', [proxyPort.toString()]);
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance.on('error', function (err: any): void {
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance = child_process.spawn("idevicedebugserverproxy", [proxyPort.toString()]);
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance.on("error", function (err: any): void {
deferred.reject(err);
});
// Allow 200ms for the spawn to error out, ~125ms isn't uncommon for some failures
@ -68,9 +68,9 @@ export class CordovaIosDeviceLauncher {
let deferred = Q.defer();
let portRange = `null:${proxyPort},:${proxyRangeStart}-${proxyRangeEnd}`;
CordovaIosDeviceLauncher.webDebuggerProxyInstance = child_process.spawn('ios_webkit_debug_proxy', ['-c', portRange]);
CordovaIosDeviceLauncher.webDebuggerProxyInstance.on('error', function (err: Error) {
deferred.reject(new Error('Unable to start ios_webkit_debug_proxy.'));
CordovaIosDeviceLauncher.webDebuggerProxyInstance = child_process.spawn("ios_webkit_debug_proxy", ["-c", portRange]);
CordovaIosDeviceLauncher.webDebuggerProxyInstance.on("error", function () {
deferred.reject(new Error("Unable to start ios_webkit_debug_proxy."));
});
// Allow some time for the spawned process to error out
Q.delay(250).then(() => deferred.resolve({}));
@ -87,20 +87,20 @@ export class CordovaIosDeviceLauncher {
}
public static getPathOnDevice(packageId: string): Q.Promise<string> {
return promiseExec('ideviceinstaller -l -o xml > /tmp/$$.ideviceinstaller && echo /tmp/$$.ideviceinstaller')
return promiseExec("ideviceinstaller -l -o xml > /tmp/$$.ideviceinstaller && echo /tmp/$$.ideviceinstaller")
.catch(function (err: any): any {
if (err.code === 'ENOENT') {
throw new Error('Unable to find ideviceinstaller.');
if (err.code === "ENOENT") {
throw new Error("Unable to find ideviceinstaller.");
}
throw err;
}).spread<string>(function (stdout: string, stderr: string): string {
}).spread<string>(function (stdout: string): string {
// First find the path of the app on the device
let filename: string = stdout.trim();
if (!/^\/tmp\/[0-9]+\.ideviceinstaller$/.test(filename)) {
throw new Error('Unable to list installed applications on device');
throw new Error("Unable to list installed applications on device");
}
let list: any[] = pl.parse(fs.readFileSync(filename, 'utf8'));
let list: any[] = pl.parse(fs.readFileSync(filename, "utf8"));
fs.unlink(filename);
for (let i: number = 0; i < list.length; ++i) {
if (list[i].CFBundleIdentifier === packageId) {
@ -109,7 +109,7 @@ export class CordovaIosDeviceLauncher {
}
}
throw new Error('Application not installed on the device');
throw new Error("Application not installed on the device");
});
}
@ -131,23 +131,23 @@ export class CordovaIosDeviceLauncher {
let deferred2: Q.Deferred<net.Socket> = Q.defer<net.Socket>();
let deferred3: Q.Deferred<net.Socket> = Q.defer<net.Socket>();
socket.on('data', function (data: any): void {
socket.on("data", function (data: any): void {
data = data.toString();
while (data[0] === '+') { data = data.substring(1); }
while (data[0] === "+") { data = data.substring(1); }
// Acknowledge any packets sent our way
if (data[0] === '$') {
socket.write('+');
if (data[1] === 'W') {
if (data[0] === "$") {
socket.write("+");
if (data[1] === "W") {
// The app process has exited, with hex status given by data[2-3]
let status: number = parseInt(data.substring(2, 4), 16);
endStatus = status;
socket.end();
} else if (data[1] === 'X') {
} else if (data[1] === "X") {
// The app rocess exited because of signal given by data[2-3]
let signal: number = parseInt(data.substring(2, 4), 16);
endSignal = signal;
socket.end();
} else if (data.substring(1, 3) === 'OK') {
} else if (data.substring(1, 3) === "OK") {
// last command was received OK;
if (initState === 1) {
deferred1.resolve(socket);
@ -158,19 +158,19 @@ export class CordovaIosDeviceLauncher {
deferred3.resolve(socket);
initState++;
}
} else if (data[1] === 'O') {
} else if (data[1] === "O") {
// STDOUT was written to, and the rest of the input until reaching a '#' is a hex-encoded string of that output
if (initState === 3) {
deferred3.resolve(socket);
initState++;
}
} else if (data[1] === 'E') {
} else if (data[1] === "E") {
// An error has occurred, with error code given by data[2-3]: parseInt(data.substring(2, 4), 16)
deferred1.reject('Unable to launch application.');
deferred2.reject('Unable to launch application.');
deferred3.reject('Unable to launch application.');
deferred1.reject("Unable to launch application.");
deferred2.reject("Unable to launch application.");
deferred3.reject("Unable to launch application.");
}
} else if (data === '' && initState === 3) {
} else if (data === "" && initState === 3) {
// On iOS 10.2.1 (and maybe others) after 'c' message debug server doesn't respond with '$OK', see also
// http://www.embecosm.com/appnotes/ean4/embecosm-howto-rsp-server-ean4-issue-2.html#sec_exchange_cont
deferred3.resolve(socket);
@ -178,44 +178,44 @@ export class CordovaIosDeviceLauncher {
}
});
socket.on('end', function (): void {
deferred1.reject('Unable to launch application.');
deferred2.reject('Unable to launch application.');
deferred3.reject('Unable to launch application.');
socket.on("end", function (): void {
deferred1.reject("Unable to launch application.");
deferred2.reject("Unable to launch application.");
deferred3.reject("Unable to launch application.");
});
socket.on('error', function (err: Error): void {
socket.on("error", function (err: Error): void {
deferred1.reject(err);
deferred2.reject(err);
deferred3.reject(err);
});
socket.connect(portNumber, 'localhost', function (): void {
socket.connect(portNumber, "localhost", function (): void {
// set argument 0 to the (encoded) path of the app
let cmd: string = CordovaIosDeviceLauncher.makeGdbCommand('A' + encodedPath.length + ',0,' + encodedPath);
let cmd: string = CordovaIosDeviceLauncher.makeGdbCommand("A" + encodedPath.length + ",0," + encodedPath);
initState++;
socket.write(cmd);
setTimeout(function (): void {
deferred1.reject('Timeout launching application. Is the device locked?');
deferred1.reject("Timeout launching application. Is the device locked?");
}, appLaunchStepTimeout);
});
return deferred1.promise.then(function (sock: net.Socket): Q.Promise<net.Socket> {
// Set the step and continue thread to any thread
let cmd: string = CordovaIosDeviceLauncher.makeGdbCommand('Hc0');
let cmd: string = CordovaIosDeviceLauncher.makeGdbCommand("Hc0");
initState++;
sock.write(cmd);
setTimeout(function (): void {
deferred2.reject('Timeout launching application. Is the device locked?');
deferred2.reject("Timeout launching application. Is the device locked?");
}, appLaunchStepTimeout);
return deferred2.promise;
}).then(function (sock: net.Socket): Q.Promise<net.Socket> {
// Continue execution; actually start the app running.
let cmd: string = CordovaIosDeviceLauncher.makeGdbCommand('c');
let cmd: string = CordovaIosDeviceLauncher.makeGdbCommand("c");
initState++;
sock.write(cmd);
setTimeout(function (): void {
deferred3.reject('Timeout launching application. Is the device locked?');
deferred3.reject("Timeout launching application. Is the device locked?");
}, appLaunchStepTimeout);
return deferred3.promise;
}).then(() => packagePath);
@ -223,32 +223,32 @@ export class CordovaIosDeviceLauncher {
public static encodePath(packagePath: string): string {
// Encode the path by converting each character value to hex
return packagePath.split('').map((c: string) => c.charCodeAt(0).toString(16)).join('').toUpperCase();
return packagePath.split("").map((c: string) => c.charCodeAt(0).toString(16)).join("").toUpperCase();
}
private static mountDeveloperImage(): Q.Promise<any> {
return CordovaIosDeviceLauncher.getDiskImage()
.then(function (path: string): Q.Promise<any> {
let imagemounter: child_process.ChildProcess = child_process.spawn('ideviceimagemounter', [path]);
let imagemounter: child_process.ChildProcess = child_process.spawn("ideviceimagemounter", [path]);
let deferred: Q.Deferred<any> = Q.defer();
let stdout: string = '';
imagemounter.stdout.on('data', function (data: any): void {
let stdout: string = "";
imagemounter.stdout.on("data", function (data: any): void {
stdout += data.toString();
});
imagemounter.on('close', function (code: number): void {
imagemounter.on("close", function (code: number): void {
if (code !== 0) {
if (stdout.indexOf('Error:') !== -1) {
if (stdout.indexOf("Error:") !== -1) {
deferred.resolve({}); // Technically failed, but likely caused by the image already being mounted.
} else if (stdout.indexOf('No device found, is it plugged in?') !== -1) {
deferred.reject('Unable to find device. Is the device plugged in?');
} else if (stdout.indexOf("No device found, is it plugged in?") !== -1) {
deferred.reject("Unable to find device. Is the device plugged in?");
}
deferred.reject('Unable to mount developer disk image.');
deferred.reject("Unable to mount developer disk image.");
} else {
deferred.resolve({});
}
});
imagemounter.on('error', function(err: any): void {
imagemounter.on("error", function(err: any): void {
deferred.reject(err);
});
return deferred.promise;
@ -257,37 +257,37 @@ export class CordovaIosDeviceLauncher {
private static getDiskImage(): Q.Promise<string> {
// Attempt to find the OS version of the iDevice, e.g. 7.1
let versionInfo: Q.Promise<any> = promiseExec('ideviceinfo -s -k ProductVersion')
.spread<string>(function (stdout: string, stderr: string): string {
let versionInfo: Q.Promise<any> = promiseExec("ideviceinfo -s -k ProductVersion")
.spread<string>(function (stdout: string): string {
// Versions for DeveloperDiskImage seem to be X.Y, while some device versions are X.Y.Z
return /^(\d+\.\d+)(?:\.\d+)?$/gm.exec(stdout.trim())[1];
})
.catch(function (e): string {
throw new Error('Unable to get device OS version. Details: ${e.message}');
throw new Error(`Unable to get device OS version. Details: ${e.message}`);
});
// Attempt to find the path where developer resources exist.
let pathInfo: Q.Promise<any> = promiseExec('xcrun -sdk iphoneos --show-sdk-platform-path').spread<string>(function (stdout: string, stderr: string): string {
let pathInfo: Q.Promise<any> = promiseExec("xcrun -sdk iphoneos --show-sdk-platform-path").spread<string>(function (stdout: string): string {
let sdkpath: string = stdout.trim();
return sdkpath;
});
// Attempt to find the developer disk image for the appropriate
return Q.all([versionInfo, pathInfo]).spread<string>(function (version: string, sdkpath: string): Q.Promise<string> {
let find: child_process.ChildProcess = child_process.spawn('find', [sdkpath, '-path', '*' + version + '*', '-name', 'DeveloperDiskImage.dmg']);
let find: child_process.ChildProcess = child_process.spawn("find", [sdkpath, "-path", "*" + version + "*", "-name", "DeveloperDiskImage.dmg"]);
let deferred: Q.Deferred<string> = Q.defer<string>();
find.stdout.on('data', function (data: any): void {
find.stdout.on("data", function (data: any): void {
let dataStr: string = data.toString();
let path: string = dataStr.split('\n')[0].trim();
let path: string = dataStr.split("\n")[0].trim();
if (!path) {
deferred.reject('Unable to find developer disk image');
deferred.reject("Unable to find developer disk image");
} else {
deferred.resolve(path);
}
});
find.on('close', function (code: number): void {
deferred.reject('Unable to find developer disk image');
find.on("close", function (): void {
deferred.reject("Unable to find developer disk image");
});
return deferred.promise;
@ -295,7 +295,7 @@ export class CordovaIosDeviceLauncher {
}
private static makeGdbCommand(command: string): string {
let commandString: string = '$' + command + '#';
let commandString: string = "$" + command + "#";
let stringSum: number = 0;
for (let i: number = 0; i < command.length; i++) {
stringSum += command.charCodeAt(i);
@ -307,7 +307,7 @@ export class CordovaIosDeviceLauncher {
/* tslint:enable:no-bitwise */
let checksum: string = stringSum.toString(16).toUpperCase();
if (checksum.length < 2) {
checksum = '0' + checksum;
checksum = "0" + checksum;
}
commandString += checksum;
@ -315,4 +315,4 @@ export class CordovaIosDeviceLauncher {
}
}
process.on('exit', CordovaIosDeviceLauncher.cleanup);
process.on("exit", CordovaIosDeviceLauncher.cleanup);

Просмотреть файл

@ -1,14 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import {DebugProtocol} from 'vscode-debugprotocol';
import {utils, logger, chromeUtils, ISetBreakpointsArgs, IDebugTransformer, IStackTraceResponseBody} from 'vscode-chrome-debug-core';
import {ICordovaLaunchRequestArgs, ICordovaAttachRequestArgs} from './cordovaDebugAdapter';
import {utils, logger, chromeUtils, ISetBreakpointsArgs, IStackTraceResponseBody} from "vscode-chrome-debug-core";
import {ICordovaLaunchRequestArgs, ICordovaAttachRequestArgs} from "./cordovaDebugAdapter";
import {BasePathTransformer} from 'vscode-chrome-debug-core';
import {BasePathTransformer} from "vscode-chrome-debug-core";
import * as path from 'path';
import * as fs from 'fs';
import * as path from "path";
import * as fs from "fs";
interface IPendingBreakpoint {
resolve: () => void;
@ -130,16 +129,16 @@ export class CordovaPathTransformer extends BasePathTransformer {
}
public getClientPath(sourceUrl: string): string {
let wwwRoot = path.join(this._cordovaRoot, 'www');
let wwwRoot = path.join(this._cordovaRoot, "www");
// Given an absolute file:/// (such as from the iOS simulator) vscode-chrome-debug's
// default behavior is to use that exact file, if it exists. We don't want that,
// since we know that those files are copies of files in the local folder structure.
// A simple workaround for this is to convert file:// paths to bogus http:// paths
sourceUrl = sourceUrl.replace('file:///', 'http://localhost/');
sourceUrl = sourceUrl.replace("file:///", "http://localhost/");
// Find the mapped local file. Try looking first in the user-specified webRoot, then in the project root, and then in the www folder
let defaultPath = '';
let defaultPath = "";
[this._webRoot, this._cordovaRoot, wwwRoot].find((searchFolder) => {
let mappedPath = chromeUtils.targetUrlToClientPath(searchFolder, sourceUrl);
@ -154,7 +153,7 @@ export class CordovaPathTransformer extends BasePathTransformer {
if (defaultPath.toLowerCase().indexOf(wwwRoot.toLowerCase()) === 0) {
// If the path appears to be in www, check to see if it exists in /merges/<platform>/<relative path>
let relativePath = path.relative(wwwRoot, defaultPath);
let mergesPath = path.join(this._cordovaRoot, 'merges', this._platform, relativePath);
let mergesPath = path.join(this._cordovaRoot, "merges", this._platform, relativePath);
if (fs.existsSync(mergesPath)) {
// This file is overriden by a merge: Use that one
if (fs.existsSync(defaultPath)) {

Просмотреть файл

@ -1,13 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import {ChromeDebugSession, BaseSourceMapTransformer, BasePathTransformer} from 'vscode-chrome-debug-core';
import {CordovaDebugAdapter} from './cordovaDebugAdapter';
import {CordovaPathTransformer} from './cordovaPathTransformer';
import {ChromeDebugSession, BaseSourceMapTransformer, BasePathTransformer} from "vscode-chrome-debug-core";
import {CordovaDebugAdapter} from "./cordovaDebugAdapter";
import {CordovaPathTransformer} from "./cordovaPathTransformer";
ChromeDebugSession.run(ChromeDebugSession.getSession({
adapter: CordovaDebugAdapter,
extensionName: 'cordova-tools',
extensionName: "cordova-tools",
pathTransformer: <typeof BasePathTransformer><any>CordovaPathTransformer,
sourceMapTransformer: BaseSourceMapTransformer
sourceMapTransformer: BaseSourceMapTransformer,
}));

Просмотреть файл

@ -1,37 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import * as child_process from 'child_process';
import {CordovaProjectHelper} from '../utils/cordovaProjectHelper';
import * as os from 'os';
import * as Q from 'q';
import * as path from 'path';
import * as util from 'util';
import { ExtensionMessageSender, ExtensionMessage } from '../common/extensionMessaging';
import { CordovaCommandHelper } from '../utils/cordovaCommandHelper';
import * as child_process from "child_process";
import {CordovaProjectHelper} from "../utils/cordovaProjectHelper";
import * as Q from "q";
import * as path from "path";
import * as util from "util";
// suppress the following strings because they are not actual errors:
const errorsToSuppress = ['Run an Ionic project on a connected device'];
const errorsToSuppress = ["Run an Ionic project on a connected device"];
export function execCommand(command: string, args: string[], errorLogger: (message: string) => void): Q.Promise<string> {
let deferred = Q.defer<string>();
let proc = child_process.spawn(command, args, { stdio: 'pipe' });
let stderr = '';
let stdout = '';
proc.stderr.on('data', (data: Buffer) => {
let proc = child_process.spawn(command, args, { stdio: "pipe" });
let stderr = "";
let stdout = "";
proc.stderr.on("data", (data: Buffer) => {
stderr += data.toString();
});
proc.stdout.on('data', (data: Buffer) => {
proc.stdout.on("data", (data: Buffer) => {
stdout += data.toString();
});
proc.on('error', (err: Error) => {
proc.on("error", (err: Error) => {
deferred.reject(err);
});
proc.on('close', (code: number) => {
proc.on("close", (code: number) => {
if (code !== 0) {
errorLogger(stderr);
errorLogger(stdout);
deferred.reject(`Error running '${command} ${args.join(' ')}'`);
deferred.reject(`Error running '${command} ${args.join(" ")}'`);
}
deferred.resolve(stdout);
});
@ -42,54 +39,54 @@ export function execCommand(command: string, args: string[], errorLogger: (messa
export function cordovaRunCommand(args: string[], cordovaRootPath: string): Q.Promise<string[]> {
let defer = Q.defer<string[]>();
let isIonicProject = CordovaProjectHelper.isIonicProject(cordovaRootPath);
let cliName = isIonicProject ? 'ionic' : 'cordova';
let output = '';
let stderr = '';
let cliName = isIonicProject ? "ionic" : "cordova";
let output = "";
let stderr = "";
let cordovaProcess = cordovaStartCommand(args, cordovaRootPath);
// Prevent these lines to be shown more than once
// to prevent debug console pollution
let isShown = {
'Running command': false,
'cordova prepare': false,
'cordova platform add': false
"Running command": false,
"cordova prepare": false,
"cordova platform add": false,
};
cordovaProcess.stderr.on('data', data => {
cordovaProcess.stderr.on("data", data => {
stderr += data.toString();
for (var i = 0; i < errorsToSuppress.length; i++) {
for (let i = 0; i < errorsToSuppress.length; i++) {
if (data.toString().indexOf(errorsToSuppress[i]) >= 0) {
return;
}
}
defer.notify([data.toString(), 'stderr']);
defer.notify([data.toString(), "stderr"]);
});
cordovaProcess.stdout.on('data', (data: Buffer) => {
let str = data.toString().replace(/\u001b/g, '').replace(/\[2K\[G/g, ''); // Erasing `[2K[G` artifacts from DEBUG CONSOLE output
cordovaProcess.stdout.on("data", (data: Buffer) => {
let str = data.toString().replace(/\u001b/g, "").replace(/\[2K\[G/g, ""); // Erasing `[2K[G` artifacts from DEBUG CONSOLE output
output += str;
for (let message in isShown) {
if (str.indexOf(message) > -1) {
if (!isShown[message]) {
isShown[message] = true;
defer.notify([str, 'stdout']);
defer.notify([str, "stdout"]);
}
return;
}
}
defer.notify([str, 'stdout']);
defer.notify([str, "stdout"]);
if (isIonicProject && str.indexOf('LAUNCH SUCCESS') >= 0) {
if (isIonicProject && str.indexOf("LAUNCH SUCCESS") >= 0) {
defer.resolve([output, stderr]);
}
});
cordovaProcess.on('exit', exitCode => {
cordovaProcess.on("exit", exitCode => {
if (exitCode) {
defer.reject(new Error(util.format('\'%s %s\' failed with exit code %d', cliName, args.join(' '), exitCode)));
defer.reject(new Error(util.format("'%s %s' failed with exit code %d", cliName, args.join(" "), exitCode)));
} else {
defer.resolve([output, stderr]);
}
});
cordovaProcess.on('error', error => {
cordovaProcess.on("error", error => {
defer.reject(error);
});
@ -99,29 +96,29 @@ export function cordovaRunCommand(args: string[], cordovaRootPath: string): Q.Pr
export function cordovaStartCommand(args: string[], cordovaRootPath: string): child_process.ChildProcess {
const command = CordovaProjectHelper.getCliCommand(cordovaRootPath);
const isIonic = CordovaProjectHelper.isIonicProject(cordovaRootPath);
const isIonicServe: boolean = args.indexOf('serve') >= 0;
const isIonicServe: boolean = args.indexOf("serve") >= 0;
if (isIonic && !isIonicServe) {
const isIonicCliVersionGte3 = CordovaProjectHelper.isIonicCliVersionGte3(cordovaRootPath);
if (isIonicCliVersionGte3) {
args.unshift('cordova');
args.unshift("cordova");
}
}
if (isIonic) {
args.push('--no-interactive');
args.push("--no-interactive");
} else {
args.push('--no-update-notifier');
args.push("--no-update-notifier");
}
return child_process.spawn(command, args, { cwd: cordovaRootPath });
}
export function killTree(processId: number): void {
const cmd = process.platform === 'win32' ?
const cmd = process.platform === "win32" ?
`taskkill.exe /F /T /PID` :
path.join(__dirname, '../../../scripts/terminateProcess.sh')
path.join(__dirname, "../../../scripts/terminateProcess.sh");
try {
child_process.execSync(`${cmd} ${processId}`);

Просмотреть файл

@ -1,27 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import * as fs from 'fs';
import * as fs from "fs";
import {
CompletionItem, CompletionItemKind, CompletionItemProvider,
DocumentSelector, CancellationToken, TextDocument, SnippetString, Position
} from 'vscode';
DocumentSelector, SnippetString
} from "vscode";
// Types to outline TextMate snippets format, used in this extension's snippet files
type TMSnippet = { prefix: string, body: string[], description: string };
type TMSnippets = { [name: string]: TMSnippet };
export class IonicCompletionProvider implements CompletionItemProvider {
public static HTML_DOCUMENT_SELECTOR: DocumentSelector = 'html';
public static JS_DOCUMENT_SELECTOR: DocumentSelector = 'javascript';
public static HTML_DOCUMENT_SELECTOR: DocumentSelector = "html";
public static JS_DOCUMENT_SELECTOR: DocumentSelector = "javascript";
private snippetCompletions: CompletionItem[];
constructor(private completionsSource: string) { }
public provideCompletionItems(document: TextDocument,
position: Position,
token: CancellationToken): CompletionItem[] {
public provideCompletionItems(): CompletionItem[] {
if (this.snippetCompletions) {
return this.snippetCompletions;
@ -30,7 +28,7 @@ export class IonicCompletionProvider implements CompletionItemProvider {
this.snippetCompletions = [];
try {
let rawSnippets: TMSnippets = JSON.parse(fs.readFileSync(this.completionsSource, 'utf8'));
let rawSnippets: TMSnippets = JSON.parse(fs.readFileSync(this.completionsSource, "utf8"));
this.snippetCompletions = Object.keys(rawSnippets)
.map(name => makeCompletionItem(rawSnippets[name]));
@ -47,7 +45,7 @@ function makeCompletionItem(rawSnippet: TMSnippet): CompletionItem {
const item = new CompletionItem(rawSnippet.prefix);
item.documentation = rawSnippet.description;
item.kind = CompletionItemKind.Snippet;
item.insertText = new SnippetString(rawSnippet.body.join('\n'));
item.insertText = new SnippetString(rawSnippet.body.join("\n"));
return item;
}

Просмотреть файл

@ -4,7 +4,7 @@
import * as net from "net";
import * as Q from "q";
import {PluginSimulator} from "./simulate";
import {SimulationInfo} from '../common/simulationInfo';
import {SimulationInfo} from "../common/simulationInfo";
import {SimulateOptions} from "cordova-simulate";
import * as vscode from "vscode";
@ -20,8 +20,8 @@ import {Telemetry} from "../utils/telemetry";
import { CordovaCommandHelper } from "../utils/cordovaCommandHelper";
export class ExtensionServer implements vscode.Disposable {
private serverInstance: net.Server = null;
public pluginSimulator: PluginSimulator;
private serverInstance: net.Server = null;
private messageHandlerDictionary: { [id: number]: ((...argArray: any[]) => Q.Promise<any>) } = {};
private pipePath: string;
@ -158,7 +158,7 @@ export class ExtensionServer implements vscode.Disposable {
};
socket.on("data", dataCallback);
};
}
/**
* Recovers the server in case the named socket we use already exists, but no other instance of VSCode is active.

Просмотреть файл

@ -2,17 +2,70 @@
// Licensed under the MIT license. See LICENSE file in the project root for details.
import {Hash} from "../utils/hash";
import * as http from "http";
import * as Q from "q";
import * as cordovaServer from "cordova-serve";
import * as path from "path";
import {Simulator, SimulateOptions, launchBrowser} from "cordova-simulate";
import {CordovaSimulateTelemetry} from "../utils/cordovaSimulateTelemetry";
import {IProjectType, CordovaProjectHelper} from '../utils/cordovaProjectHelper';
import {SimulationInfo} from '../common/simulationInfo';
import {IProjectType, CordovaProjectHelper} from "../utils/cordovaProjectHelper";
import {SimulationInfo} from "../common/simulationInfo";
import * as vscode from "vscode";
import { CordovaCommandHelper } from "../utils/cordovaCommandHelper";
/**
* Content provider hosting the simulation UI inside a document.
*/
class SimHostContentProvider implements vscode.TextDocumentContentProvider {
private simHostUrl: string;
private simulateUri: vscode.Uri;
private changeEmitter = new vscode.EventEmitter<vscode.Uri>();
constructor(simHostUrl: string, simulateUri: vscode.Uri) {
this.simHostUrl = simHostUrl;
this.simulateUri = simulateUri;
}
get onDidChange() {
return this.changeEmitter.event;
}
public fireChange() {
this.changeEmitter.fire(this.simulateUri);
}
public provideTextDocumentContent(): string {
// always return different html so that the tab is properly reloaded and events are fired
return `<!DOCTYPE html>
<html>
<head>
<style>
html, body {
height: 100%;
margin: 0;
overflow: hidden;
}
.intrinsic-container iframe {
position: absolute;
top:0;
left: 0;
border: 0;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div style="display: none">
Always be changing ${Math.random()}
</div>
<div class="intrinsic-container">
<iframe src="${this.simHostUrl}" ></iframe>
</div>
</body>
</html>`;
}
}
/**
* Plugin simulation entry point.
*/
@ -47,8 +100,8 @@ export class PluginSimulator implements vscode.Disposable {
const uri = vscode.Uri.file(fsPath);
const workspaceFolder = <vscode.WorkspaceFolder>vscode.workspace.getWorkspaceFolder(uri);
const simulateProtocol = 'cordova-simulate-' + Hash.hashCode(workspaceFolder.uri.fsPath);
const simulateUri = vscode.Uri.parse(simulateProtocol + '://authority/cordova-simulate');
const simulateProtocol = "cordova-simulate-" + Hash.hashCode(workspaceFolder.uri.fsPath);
const simulateUri = vscode.Uri.parse(simulateProtocol + "://authority/cordova-simulate");
let provider = new SimHostContentProvider(this.simulator.simHostUrl(), simulateUri);
this.registration = vscode.workspace.registerTextDocumentContentProvider(simulateProtocol, provider);
@ -61,7 +114,7 @@ export class PluginSimulator implements vscode.Disposable {
const workspaceFolder = <vscode.WorkspaceFolder>vscode.workspace.getWorkspaceFolder(uri);
simulateOptions.dir = workspaceFolder.uri.fsPath;
if (!simulateOptions.simulationpath) {
simulateOptions.simulationpath = path.join(workspaceFolder.uri.fsPath, '.vscode', 'simulate');;
simulateOptions.simulationpath = path.join(workspaceFolder.uri.fsPath, ".vscode", "simulate");
}
return Q({}).then(() => {
@ -102,17 +155,13 @@ export class PluginSimulator implements vscode.Disposable {
urlRoot: this.simulator.urlRoot(),
};
if (projectType.ionic2 && platform && platform !== "browser") {
this.simulationInfo.appHostUrl = `${this.simulationInfo.appHostUrl}?ionicplatform=${simulateOptions.platform}`
this.simulationInfo.appHostUrl = `${this.simulationInfo.appHostUrl}?ionicplatform=${simulateOptions.platform}`;
}
return this.simulationInfo;
});
});
}
private isServerRunning(): boolean {
return this.simulator && this.simulator.isRunning();
}
public dispose(): void {
if (this.registration) {
this.registration.dispose();
@ -120,63 +169,12 @@ export class PluginSimulator implements vscode.Disposable {
}
if (this.simulator) {
this.simulator.stopSimulation().done(()=>{}, () => {});
this.simulator.stopSimulation().done(() => {}, () => {});
this.simulator = null;
}
}
private isServerRunning(): boolean {
return this.simulator && this.simulator.isRunning();
}
}
/**
* Content provider hosting the simulation UI inside a document.
*/
class SimHostContentProvider implements vscode.TextDocumentContentProvider {
private simHostUrl: string;
private simulateUri: vscode.Uri;
private changeEmitter = new vscode.EventEmitter<vscode.Uri>();
constructor(simHostUrl: string, simulateUri: vscode.Uri) {
this.simHostUrl = simHostUrl;
this.simulateUri = simulateUri;
}
get onDidChange() {
return this.changeEmitter.event;
}
public fireChange() {
this.changeEmitter.fire(this.simulateUri);
}
public provideTextDocumentContent(uri: vscode.Uri): string {
// always return different html so that the tab is properly reloaded and events are fired
return `<!DOCTYPE html>
<html>
<head>
<style>
html, body {
height: 100%;
margin: 0;
overflow: hidden;
}
.intrinsic-container iframe {
position: absolute;
top:0;
left: 0;
border: 0;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div style="display: none">
Always be changing ${Math.random()}
</div>
<div class="intrinsic-container">
<iframe src="${this.simHostUrl}" ></iframe>
</div>
</body>
</html>`;
}
}

Просмотреть файл

@ -1,23 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import * as child_process from 'child_process';
import * as Q from 'q';
import * as os from 'os';
import * as util from 'util';
import { window, WorkspaceConfiguration, workspace, Uri } from 'vscode';
import * as child_process from "child_process";
import * as Q from "q";
import * as os from "os";
import * as util from "util";
import { window, WorkspaceConfiguration, workspace, Uri } from "vscode";
import { TelemetryHelper } from './telemetryHelper';
import { OutputChannelLogger } from './outputChannelLogger';
import { CordovaProjectHelper } from './cordovaProjectHelper';
import { TelemetryHelper } from "./telemetryHelper";
import { OutputChannelLogger } from "./outputChannelLogger";
import { CordovaProjectHelper } from "./cordovaProjectHelper";
export class CordovaCommandHelper {
private static CORDOVA_CMD_NAME: string = os.platform() === 'win32' ? 'cordova.cmd' : 'cordova';
private static IONIC_CMD_NAME: string = os.platform() === 'win32' ? 'ionic.cmd' : 'ionic';
private static CORDOVA_TELEMETRY_EVENT_NAME: string = 'cordovaCommand';
private static IONIC_TELEMETRY_EVENT_NAME: string = 'ionicCommand';
private static CORDOVA_DISPLAY_NAME: string = 'Cordova';
private static IONIC_DISPLAY_NAME: string = 'Ionic';
private static CORDOVA_CMD_NAME: string = os.platform() === "win32" ? "cordova.cmd" : "cordova";
private static IONIC_CMD_NAME: string = os.platform() === "win32" ? "ionic.cmd" : "ionic";
private static CORDOVA_TELEMETRY_EVENT_NAME: string = "cordovaCommand";
private static IONIC_TELEMETRY_EVENT_NAME: string = "ionicCommand";
private static CORDOVA_DISPLAY_NAME: string = "Cordova";
private static IONIC_DISPLAY_NAME: string = "Ionic";
public static executeCordovaCommand(projectRoot: string, command: string, useIonic: boolean = false) {
let telemetryEventName: string = CordovaCommandHelper.CORDOVA_TELEMETRY_EVENT_NAME;
@ -33,7 +33,7 @@ export class CordovaCommandHelper {
return CordovaCommandHelper.selectPlatform(projectRoot, command)
.then((platform) => {
TelemetryHelper.generate(telemetryEventName, (generator) => {
generator.add('command', command, false);
generator.add("command", command, false);
let logger = OutputChannelLogger.getMainChannel();
let commandToExecute = `${cliCommandName} ${command}`;
@ -43,36 +43,36 @@ export class CordovaCommandHelper {
const runArgs = CordovaCommandHelper.getRunArguments(projectRoot);
if (runArgs.length) {
commandToExecute += ` ${runArgs.join(' ')}`;
commandToExecute += ` ${runArgs.join(" ")}`;
}
logger.log(`########### EXECUTING: ${commandToExecute} ###########`);
let process = child_process.exec(commandToExecute, { cwd: projectRoot });
let deferred = Q.defer();
process.on('error', (err: any) => {
process.on("error", (err: any) => {
// ENOENT error will be thrown if no Cordova.cmd or ionic.cmd is found
if (err.code === 'ENOENT') {
window.showErrorMessage(util.format('%s not found, please run "npm install –g %s" to install %s globally', cliDisplayName, cliDisplayName.toLowerCase(), cliDisplayName));
if (err.code === "ENOENT") {
window.showErrorMessage(util.format("%s not found, please run \"npm install –g %s\" to install %s globally", cliDisplayName, cliDisplayName.toLowerCase(), cliDisplayName));
}
deferred.reject(err);
});
process.stderr.on('data', (data: any) => {
process.stderr.on("data", (data: any) => {
logger.append(data);
});
process.stdout.on('data', (data: any) => {
process.stdout.on("data", (data: any) => {
logger.append(data);
});
process.stdout.on('close', (exitCode: number) => {
process.stdout.on("close", () => {
logger.log(`########### FINISHED EXECUTING: ${commandToExecute} ###########`);
deferred.resolve({});
});
return TelemetryHelper.determineProjectTypes(projectRoot)
.then((projectType) => generator.add('projectType', projectType, false))
.then((projectType) => generator.add("projectType", projectType, false))
.then(() => deferred.promise);
});
});
@ -82,16 +82,16 @@ export class CordovaCommandHelper {
* Get command line run arguments from settings.json
*/
public static getRunArguments(fsPath: string): string[] {
return CordovaCommandHelper.getSetting(fsPath, 'runArguments') || [];
return CordovaCommandHelper.getSetting(fsPath, "runArguments") || [];
}
public static getSimulatorInExternalBrowserSetting(fsPath: string): boolean {
return CordovaCommandHelper.getSetting(fsPath, 'simulatorInExternalBrowser') === true;
return CordovaCommandHelper.getSetting(fsPath, "simulatorInExternalBrowser") === true;
}
private static getSetting(fsPath: string, configKey: string): any {
let uri = Uri.file(fsPath);
const workspaceConfiguration: WorkspaceConfiguration = workspace.getConfiguration('cordova', uri);
const workspaceConfiguration: WorkspaceConfiguration = workspace.getConfiguration("cordova", uri);
if (workspaceConfiguration.has(configKey)) {
return workspaceConfiguration.get(configKey);
}
@ -103,17 +103,17 @@ export class CordovaCommandHelper {
return Q({})
.then(() => {
if (['prepare', 'build', 'run'].indexOf(command) > -1) {
if (["prepare", "build", "run"].indexOf(command) > -1) {
if (platforms.length > 1) {
platforms.unshift('all');
platforms.unshift("all");
return window.showQuickPick(platforms)
.then((platform) => {
if (!platform) {
throw new Error('Platform selection was canceled. Please select target platform to continue!');
throw new Error("Platform selection was canceled. Please select target platform to continue!");
}
if (platform === 'all') {
return '';
if (platform === "all") {
return "";
}
return platform;
@ -121,11 +121,11 @@ export class CordovaCommandHelper {
} else if (platforms.length === 1) {
return platforms[0];
} else {
throw new Error('No any platforms installed');
throw new Error("No any platforms installed");
}
}
return '';
return "";
});
}
@ -134,11 +134,11 @@ export class CordovaCommandHelper {
return platforms.filter((platform) => {
switch (platform) {
case 'ios':
case 'osx':
return osPlatform === 'darwin';
case 'windows':
return osPlatform === 'win32';
case "ios":
case "osx":
return osPlatform === "darwin";
case "windows":
return osPlatform === "win32";
default:
return true;
}

Просмотреть файл

@ -1,12 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import * as child_process from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import * as Q from 'q';
import * as child_process from "child_process";
import * as fs from "fs";
import * as path from "path";
import * as Q from "q";
import * as semver from "semver";
import * as os from 'os';
import * as os from "os";
export interface IProjectType {
ionic: boolean;
@ -35,7 +35,7 @@ export class CordovaProjectHelper {
private static PROJECT_PLUGINS_DIR: string = "plugins";
private static IONIC_PROJECT_FILE: string = "ionic.project";
private static CONFIG_IONIC_FILENAME: string = 'ionic.config.json';
private static CONFIG_IONIC_FILENAME: string = "ionic.config.json";
private static IONIC_LIB_DEFAULT_PATH: string = path.join("www", "lib", "ionic");
private static CORE_PLUGIN_LIST: string[] = ["cordova-plugin-battery-status",
@ -93,7 +93,7 @@ export class CordovaProjectHelper {
CordovaProjectHelper.makeDirectoryRecursive(parentPath);
}
fs.mkdirSync(dirPath)
fs.mkdirSync(dirPath);
}
/**
@ -103,7 +103,7 @@ export class CordovaProjectHelper {
if (fs.existsSync(dirPath)) {
if (fs.lstatSync(dirPath).isDirectory()) {
fs.readdirSync(dirPath).forEach(function (file) {
var curPath = path.join(dirPath, file);
let curPath = path.join(dirPath, file);
CordovaProjectHelper.deleteDirectoryRecursive(curPath);
});
@ -112,15 +112,15 @@ export class CordovaProjectHelper {
fs.unlinkSync(dirPath);
}
}
};
}
/**
* Helper function to asynchronously copy a file
*/
public static copyFile(from: string, to: string, encoding?: string): Q.Promise<any> {
var deferred: Q.Deferred<any> = Q.defer();
var destFile: fs.WriteStream = fs.createWriteStream(to, { encoding: encoding });
var srcFile: fs.ReadStream = fs.createReadStream(from, { encoding: encoding });
let deferred: Q.Deferred<any> = Q.defer();
let destFile: fs.WriteStream = fs.createWriteStream(to, { encoding: encoding });
let srcFile: fs.ReadStream = fs.createReadStream(from, { encoding: encoding });
destFile.on("finish", function (): void {
deferred.resolve({});
});
@ -148,7 +148,7 @@ export class CordovaProjectHelper {
}
try {
let fetchJsonContents = fs.readFileSync(fetchJsonPath, 'utf8');
let fetchJsonContents = fs.readFileSync(fetchJsonPath, "utf8");
let fetchJson = JSON.parse(fetchJsonContents);
return Object.keys(fetchJson);
} catch (error) {
@ -170,7 +170,7 @@ export class CordovaProjectHelper {
try {
let platformsDirContents = fs.readdirSync(platformsPath);
return platformsDirContents.filter((platform) => {
return platform.charAt(0) !== '.';
return platform.charAt(0) !== ".";
});
} catch (error) {
console.error(error);
@ -179,19 +179,19 @@ export class CordovaProjectHelper {
}
public static getInstalledPluginDetails(projectRoot: string, pluginId: string): IPluginDetails {
let packageJsonPath: string = path.resolve(projectRoot, CordovaProjectHelper.PROJECT_PLUGINS_DIR, pluginId, 'package.json');
let packageJsonPath: string = path.resolve(projectRoot, CordovaProjectHelper.PROJECT_PLUGINS_DIR, pluginId, "package.json");
if (!CordovaProjectHelper.existsSync(packageJsonPath)) {
return null;
}
try {
let packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
let packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
let details: IPluginDetails = {
PluginId: packageJson.name,
Version: packageJson.version,
PluginType: CordovaProjectHelper.CORE_PLUGIN_LIST.indexOf(pluginId) >= 0 ? 'Core' : 'Npm'
PluginType: CordovaProjectHelper.CORE_PLUGIN_LIST.indexOf(pluginId) >= 0 ? "Core" : "Npm",
};
return details;
@ -213,7 +213,7 @@ export class CordovaProjectHelper {
while (!CordovaProjectHelper.existsSync(path.join(projectRoot, CordovaProjectHelper.CONFIG_XML_FILENAME))
&& !CordovaProjectHelper.existsSync(path.join(projectRoot, CordovaProjectHelper.CONFIG_IONIC_FILENAME))) {
// Navigate up one level until either config.xml is found
parentPath = path.resolve(projectRoot, '..');
parentPath = path.resolve(projectRoot, "..");
if (parentPath !== projectRoot) {
projectRoot = parentPath;
} else {
@ -287,11 +287,11 @@ export class CordovaProjectHelper {
* Helper function to determine whether the project is an Ionic 2 project or no. NOTE: we currently rely on "ionic.config.js" file, which may change as Ionic 2 continues development.
*/
public static isIonic2Project(projectRoot: string): boolean {
const packageJsonPath = path.join(projectRoot, 'package.json');
const packageJsonPath = path.join(projectRoot, "package.json");
if (!fs.existsSync(packageJsonPath)) {
return false;
}
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
const dependencies = packageJson.dependencies || {};
const devDependencies = packageJson.devDependencies || {};
const highestNotSupportedIonic2BetaVersion = "2.0.0-beta.9";
@ -299,7 +299,7 @@ export class CordovaProjectHelper {
const ionicVersion = dependencies["ionic-angular"];
// Assuming for now that latest version is > 3
if (ionicVersion === 'latest' || ionicVersion === 'nightly') {
if (ionicVersion === "latest" || ionicVersion === "nightly") {
return true;
}
@ -320,35 +320,35 @@ export class CordovaProjectHelper {
* manifest and can be considered as a typescript project.
*/
public static isTypescriptProject(projectRoot: string): boolean {
return fs.existsSync(path.resolve(projectRoot, 'tsconfig.json'));
return fs.existsSync(path.resolve(projectRoot, "tsconfig.json"));
}
public static getCliCommand(fsPath: string) {
const cliName = CordovaProjectHelper.isIonicProject(fsPath) ? 'ionic' : 'cordova';
const commandExtension = os.platform() === 'win32' ? '.cmd' : '';
const cliName = CordovaProjectHelper.isIonicProject(fsPath) ? "ionic" : "cordova";
const commandExtension = os.platform() === "win32" ? ".cmd" : "";
const command = cliName + commandExtension;
return command;
}
public static getIonicCliVersion(fsPath: string): string {
const command = CordovaProjectHelper.getCliCommand(fsPath);
const ionicInfo = child_process.spawnSync(command, ['-v', '--quiet'], {
const ionicInfo = child_process.spawnSync(command, ["-v", "--quiet"], {
cwd: fsPath,
env: {
...process.env,
CI: "Hack to disable Ionic autoupdate prompt"
}
CI: "Hack to disable Ionic autoupdate prompt",
},
});
// A warning might appear on second line
return ionicInfo.stdout.toString().split('\n')[0].trim();
return ionicInfo.stdout.toString().split("\n")[0].trim();
}
public static isIonicCliVersionGte3(fsPath: string): boolean {
try {
const ionicVersion = CordovaProjectHelper.getIonicCliVersion(fsPath);
return semver.gte(ionicVersion, '3.0.0');
return semver.gte(ionicVersion, "3.0.0");
} catch (err) {
console.error('Error while detecting Ionic CLI version', err);
console.error("Error while detecting Ionic CLI version", err);
}
return true;
}

Просмотреть файл

@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import {Telemetry} from './telemetry';
import {TelemetryGenerator, TelemetryHelper} from './telemetryHelper';
import {Telemetry} from "./telemetry";
import {TelemetryGenerator, TelemetryHelper} from "./telemetryHelper";
/**
* This class is a telemetry wrapper compatible with cordova-simulate's telemetry. Cordova-simulate expects an object with a sendTelemetry() function, and calls it every time there is a
@ -10,7 +10,7 @@ import {TelemetryGenerator, TelemetryHelper} from './telemetryHelper';
*/
export class CordovaSimulateTelemetry {
public sendTelemetry(eventName: string, props: Telemetry.ITelemetryProperties, piiProps: Telemetry.ITelemetryProperties): void {
let fullEventName = 'cordova-simulate-' + eventName;
let fullEventName = "cordova-simulate-" + eventName;
let generator = new TelemetryGenerator(fullEventName);
let telemetryEvent = new Telemetry.TelemetryEvent(fullEventName);

Просмотреть файл

@ -1,20 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import * as vscode from 'vscode';
import * as vscode from "vscode";
const channels: { [channelName: string]: OutputChannelLogger } = {};
export class OutputChannelLogger {
public static MAIN_CHANNEL_NAME: string = 'Cordova Tools';
public static MAIN_CHANNEL_NAME: string = "Cordova Tools";
private outputChannel: vscode.OutputChannel;
private static purify(message: string): string {
return message
.toString()
.replace(/\u001b/g, '')
.replace(/\[2K\[G/g, '') // Erasing `[2K[G` artifacts from output
.replace(/\[\d+m/g, ''); // Erasing "colors" from output
constructor(public readonly channelName: string, lazy: boolean = false, private preserveFocus: boolean = false) {
if (!lazy) {
this.channel = vscode.window.createOutputChannel(this.channelName);
this.channel.show(this.preserveFocus);
}
}
public static disposeChannel(channelName: string): void {
@ -36,11 +35,12 @@ export class OutputChannelLogger {
return channels[channelName];
}
constructor(public readonly channelName: string, lazy: boolean = false, private preserveFocus: boolean = false) {
if (!lazy) {
this.channel = vscode.window.createOutputChannel(this.channelName);
this.channel.show(this.preserveFocus);
}
private static purify(message: string): string {
return message
.toString()
.replace(/\u001b/g, "")
.replace(/\[2K\[G/g, "") // Erasing `[2K[G` artifacts from output
.replace(/\[\d+m/g, ""); // Erasing "colors" from output
}
public log(message: string): void {

Просмотреть файл

@ -1,17 +1,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import * as os from 'os';
import * as path from 'path';
import * as os from "os";
import * as path from "path";
export function settingsHome(): string {
switch (os.platform()) {
case 'win32':
return path.join(process.env['APPDATA'], 'vscode-cordova');
case 'darwin':
case 'linux':
return path.join(process.env['HOME'], '.vscode-cordova');
case "win32":
return path.join(process.env["APPDATA"], "vscode-cordova");
case "darwin":
case "linux":
return path.join(process.env["HOME"], ".vscode-cordova");
default:
throw new Error('UnexpectedPlatform');
throw new Error("UnexpectedPlatform");
}
}

Просмотреть файл

@ -3,39 +3,234 @@
/// <reference path='../../typings/winreg/winreg.d.ts' />
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as Q from 'q';
import * as readline from 'readline';
import * as winreg from 'winreg';
import * as crypto from "crypto";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import * as Q from "q";
import * as winreg from "winreg";
import {
ExtensionMessage,
ExtensionMessageSender
} from '../common/extensionMessaging';
} from "../common/extensionMessaging";
import { settingsHome } from './settingsHelper'
import { settingsHome } from "./settingsHelper";
/**
* Telemetry module specialized for vscode integration.
*/
export module Telemetry {
export var appName: string;
export var isOptedIn: boolean = false;
export var reporter: ITelemetryReporter;
export var reporterDictionary: { [key: string]: ITelemetryReporter } = {};
export let appName: string;
export let isOptedIn: boolean = false;
export let reporter: ITelemetryReporter;
export let reporterDictionary: { [key: string]: ITelemetryReporter } = {};
export interface ITelemetryProperties {
[propertyName: string]: any;
};
}
export interface ITelemetryReporter {
sendTelemetryEvent(eventName: string, properties?: ITelemetryEventProperties, measures?: ITelemetryEventMeasures);
}
class ExtensionTelemetryReporter implements ITelemetryReporter {
private extensionMessageSender: ExtensionMessageSender;
private extensionId: string;
private extensionVersion: string;
private appInsightsKey: string;
constructor(extensionId: string, extensionVersion: string, key: string, projectRoot: string) {
this.extensionId = extensionId;
this.extensionVersion = extensionVersion;
this.appInsightsKey = key;
this.extensionMessageSender = new ExtensionMessageSender(projectRoot);
}
public sendTelemetryEvent(eventName: string, properties?: ITelemetryEventProperties, measures?: ITelemetryEventMeasures) {
this.extensionMessageSender.sendMessage(ExtensionMessage.SEND_TELEMETRY, [this.extensionId, this.extensionVersion, this.appInsightsKey, eventName, properties, measures])
.catch(function () { })
.done();
}
}
class TelemetryUtils {
public static USERTYPE_INTERNAL: string = "Internal";
public static USERTYPE_EXTERNAL: string = "External";
public static userType: string;
public static sessionId: string;
public static optInCollectedForCurrentSession: boolean;
public static initDeferred: Q.Deferred<any> = Q.defer<any>();
private static userId: string;
private static telemetrySettings: ITelemetrySettings = null;
private static TELEMETRY_SETTINGS_FILENAME: string = "VSCodeTelemetrySettings.json";
private static APPINSIGHTS_INSTRUMENTATIONKEY: string = "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217"; // Matches vscode telemetry key
private static REGISTRY_SQMCLIENT_NODE: string = "\\SOFTWARE\\Microsoft\\SQMClient";
private static REGISTRY_USERID_VALUE: string = "UserId";
private static INTERNAL_DOMAIN_SUFFIX: string = "microsoft.com";
private static INTERNAL_USER_ENV_VAR: string = "TACOINTERNAL";
private static get telemetrySettingsFile(): string {
return path.join(settingsHome(), TelemetryUtils.TELEMETRY_SETTINGS_FILENAME);
}
public static init(appVersion: string, initOptions: ITelemetryInitOptions): Q.Promise<any> {
TelemetryUtils.loadSettings();
if (initOptions.isExtensionProcess) {
let TelemetryReporter = require("vscode-extension-telemetry").default;
Telemetry.reporter = new TelemetryReporter(Telemetry.appName, appVersion, TelemetryUtils.APPINSIGHTS_INSTRUMENTATIONKEY);
} else {
Telemetry.reporter = new ExtensionTelemetryReporter(Telemetry.appName, appVersion, TelemetryUtils.APPINSIGHTS_INSTRUMENTATIONKEY, initOptions.projectRoot);
}
TelemetryUtils.getUserId()
.then(function (userId: string): void {
TelemetryUtils.userId = userId;
TelemetryUtils.userType = TelemetryUtils.getUserType();
Telemetry.isOptedIn = TelemetryUtils.getTelemetryOptInSetting();
TelemetryUtils.saveSettings();
TelemetryUtils.initDeferred.resolve(void 0);
});
return TelemetryUtils.initDeferred.promise;
}
public static addCommonProperties(event: any): void {
if (Telemetry.isOptedIn) {
event.properties["cordova.userId"] = TelemetryUtils.userId;
}
event.properties["cordova.userType"] = TelemetryUtils.userType;
}
public static generateGuid(): string {
let hexValues: string[] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
// c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
let oct: string = "";
let tmp: number;
/* tslint:disable:no-bitwise */
for (let a: number = 0; a < 4; a++) {
tmp = (4294967296 * Math.random()) | 0;
oct += hexValues[tmp & 0xF] + hexValues[tmp >> 4 & 0xF] + hexValues[tmp >> 8 & 0xF] + hexValues[tmp >> 12 & 0xF] + hexValues[tmp >> 16 & 0xF] + hexValues[tmp >> 20 & 0xF] + hexValues[tmp >> 24 & 0xF] + hexValues[tmp >> 28 & 0xF];
}
// 'Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively'
let clockSequenceHi: string = hexValues[8 + (Math.random() * 4) | 0];
return oct.substr(0, 8) + "-" + oct.substr(9, 4) + "-4" + oct.substr(13, 3) + "-" + clockSequenceHi + oct.substr(16, 3) + "-" + oct.substr(19, 12);
/* tslint:enable:no-bitwise */
}
public static getTelemetryOptInSetting(): boolean {
if (TelemetryUtils.telemetrySettings.optIn === undefined) {
// Opt-in by default
TelemetryUtils.telemetrySettings.optIn = true;
}
return TelemetryUtils.telemetrySettings.optIn;
}
private static getUserType(): string {
let userType: string = TelemetryUtils.telemetrySettings.userType;
if (userType === undefined) {
if (process.env[TelemetryUtils.INTERNAL_USER_ENV_VAR]) {
userType = TelemetryUtils.USERTYPE_INTERNAL;
} else if (os.platform() === "win32") {
let domain: string = process.env["USERDNSDOMAIN"];
domain = domain ? domain.toLowerCase().substring(domain.length - TelemetryUtils.INTERNAL_DOMAIN_SUFFIX.length) : null;
userType = domain === TelemetryUtils.INTERNAL_DOMAIN_SUFFIX ? TelemetryUtils.USERTYPE_INTERNAL : TelemetryUtils.USERTYPE_EXTERNAL;
} else {
userType = TelemetryUtils.USERTYPE_EXTERNAL;
}
TelemetryUtils.telemetrySettings.userType = userType;
}
return userType;
}
private static getRegistryValue(key: string, value: string, hive: string): Q.Promise<string> {
let deferred: Q.Deferred<string> = Q.defer<string>();
let regKey = new winreg({
hive: hive,
key: key,
});
regKey.get(value, function (err: any, itemValue: winreg.RegistryItem) {
if (err) {
// Fail gracefully by returning null if there was an error.
deferred.resolve(null);
} else {
deferred.resolve(itemValue.value);
}
});
return deferred.promise;
}
/*
* Load settings data from settingsHome/TelemetrySettings.json
*/
private static loadSettings(): ITelemetrySettings {
try {
TelemetryUtils.telemetrySettings = JSON.parse(<any>fs.readFileSync(TelemetryUtils.telemetrySettingsFile));
} catch (e) {
// if file does not exist or fails to parse then assume no settings are saved and start over
TelemetryUtils.telemetrySettings = {};
}
return TelemetryUtils.telemetrySettings;
}
/*
* Save settings data in settingsHome/TelemetrySettings.json
*/
private static saveSettings(): void {
if (!fs.existsSync(settingsHome())) {
fs.mkdirSync(settingsHome());
}
fs.writeFileSync(TelemetryUtils.telemetrySettingsFile, JSON.stringify(TelemetryUtils.telemetrySettings));
}
private static getUniqueId(regValue: string, regHive: string, fallback: () => string): Q.Promise<any> {
let uniqueId: string;
if (os.platform() === "win32") {
return TelemetryUtils.getRegistryValue(TelemetryUtils.REGISTRY_SQMCLIENT_NODE, regValue, regHive)
.then(function (id: string): Q.Promise<string> {
if (id) {
uniqueId = id.replace(/[{}]/g, "");
return Q.resolve(uniqueId);
} else {
return Q.resolve(fallback());
}
});
} else {
return Q.resolve(fallback());
}
}
private static getUserId(): Q.Promise<string> {
let userId: string = TelemetryUtils.telemetrySettings.userId;
if (!userId) {
return TelemetryUtils.getUniqueId(TelemetryUtils.REGISTRY_USERID_VALUE, winreg.HKCU, TelemetryUtils.generateGuid)
.then(function (id: string): Q.Promise<string> {
TelemetryUtils.telemetrySettings.userId = id;
return Q.resolve(id);
});
} else {
TelemetryUtils.telemetrySettings.userId = userId;
return Q.resolve(userId);
}
}
}
/**
* TelemetryEvent represents a basic telemetry data point
*/
export class TelemetryEvent {
private static PII_HASH_KEY: string = '959069c9-9e93-4fa1-bf16-3f8120d7db0c';
private static PII_HASH_KEY: string = "959069c9-9e93-4fa1-bf16-3f8120d7db0c";
public name: string;
public properties: ITelemetryProperties;
private eventId: string;
@ -48,16 +243,16 @@ export module Telemetry {
}
public setPiiProperty(name: string, value: string): void {
var hmac: any = crypto.createHmac('sha256', new Buffer(TelemetryEvent.PII_HASH_KEY, 'utf8'));
var hashedValue: any = hmac.update(value).digest('hex');
let hmac: any = crypto.createHmac("sha256", new Buffer(TelemetryEvent.PII_HASH_KEY, "utf8"));
let hashedValue: any = hmac.update(value).digest("hex");
this.properties[name] = hashedValue;
if (Telemetry.isInternal()) {
this.properties[name + '.nothashed'] = value;
this.properties[name + ".nothashed"] = value;
}
}
};
}
export interface ITelemetryInitOptions {
isExtensionProcess: boolean;
@ -80,11 +275,11 @@ export module Telemetry {
try {
if (Telemetry.reporter) {
var properties: ITelemetryEventProperties = {};
var measures: ITelemetryEventMeasures = {};
let properties: ITelemetryEventProperties = {};
let measures: ITelemetryEventMeasures = {};
Object.keys(event.properties || {}).forEach(function (key: string) {
var propertyValue = event.properties[key];
let propertyValue = event.properties[key];
switch (typeof propertyValue) {
case "string":
@ -130,181 +325,6 @@ export module Telemetry {
userType?: string;
}
class TelemetryUtils {
public static USERTYPE_INTERNAL: string = 'Internal';
public static USERTYPE_EXTERNAL: string = 'External';
public static userType: string;
public static sessionId: string;
public static optInCollectedForCurrentSession: boolean;
public static initDeferred: Q.Deferred<any> = Q.defer<any>();
private static userId: string;
private static telemetrySettings: ITelemetrySettings = null;
private static TELEMETRY_SETTINGS_FILENAME: string = 'VSCodeTelemetrySettings.json';
private static APPINSIGHTS_INSTRUMENTATIONKEY: string = 'AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217'; // Matches vscode telemetry key
private static REGISTRY_SQMCLIENT_NODE: string = '\\SOFTWARE\\Microsoft\\SQMClient';
private static REGISTRY_USERID_VALUE: string = 'UserId';
private static INTERNAL_DOMAIN_SUFFIX: string = 'microsoft.com';
private static INTERNAL_USER_ENV_VAR: string = 'TACOINTERNAL';
private static get telemetrySettingsFile(): string {
return path.join(settingsHome(), TelemetryUtils.TELEMETRY_SETTINGS_FILENAME);
}
public static init(appVersion: string, initOptions: ITelemetryInitOptions): Q.Promise<any> {
TelemetryUtils.loadSettings();
if (initOptions.isExtensionProcess) {
let TelemetryReporter = require('vscode-extension-telemetry').default;
Telemetry.reporter = new TelemetryReporter(Telemetry.appName, appVersion, TelemetryUtils.APPINSIGHTS_INSTRUMENTATIONKEY);
}
else {
Telemetry.reporter = new ExtensionTelemetryReporter(Telemetry.appName, appVersion, TelemetryUtils.APPINSIGHTS_INSTRUMENTATIONKEY, initOptions.projectRoot);
}
TelemetryUtils.getUserId()
.then(function (userId: string): void {
TelemetryUtils.userId = userId;
TelemetryUtils.userType = TelemetryUtils.getUserType();
Telemetry.isOptedIn = TelemetryUtils.getTelemetryOptInSetting();
TelemetryUtils.saveSettings();
TelemetryUtils.initDeferred.resolve(void 0);
});
return TelemetryUtils.initDeferred.promise;
}
public static addCommonProperties(event: any): void {
if (Telemetry.isOptedIn) {
event.properties['cordova.userId'] = TelemetryUtils.userId;
}
event.properties['cordova.userType'] = TelemetryUtils.userType;
}
public static generateGuid(): string {
var hexValues: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
// c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
var oct: string = '';
var tmp: number;
/* tslint:disable:no-bitwise */
for (var a: number = 0; a < 4; a++) {
tmp = (4294967296 * Math.random()) | 0;
oct += hexValues[tmp & 0xF] + hexValues[tmp >> 4 & 0xF] + hexValues[tmp >> 8 & 0xF] + hexValues[tmp >> 12 & 0xF] + hexValues[tmp >> 16 & 0xF] + hexValues[tmp >> 20 & 0xF] + hexValues[tmp >> 24 & 0xF] + hexValues[tmp >> 28 & 0xF];
}
// 'Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively'
var clockSequenceHi: string = hexValues[8 + (Math.random() * 4) | 0];
return oct.substr(0, 8) + '-' + oct.substr(9, 4) + '-4' + oct.substr(13, 3) + '-' + clockSequenceHi + oct.substr(16, 3) + '-' + oct.substr(19, 12);
/* tslint:enable:no-bitwise */
}
public static getTelemetryOptInSetting(): boolean {
if (TelemetryUtils.telemetrySettings.optIn === undefined) {
// Opt-in by default
TelemetryUtils.telemetrySettings.optIn = true;
}
return TelemetryUtils.telemetrySettings.optIn;
}
private static getUserType(): string {
var userType: string = TelemetryUtils.telemetrySettings.userType;
if (userType === undefined) {
if (process.env[TelemetryUtils.INTERNAL_USER_ENV_VAR]) {
userType = TelemetryUtils.USERTYPE_INTERNAL;
} else if (os.platform() === 'win32') {
var domain: string = process.env['USERDNSDOMAIN'];
domain = domain ? domain.toLowerCase().substring(domain.length - TelemetryUtils.INTERNAL_DOMAIN_SUFFIX.length) : null;
userType = domain === TelemetryUtils.INTERNAL_DOMAIN_SUFFIX ? TelemetryUtils.USERTYPE_INTERNAL : TelemetryUtils.USERTYPE_EXTERNAL;
} else {
userType = TelemetryUtils.USERTYPE_EXTERNAL;
}
TelemetryUtils.telemetrySettings.userType = userType;
}
return userType;
}
private static getRegistryValue(key: string, value: string, hive: string): Q.Promise<string> {
var deferred: Q.Deferred<string> = Q.defer<string>();
var regKey = new winreg({
hive: hive,
key: key
});
regKey.get(value, function (err: any, itemValue: winreg.RegistryItem) {
if (err) {
// Fail gracefully by returning null if there was an error.
deferred.resolve(null);
}
else {
deferred.resolve(itemValue.value);
}
});
return deferred.promise;
}
/*
* Load settings data from settingsHome/TelemetrySettings.json
*/
private static loadSettings(): ITelemetrySettings {
try {
TelemetryUtils.telemetrySettings = JSON.parse(<any>fs.readFileSync(TelemetryUtils.telemetrySettingsFile));
} catch (e) {
// if file does not exist or fails to parse then assume no settings are saved and start over
TelemetryUtils.telemetrySettings = {};
}
return TelemetryUtils.telemetrySettings;
}
/*
* Save settings data in settingsHome/TelemetrySettings.json
*/
private static saveSettings(): void {
if (!fs.existsSync(settingsHome())) {
fs.mkdirSync(settingsHome());
}
fs.writeFileSync(TelemetryUtils.telemetrySettingsFile, JSON.stringify(TelemetryUtils.telemetrySettings));
}
private static getUniqueId(regValue: string, regHive: string, fallback: () => string): Q.Promise<any> {
var uniqueId: string;
var deferred: Q.Deferred<string> = Q.defer<string>();
if (os.platform() === 'win32') {
return TelemetryUtils.getRegistryValue(TelemetryUtils.REGISTRY_SQMCLIENT_NODE, regValue, regHive)
.then(function (id: string): Q.Promise<string> {
if (id) {
uniqueId = id.replace(/[{}]/g, '');
return Q.resolve(uniqueId);
} else {
return Q.resolve(fallback());
}
});
} else {
return Q.resolve(fallback());
}
}
private static getUserId(): Q.Promise<string> {
var userId: string = TelemetryUtils.telemetrySettings.userId;
if (!userId) {
return TelemetryUtils.getUniqueId(TelemetryUtils.REGISTRY_USERID_VALUE, winreg.HKCU, TelemetryUtils.generateGuid)
.then(function (id: string): Q.Promise<string> {
TelemetryUtils.telemetrySettings.userId = id;
return Q.resolve(id);
});
} else {
TelemetryUtils.telemetrySettings.userId = userId;
return Q.resolve(userId);
}
}
};
export interface ITelemetryEventProperties {
[key: string]: string;
}
@ -313,39 +333,15 @@ export module Telemetry {
[key: string]: number;
}
export interface ITelemetryReporter {
sendTelemetryEvent(eventName: string, properties?: ITelemetryEventProperties, measures?: ITelemetryEventMeasures);
}
class ExtensionTelemetryReporter implements ITelemetryReporter {
private extensionMessageSender: ExtensionMessageSender;
private extensionId: string;
private extensionVersion: string;
private appInsightsKey: string;
constructor(extensionId: string, extensionVersion: string, key: string, projectRoot: string) {
this.extensionId = extensionId;
this.extensionVersion = extensionVersion;
this.appInsightsKey = key;
this.extensionMessageSender = new ExtensionMessageSender(projectRoot);
}
sendTelemetryEvent(eventName: string, properties?: ITelemetryEventProperties, measures?: ITelemetryEventMeasures) {
this.extensionMessageSender.sendMessage(ExtensionMessage.SEND_TELEMETRY, [this.extensionId, this.extensionVersion, this.appInsightsKey, eventName, properties, measures])
.catch(function () { })
.done();
}
}
export function sendExtensionTelemetry(extensionId: string, extensionVersion: string, appInsightsKey: string, eventName: string, properties: ITelemetryEventProperties, measures: ITelemetryEventMeasures): void {
let reporter: ITelemetryReporter = Telemetry.reporterDictionary[extensionId];
if (!reporter) {
let TelemetryReporter = require('vscode-extension-telemetry').default;
let TelemetryReporter = require("vscode-extension-telemetry").default;
Telemetry.reporterDictionary[extensionId] = new TelemetryReporter(extensionId, extensionVersion, appInsightsKey);
reporter = Telemetry.reporterDictionary[extensionId];
}
reporter.sendTelemetryEvent(eventName, properties, measures);
}
};
}

Просмотреть файл

@ -1,12 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import {CordovaProjectHelper, IPluginDetails} from './cordovaProjectHelper';
import * as fs from 'fs';
import * as path from 'path';
import * as Q from 'q';
import {Telemetry} from './telemetry';
import {IProjectType} from './cordovaProjectHelper';
/* tslint:disable:no-use-before-declare */
import {CordovaProjectHelper, IPluginDetails} from "./cordovaProjectHelper";
import * as fs from "fs";
import * as path from "path";
import * as Q from "q";
import {Telemetry} from "./telemetry";
import {IProjectType} from "./cordovaProjectHelper";
export interface ITelemetryPropertyInfo {
value: any;
@ -33,7 +34,7 @@ export abstract class TelemetryGeneratorBase {
protected telemetryProperties: ICommandTelemetryProperties = {};
private componentName: string;
private currentStepStartTime: [number, number];
private currentStep: string = 'initialStep';
private currentStep: string = "initialStep";
private errorIndex: number = -1; // In case we have more than one error (We start at -1 because we increment it before using it)
constructor(componentName: string) {
@ -41,8 +42,6 @@ export abstract class TelemetryGeneratorBase {
this.currentStepStartTime = process.hrtime();
}
protected abstract sendTelemetryEvent(telemetryEvent: Telemetry.TelemetryEvent): void;
public add(baseName: string, value: any, isPii: boolean): TelemetryGeneratorBase {
return this.addWithPiiEvaluator(baseName, value, () => isPii);
}
@ -55,7 +54,7 @@ export abstract class TelemetryGeneratorBase {
try {
if (Array.isArray(value)) {
this.addArray(baseName, <any[]>value, piiEvaluator);
} else if (!!value && (typeof value === 'object' || typeof value === 'function')) {
} else if (!!value && (typeof value === "object" || typeof value === "function")) {
this.addHash(baseName, <IDictionary<any>>value, piiEvaluator);
} else {
this.addString(baseName, String(value), piiEvaluator);
@ -63,24 +62,24 @@ export abstract class TelemetryGeneratorBase {
} catch (error) {
// We don't want to crash the functionality if the telemetry fails.
// This error message will be a javascript error message, so it's not pii
this.addString('telemetryGenerationError.' + baseName, String(error), () => false);
this.addString("telemetryGenerationError." + baseName, String(error), () => false);
}
return this;
}
public addError(error: Error): TelemetryGeneratorBase {
this.add('error.message' + ++this.errorIndex, error.message, /*isPii*/ true);
var errorWithErrorCode: IHasErrorCode = <IHasErrorCode><Object>error;
this.add("error.message" + ++this.errorIndex, error.message, /*isPii*/ true);
let errorWithErrorCode: IHasErrorCode = <IHasErrorCode><Object>error;
if (errorWithErrorCode.errorCode) {
this.add('error.code' + this.errorIndex, errorWithErrorCode.errorCode, /*isPii*/ false);
this.add("error.code" + this.errorIndex, errorWithErrorCode.errorCode, /*isPii*/ false);
}
return this;
}
public time<T>(name: string, codeToMeasure: { (): Thenable<T> }): Q.Promise<T> {
var startTime: [number, number] = process.hrtime();
let startTime: [number, number] = process.hrtime();
return Q(codeToMeasure()).finally(() => this.finishTime(name, startTime)).fail((reason: any): Q.Promise<T> => {
this.addError(reason);
throw reason;
@ -101,7 +100,7 @@ export abstract class TelemetryGeneratorBase {
public send(): void {
if (this.currentStep) {
this.add('lastStepExecuted', this.currentStep, /*isPii*/ false);
this.add("lastStepExecuted", this.currentStep, /*isPii*/ false);
}
this.step(null); // Send the last step
@ -111,22 +110,24 @@ export abstract class TelemetryGeneratorBase {
return this.telemetryProperties;
}
protected abstract sendTelemetryEvent(telemetryEvent: Telemetry.TelemetryEvent): void;
private sendCurrentStep(): void {
this.add('step', this.currentStep, /*isPii*/ false);
var telemetryEvent: Telemetry.TelemetryEvent = new Telemetry.TelemetryEvent(this.componentName);
this.add("step", this.currentStep, /*isPii*/ false);
let telemetryEvent: Telemetry.TelemetryEvent = new Telemetry.TelemetryEvent(this.componentName);
TelemetryHelper.addTelemetryEventProperties(telemetryEvent, this.telemetryProperties);
this.sendTelemetryEvent(telemetryEvent);
}
private addArray(baseName: string, array: any[], piiEvaluator: { (value: string, name: string): boolean }): void {
// Object is an array, we add each element as baseNameNNN
var elementIndex: number = 1; // We send telemetry properties in a one-based index
let elementIndex: number = 1; // We send telemetry properties in a one-based index
array.forEach((element: any) => this.addWithPiiEvaluator(baseName + elementIndex++, element, piiEvaluator));
}
private addHash(baseName: string, hash: IDictionary<any>, piiEvaluator: { (value: string, name: string): boolean }): void {
// Object is a hash, we add each element as baseName.KEY
Object.keys(hash).forEach((key: string) => this.addWithPiiEvaluator(baseName + '.' + key, hash[key], piiEvaluator));
Object.keys(hash).forEach((key: string) => this.addWithPiiEvaluator(baseName + "." + key, hash[key], piiEvaluator));
}
private addString(name: string, value: string, piiEvaluator: { (value: string, name: string): boolean }): void {
@ -134,13 +135,13 @@ export abstract class TelemetryGeneratorBase {
}
private combine(...components: string[]): string {
var nonNullComponents: string[] = components.filter((component: string) => component !== null);
return nonNullComponents.join('.');
let nonNullComponents: string[] = components.filter((component: string) => component !== null);
return nonNullComponents.join(".");
}
private finishTime(name: string, startTime: [number, number]): void {
var endTime: [number, number] = process.hrtime(startTime);
this.add(this.combine(name, 'time'), String(endTime[0] * 1000 + endTime[1] / 1000000), /*isPii*/ false);
let endTime: [number, number] = process.hrtime(startTime);
this.add(this.combine(name, "time"), String(endTime[0] * 1000 + endTime[1] / 1000000), /*isPii*/ false);
}
}
@ -150,15 +151,6 @@ export class TelemetryGenerator extends TelemetryGeneratorBase {
}
}
export interface ISimulateTelemetryProperties {
platform?: string;
target: string;
port: number;
simulatePort?: number;
livereload?: boolean;
forceprepare?: boolean;
}
export class TelemetryHelper {
public static createTelemetryEvent(eventName: string): Telemetry.TelemetryEvent {
return new Telemetry.TelemetryEvent(eventName);
@ -166,17 +158,17 @@ export class TelemetryHelper {
public static determineProjectTypes(projectRoot: string): Q.Promise<IProjectType> {
let promiseExists = (file: string) => {
var deferred = Q.defer<boolean>();
let deferred = Q.defer<boolean>();
fs.exists(file, (exist: boolean) => deferred.resolve(exist));
return deferred.promise;
}
};
let isIonic1 = CordovaProjectHelper.isIonic1Project(projectRoot);
let isIonic2 = CordovaProjectHelper.isIonic2Project(projectRoot);
let meteor = promiseExists(path.join(projectRoot, '.meteor'));
let mobilefirst = promiseExists(path.join(projectRoot, '.project'));
let phonegap = promiseExists(path.join(projectRoot, 'www', 'res', '.pgbomit'));
let cordova = promiseExists(path.join(projectRoot, 'config.xml'));
let meteor = promiseExists(path.join(projectRoot, ".meteor"));
let mobilefirst = promiseExists(path.join(projectRoot, ".project"));
let phonegap = promiseExists(path.join(projectRoot, "www", "res", ".pgbomit"));
let cordova = promiseExists(path.join(projectRoot, "config.xml"));
return Q.all([meteor, mobilefirst, phonegap, cordova])
.spread((isMeteor: boolean, isMobilefirst: boolean, isPhonegap: boolean, isCordova: boolean) => {
return { ionic: isIonic1, ionic2: isIonic2, meteor: isMeteor, mobilefirst: isMobilefirst, phonegap: isPhonegap, cordova: isCordova };
@ -206,18 +198,18 @@ export class TelemetryHelper {
}
public static generate<T>(name: string, codeGeneratingTelemetry: { (telemetry: TelemetryGenerator): Thenable<T> }): Q.Promise<T> {
var generator: TelemetryGenerator = new TelemetryGenerator(name);
let generator: TelemetryGenerator = new TelemetryGenerator(name);
return generator.time(null, () => codeGeneratingTelemetry(generator)).finally(() => generator.send());
}
public static sendPluginsList(projectRoot: string, pluginsList: string[]): void {
// Load list of previously sent plugins = previousPlugins
var pluginFilePath = path.join(projectRoot, ".vscode", "plugins.json");
var pluginFileJson : any;
let pluginFilePath = path.join(projectRoot, ".vscode", "plugins.json");
let pluginFileJson: any;
if (CordovaProjectHelper.existsSync(pluginFilePath)) {
try {
let pluginFileJsonContents = fs.readFileSync(pluginFilePath, 'utf8').toString();
let pluginFileJsonContents = fs.readFileSync(pluginFilePath, "utf8").toString();
pluginFileJson = JSON.parse(pluginFileJsonContents);
} catch (error) {
console.error(error);
@ -225,14 +217,14 @@ export class TelemetryHelper {
}
// Get list of plugins in pluginsList but not in previousPlugins
var pluginsFileList : string[] = new Array<string>();
let pluginsFileList: string[] = new Array<string>();
if (pluginFileJson && pluginFileJson.plugins) {
pluginsFileList = pluginFileJson.plugins;
} else {
pluginFileJson = new Object();
}
var newPlugins : string[] = new Array<string>();
let newPlugins: string[] = new Array<string>();
pluginsList.forEach(plugin => {
if (pluginsFileList.indexOf(plugin) < 0) {
newPlugins.push(plugin);
@ -241,7 +233,7 @@ export class TelemetryHelper {
});
// If none, return
if (newPlugins.length == 0) {
if (newPlugins.length === 0) {
return;
}
@ -250,12 +242,12 @@ export class TelemetryHelper {
newPlugins.map(pluginName => CordovaProjectHelper.getInstalledPluginDetails(projectRoot, pluginName))
.filter(detail => !!detail);
let pluginEvent = new Telemetry.TelemetryEvent('plugins', { plugins: JSON.stringify(pluginDetails) });
let pluginEvent = new Telemetry.TelemetryEvent("plugins", { plugins: JSON.stringify(pluginDetails) });
Telemetry.send(pluginEvent);
// Write out new list of previousPlugins
pluginFileJson.plugins = pluginsFileList;
fs.writeFileSync(pluginFilePath, JSON.stringify(pluginFileJson), 'utf8');
fs.writeFileSync(pluginFilePath, JSON.stringify(pluginFileJson), "utf8");
}
private static setTelemetryEventProperty(event: Telemetry.TelemetryEvent, propertyName: string, propertyValue: string, isPii: boolean): void {
@ -267,8 +259,18 @@ export class TelemetryHelper {
}
private static addMultiValuedTelemetryEventProperty(event: Telemetry.TelemetryEvent, propertyName: string, propertyValue: string[], isPii: boolean): void {
for (var i: number = 0; i < propertyValue.length; i++) {
for (let i: number = 0; i < propertyValue.length; i++) {
TelemetryHelper.setTelemetryEventProperty(event, propertyName + i, propertyValue[i], isPii);
}
}
};
}
export interface ISimulateTelemetryProperties {
platform?: string;
target: string;
port: number;
simulatePort?: number;
livereload?: boolean;
forceprepare?: boolean;
}
/* tslint:enable */

Просмотреть файл

@ -1,27 +1,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import * as path from 'path';
import * as fs from 'fs';
import * as Q from 'q';
import {TelemetryHelper} from './telemetryHelper';
import {CordovaProjectHelper} from './cordovaProjectHelper';
import * as path from "path";
import * as fs from "fs";
import * as Q from "q";
import {TelemetryHelper} from "./telemetryHelper";
import {CordovaProjectHelper} from "./cordovaProjectHelper";
export class TsdHelper {
private static CORDOVA_TYPINGS_FOLDERNAME = "CordovaTypings";
private static CORDOVA_TYPINGS_PATH = path.resolve(__dirname, "..", "..", "..", TsdHelper.CORDOVA_TYPINGS_FOLDERNAME);
private static USER_TYPINGS_FOLDERNAME = "typings";
private static installTypeDefinitionFile(src: string, dest: string): Q.Promise<any> {
// Ensure that the parent folder exits; if not, create the hierarchy of directories
let parentFolder = path.resolve(dest, "..");
if (!CordovaProjectHelper.existsSync(parentFolder)) {
CordovaProjectHelper.makeDirectoryRecursive(parentFolder);
}
return CordovaProjectHelper.copyFile(src, dest);
}
/**
* Helper to install type defintion files for Cordova plugins and Ionic projects.
* {typingsFolderPath} - the parent folder where the type definitions need to be installed
@ -31,8 +21,8 @@ export class TsdHelper {
public static installTypings(typingsFolderPath: string, typeDefsPath: string[], projectRoot?: string): void {
let installedTypeDefs: string[] = [];
TelemetryHelper.generate('addTypings', (generator) => {
generator.add('addedTypeDefinitions', typeDefsPath, false);
TelemetryHelper.generate("addTypings", (generator) => {
generator.add("addedTypeDefinitions", typeDefsPath, false);
return Q.all(typeDefsPath.map((relativePath: string): Q.Promise<any> => {
let src = path.resolve(TsdHelper.CORDOVA_TYPINGS_PATH, relativePath);
let dest = path.resolve(typingsFolderPath, relativePath);
@ -62,14 +52,14 @@ export class TsdHelper {
if (installedTypeDefs.length === 0) return;
let typingsFolder = path.resolve(projectRoot, TsdHelper.USER_TYPINGS_FOLDERNAME);
let indexFile = path.resolve(typingsFolder, 'cordova-typings.d.ts');
let indexFile = path.resolve(typingsFolder, "cordova-typings.d.ts");
// Ensure that the 'typings' folder exits; if not, create it
if (!CordovaProjectHelper.existsSync(typingsFolder)) {
CordovaProjectHelper.makeDirectoryRecursive(typingsFolder);
}
let references = CordovaProjectHelper.existsSync(indexFile) ? fs.readFileSync(indexFile, 'utf8') : '';
let references = CordovaProjectHelper.existsSync(indexFile) ? fs.readFileSync(indexFile, "utf8") : "";
let referencesToAdd = installedTypeDefs
// Do not add references to typedefs that are not exist,
// this rarely happens if typedef file fails to copy
@ -81,7 +71,7 @@ export class TsdHelper {
if (referencesToAdd.length === 0) return;
fs.writeFileSync(indexFile, [references].concat(referencesToAdd).join('\n'), 'utf8');
fs.writeFileSync(indexFile, [references].concat(referencesToAdd).join("\n"), "utf8");
});
}
@ -95,9 +85,9 @@ export class TsdHelper {
});
let references = [];
let indexFile = path.resolve(projectRoot, TsdHelper.USER_TYPINGS_FOLDERNAME, 'cordova-typings.d.ts');
let indexFile = path.resolve(projectRoot, TsdHelper.USER_TYPINGS_FOLDERNAME, "cordova-typings.d.ts");
try {
references = fs.readFileSync(indexFile, 'utf8').split('\n');
references = fs.readFileSync(indexFile, "utf8").split("\n");
} catch (e) {
// We failed to read index file - it might not exist of
// blocked by other process - can't do anything here
@ -111,6 +101,16 @@ export class TsdHelper {
referencesToPersist.length === 0 ?
fs.unlink(indexFile) :
// Write filtered references back to index file
fs.writeFileSync(indexFile, referencesToPersist.join('\n'), 'utf8');
fs.writeFileSync(indexFile, referencesToPersist.join("\n"), "utf8");
}
private static installTypeDefinitionFile(src: string, dest: string): Q.Promise<any> {
// Ensure that the parent folder exits; if not, create the hierarchy of directories
let parentFolder = path.resolve(dest, "..");
if (!CordovaProjectHelper.existsSync(parentFolder)) {
CordovaProjectHelper.makeDirectoryRecursive(parentFolder);
}
return CordovaProjectHelper.copyFile(src, dest);
}
}

Просмотреть файл

@ -19,16 +19,12 @@
],
"jsdoc-format": true,
"label-position": true,
"label-undefined": true,
"max-line-length": [
false,
140
],
"member-access": true,
"member-ordering": [
true,
"variables-before-functions"
],
"member-ordering": [true, {"order": "fields-first"}],
"no-any": false,
"no-arg": true,
"no-bitwise": true,
@ -43,25 +39,22 @@
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-key": true,
"no-duplicate-variable": true,
"no-empty": false,
"no-eval": true,
"no-inferrable-types": false,
"no-internal-module": true,
"no-keyword-named-variables": true,
"no-internal-module": false,
"no-require-imports": false,
"no-shadowed-variable": true,
"no-shadowed-variable": false,
"no-string-literal": false,
"no-switch-case-fall-through": true,
"no-switch-case-fall-through": false,
"no-trailing-whitespace": true,
"no-unreachable": true,
"no-unused-expression": true,
"no-unused-variable": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"no-var-requires": true,
"object-literal-sort-keys": true,
"no-var-requires": false,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
@ -71,7 +64,7 @@
],
"quotemark": [
true,
"single"
"double"
],
"radix": true,
"semicolon": true,
@ -79,7 +72,11 @@
"trailing-comma": [
true,
{
"multiline": "always",
"multiline": {
"objects": "always",
"arrays": "always",
"functions": "never"
},
"singleline": "never"
}
],
@ -107,5 +104,4 @@
"check-type"
]
}
}
}