mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-30 21:07:27 -05:00
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:
@@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user