Add project telemetry (#16050)

* Add project telemetry

* Respond to some PR comments

* Wrap event in a TelemetryEvent payload

* Replace paths with empty string instead of removing them entirely

* Add "version" property to payload

* Add telemetry for typeAcquisition settings

* Add "files", "include", "exclude", and "compileOnSave"

* Convert typingsOptions include and exclude to booleanss

* Add "extends", "configFileName", and "projectType"

* configFileName: Use "other" instead of undefined

* Add "languageServiceEnabled" telemetry
This commit is contained in:
Andy
2017-05-25 13:30:27 -07:00
committed by GitHub
parent 7cca4ba536
commit d052bb83ca
11 changed files with 505 additions and 36 deletions

View File

@@ -692,8 +692,7 @@ namespace ts {
return typeAcquisition;
}
/* @internal */
export function getOptionNameMap(): OptionNameMap {
function getOptionNameMap(): OptionNameMap {
if (optionNameMapCache) {
return optionNameMapCache;
}
@@ -746,7 +745,6 @@ namespace ts {
const options: CompilerOptions = {};
const fileNames: string[] = [];
const errors: Diagnostic[] = [];
const { optionNameMap, shortOptionNames } = getOptionNameMap();
parseStrings(commandLine);
return {
@@ -758,21 +756,13 @@ namespace ts {
function parseStrings(args: string[]) {
let i = 0;
while (i < args.length) {
let s = args[i];
const s = args[i];
i++;
if (s.charCodeAt(0) === CharacterCodes.at) {
parseResponseFile(s.slice(1));
}
else if (s.charCodeAt(0) === CharacterCodes.minus) {
s = s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase();
// Try to translate short option names to their full equivalents.
const short = shortOptionNames.get(s);
if (short !== undefined) {
s = short;
}
const opt = optionNameMap.get(s);
const opt = getOptionFromName(s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1), /*allowShort*/ true);
if (opt) {
if (opt.isTSConfigOnly) {
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name));
@@ -860,6 +850,19 @@ namespace ts {
}
}
function getOptionFromName(optionName: string, allowShort = false): CommandLineOption | undefined {
optionName = optionName.toLowerCase();
const { optionNameMap, shortOptionNames } = getOptionNameMap();
// Try to translate short option names to their full equivalents.
if (allowShort) {
const short = shortOptionNames.get(optionName);
if (short !== undefined) {
optionName = short;
}
}
return optionNameMap.get(optionName);
}
/**
* Read tsconfig.json file
* @param fileName The path to the config file
@@ -1705,4 +1708,42 @@ namespace ts {
function caseInsensitiveKeyMapper(key: string) {
return key.toLowerCase();
}
/**
* Produces a cleaned version of compiler options with personally identifiying info (aka, paths) removed.
* Also converts enum values back to strings.
*/
/* @internal */
export function convertCompilerOptionsForTelemetry(opts: ts.CompilerOptions): ts.CompilerOptions {
const out: ts.CompilerOptions = {};
for (const key in opts) if (opts.hasOwnProperty(key)) {
const type = getOptionFromName(key);
if (type !== undefined) { // Ignore unknown options
out[key] = getOptionValueWithEmptyStrings(opts[key], type);
}
}
return out;
}
function getOptionValueWithEmptyStrings(value: any, option: CommandLineOption): {} {
switch (option.type) {
case "object": // "paths". Can't get any useful information from the value since we blank out strings, so just return "".
return "";
case "string": // Could be any arbitrary string -- use empty string instead.
return "";
case "number": // Allow numbers, but be sure to check it's actually a number.
return typeof value === "number" ? value : "";
case "boolean":
return typeof value === "boolean" ? value : "";
case "list":
const elementType = (option as CommandLineOptionOfListType).element;
return ts.isArray(value) ? value.map(v => getOptionValueWithEmptyStrings(v, elementType)) : "";
default:
return ts.forEachEntry(option.type, (optionEnumValue, optionStringValue) => {
if (optionEnumValue === value) {
return optionStringValue;
}
});
}
}
}

View File

@@ -521,6 +521,18 @@ namespace ts {
return result || array;
}
export function mapDefined<T>(array: ReadonlyArray<T>, mapFn: (x: T, i: number) => T | undefined): ReadonlyArray<T> {
const result: T[] = [];
for (let i = 0; i < array.length; i++) {
const item = array[i];
const mapped = mapFn(item, i);
if (mapped !== undefined) {
result.push(mapped);
}
}
return result;
}
/**
* Computes the first matching span of elements and returns a tuple of the first span
* and the remaining elements.