Skip to content

Add project telemetry #16050

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
12 commits merged into from
May 25, 2017
1 change: 1 addition & 0 deletions Jakefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ var harnessSources = harnessCoreSources.concat([
"initializeTSConfig.ts",
"printer.ts",
"textChanges.ts",
"telemetry.ts",
"transform.ts",
"customTransforms.ts",
].map(function (f) {
Expand Down
67 changes: 54 additions & 13 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -692,8 +692,7 @@ namespace ts {
return typeAcquisition;
}

/* @internal */
export function getOptionNameMap(): OptionNameMap {
function getOptionNameMap(): OptionNameMap {
if (optionNameMapCache) {
return optionNameMapCache;
}
Expand Down Expand Up @@ -746,7 +745,6 @@ namespace ts {
const options: CompilerOptions = {};
const fileNames: string[] = [];
const errors: Diagnostic[] = [];
const { optionNameMap, shortOptionNames } = getOptionNameMap();

parseStrings(commandLine);
return {
Expand All @@ -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));
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
});
}
}
}
12 changes: 12 additions & 0 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion src/harness/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
"./unittests/printer.ts",
"./unittests/transform.ts",
"./unittests/customTransforms.ts",
"./unittests/textChanges.ts"
"./unittests/textChanges.ts",
"./unittests/telemetry.ts"
]
}
Loading