@vscode/telemetry-extractor 1.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +29 -0
- package/SECURITY.md +41 -0
- package/documentation/comment-code-annotations.md +197 -0
- package/documentation/typescript-code-annotations.md +187 -0
- package/documentation/using-the-tool.md +106 -0
- package/out/cli-help.js +30 -0
- package/out/cli-help.js.map +1 -0
- package/out/cli-options.js +52 -0
- package/out/cli-options.js.map +1 -0
- package/out/extractor.js +83 -0
- package/out/extractor.js.map +1 -0
- package/out/index.js +10 -0
- package/out/index.js.map +1 -0
- package/out/lib/common-properties.js +32 -0
- package/out/lib/common-properties.js.map +1 -0
- package/out/lib/debug-patch.js +17 -0
- package/out/lib/debug-patch.js.map +1 -0
- package/out/lib/declarations.js +121 -0
- package/out/lib/declarations.js.map +1 -0
- package/out/lib/events.js +55 -0
- package/out/lib/events.js.map +1 -0
- package/out/lib/file-writer.js +77 -0
- package/out/lib/file-writer.js.map +1 -0
- package/out/lib/fragments.js +21 -0
- package/out/lib/fragments.js.map +1 -0
- package/out/lib/keywords.js +11 -0
- package/out/lib/keywords.js.map +1 -0
- package/out/lib/logger.js +10 -0
- package/out/lib/logger.js.map +1 -0
- package/out/lib/object-converter.js +107 -0
- package/out/lib/object-converter.js.map +1 -0
- package/out/lib/operations.js +118 -0
- package/out/lib/operations.js.map +1 -0
- package/out/lib/parser.js +191 -0
- package/out/lib/parser.js.map +1 -0
- package/out/lib/save-declarations.js +94 -0
- package/out/lib/save-declarations.js.map +1 -0
- package/out/lib/source-spec.js +77 -0
- package/out/lib/source-spec.js.map +1 -0
- package/out/lib/telemetry-interfaces.js +3 -0
- package/out/lib/telemetry-interfaces.js.map +1 -0
- package/out/lib/ts-parser.js +219 -0
- package/out/lib/ts-parser.js.map +1 -0
- package/package.json +39 -0
- package/src/cli-help.ts +29 -0
- package/src/cli-options.ts +31 -0
- package/src/extractor.ts +57 -0
- package/src/index.ts +4 -0
- package/src/lib/common-properties.ts +41 -0
- package/src/lib/debug-patch.ts +13 -0
- package/src/lib/declarations.ts +131 -0
- package/src/lib/events.ts +56 -0
- package/src/lib/file-writer.ts +54 -0
- package/src/lib/fragments.ts +24 -0
- package/src/lib/keywords.ts +7 -0
- package/src/lib/logger.ts +5 -0
- package/src/lib/object-converter.ts +85 -0
- package/src/lib/operations.ts +89 -0
- package/src/lib/parser.ts +184 -0
- package/src/lib/save-declarations.ts +71 -0
- package/src/lib/source-spec.ts +67 -0
- package/src/lib/telemetry-interfaces.ts +40 -0
- package/src/lib/ts-parser.ts +206 -0
- package/tsconfig.json +67 -0
- package/vscode-telemetry-extractor.d.ts +72 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.TsParser = void 0;
|
|
23
|
+
// Copyright (c) Microsoft Corporation.
|
|
24
|
+
// Licensed under the MIT license.
|
|
25
|
+
const ts_morph_1 = require("ts-morph");
|
|
26
|
+
const fs = __importStar(require("fs"));
|
|
27
|
+
const cp = __importStar(require("child_process"));
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
const vscode_ripgrep_1 = require("vscode-ripgrep");
|
|
30
|
+
const operations_1 = require("./operations");
|
|
31
|
+
class GDPREvent {
|
|
32
|
+
eventName;
|
|
33
|
+
properties;
|
|
34
|
+
constructor(name) {
|
|
35
|
+
this.eventName = name;
|
|
36
|
+
this.properties = [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
class NodeVisitor {
|
|
40
|
+
pl_node;
|
|
41
|
+
prop_name;
|
|
42
|
+
inline = false;
|
|
43
|
+
original_prop_name;
|
|
44
|
+
applyEndpoints;
|
|
45
|
+
properties = [];
|
|
46
|
+
resolved_property = Object.create(null);
|
|
47
|
+
constructor(callexpress_node, prop_name, applyEndpoints) {
|
|
48
|
+
this.pl_node = callexpress_node;
|
|
49
|
+
this.prop_name = prop_name;
|
|
50
|
+
this.original_prop_name = prop_name;
|
|
51
|
+
this.applyEndpoints = applyEndpoints;
|
|
52
|
+
}
|
|
53
|
+
visitNode(currentNode, previousNode) {
|
|
54
|
+
let type = currentNode.getTypeAtLocation(this.pl_node);
|
|
55
|
+
// If we mark a property as optional then it is nullable, however we want all properties
|
|
56
|
+
// So we want its non nullable type tl;dr this chops off the | undefined
|
|
57
|
+
if (type.isNullable()) {
|
|
58
|
+
type = type.getNonNullableType();
|
|
59
|
+
}
|
|
60
|
+
if (type.isStringLiteral() || type.isBooleanLiteral()) {
|
|
61
|
+
if (previousNode) {
|
|
62
|
+
// This means it is an inline because we had to recurse deeper than the first level to find the properties
|
|
63
|
+
if (this.prop_name !== previousNode.getEscapedName().toLowerCase() && !this.prop_name.includes(`.${previousNode.getEscapedName().toLowerCase()}`)) {
|
|
64
|
+
this.prop_name = `${this.prop_name}.${previousNode.getEscapedName().toLowerCase()}`;
|
|
65
|
+
this.inline = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// If we don't want endpoints skip them
|
|
69
|
+
if (currentNode.getEscapedName().toLowerCase() === "endpoint" && !this.applyEndpoints)
|
|
70
|
+
return;
|
|
71
|
+
// If it's a string we strip the quotes
|
|
72
|
+
if (type.isStringLiteral()) {
|
|
73
|
+
this.resolved_property[currentNode.getEscapedName()] = type.getText().substring(1, type.getText().length - 1);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
this.resolved_property[currentNode.getEscapedName()] = type.getText() === 'true';
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const properties = type.getProperties();
|
|
81
|
+
properties.forEach((prop) => {
|
|
82
|
+
this.visitNode(prop, currentNode);
|
|
83
|
+
});
|
|
84
|
+
// 95% of the time there is only one property in this array but inlines allow
|
|
85
|
+
// for the number of properties found to be unpredictable so we must return an array
|
|
86
|
+
if (this.inline && this.prop_name === this.original_prop_name) {
|
|
87
|
+
// This handles the case where the recursion will cause the inline to be counted one too many times
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const resolved = Object.create(null);
|
|
91
|
+
if (this.applyEndpoints) {
|
|
92
|
+
this.resolved_property['endPoint'] = this.resolved_property['endPoint'] ? this.resolved_property['endPoint'] : 'none';
|
|
93
|
+
}
|
|
94
|
+
resolved[this.prop_name] = this.resolved_property;
|
|
95
|
+
this.properties.push(resolved);
|
|
96
|
+
this.prop_name = this.original_prop_name;
|
|
97
|
+
}
|
|
98
|
+
resolveProperties(currentNode) {
|
|
99
|
+
this.visitNode(currentNode);
|
|
100
|
+
return this.properties;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
class TsParser {
|
|
104
|
+
sourceDir;
|
|
105
|
+
excludedDirs;
|
|
106
|
+
applyEndpoints;
|
|
107
|
+
lowerCaseEvents;
|
|
108
|
+
project;
|
|
109
|
+
constructor(sourceDir, excludedDirs, applyEndpoints, lowerCaseEvents) {
|
|
110
|
+
this.sourceDir = sourceDir;
|
|
111
|
+
this.excludedDirs = excludedDirs;
|
|
112
|
+
this.applyEndpoints = applyEndpoints;
|
|
113
|
+
this.lowerCaseEvents = lowerCaseEvents;
|
|
114
|
+
// We search for a TS config as that allows the language service to handle weird imports
|
|
115
|
+
if (fs.existsSync(path.join(this.sourceDir, 'src/tsconfig.json'))) {
|
|
116
|
+
this.project = new ts_morph_1.Project({
|
|
117
|
+
tsConfigFilePath: path.join(this.sourceDir, 'src/tsconfig.json'),
|
|
118
|
+
skipAddingFilesFromTsConfig: true
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
else if (fs.existsSync(path.join(this.sourceDir, 'tsconfig.json'))) {
|
|
122
|
+
this.project = new ts_morph_1.Project({
|
|
123
|
+
tsConfigFilePath: path.join(this.sourceDir, 'tsconfig.json'),
|
|
124
|
+
skipAddingFilesFromTsConfig: true
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
this.project = new ts_morph_1.Project({});
|
|
129
|
+
}
|
|
130
|
+
const fileGlobs = [];
|
|
131
|
+
fileGlobs.push(`'**/*.ts'`);
|
|
132
|
+
// Excluded added lasts because order determines what takes effect
|
|
133
|
+
this.excludedDirs = (0, operations_1.makeExclusionsRelativeToSource)(this.sourceDir, this.excludedDirs);
|
|
134
|
+
this.excludedDirs.forEach((dir) => {
|
|
135
|
+
fileGlobs.push(`'!${dir}/**'`);
|
|
136
|
+
});
|
|
137
|
+
let rg_glob = '';
|
|
138
|
+
for (const fg of fileGlobs) {
|
|
139
|
+
rg_glob += ` --glob ${fg}`;
|
|
140
|
+
}
|
|
141
|
+
const cmd = `${vscode_ripgrep_1.rgPath} --files-with-matches ${rg_glob} --no-ignore 'publicLog2|publicLogError2' ${this.sourceDir}`;
|
|
142
|
+
try {
|
|
143
|
+
const retrieved_paths = cp.execSync(cmd, { encoding: 'ascii' });
|
|
144
|
+
// Split the paths into an array
|
|
145
|
+
retrieved_paths.split(/(?:\r\n|\r|\n)/g).filter(path => path && path.length > 0).map((f) => {
|
|
146
|
+
this.project.addSourceFileAtPathIfExists(f);
|
|
147
|
+
return f;
|
|
148
|
+
});
|
|
149
|
+
// Empty catch because this fails when there are no typescript annotations which causes weird error messages
|
|
150
|
+
}
|
|
151
|
+
catch { }
|
|
152
|
+
}
|
|
153
|
+
parseFiles() {
|
|
154
|
+
let publicLogUse = [];
|
|
155
|
+
this.project.getSourceFiles().forEach((source) => {
|
|
156
|
+
const descendants = source.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression).filter((c) => c.getExpression().getText().includes('publicLog2'));
|
|
157
|
+
const descendants2 = source.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression).filter((c) => c.getExpression().getText().includes('publicLogError2'));
|
|
158
|
+
publicLogUse = descendants.concat(publicLogUse, descendants2);
|
|
159
|
+
});
|
|
160
|
+
const events = Object.create(null);
|
|
161
|
+
publicLogUse.forEach((pl) => {
|
|
162
|
+
try {
|
|
163
|
+
const typeArgs = pl.getTypeArguments();
|
|
164
|
+
if (typeArgs.length != 2) {
|
|
165
|
+
throw new Error(`Missing generic arguments on public log call ${pl}`);
|
|
166
|
+
}
|
|
167
|
+
if (pl.getArguments()[0].getText() === "eventName") {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
// Create an event from the name of the first argument passed in
|
|
171
|
+
let event_name = pl.getArguments()[0].getType().isStringLiteral() ? pl.getArguments()[0].getType().getText() : '';
|
|
172
|
+
// If we can't resolve the event_name there is no use continuing
|
|
173
|
+
if (event_name === '') {
|
|
174
|
+
console.error('Unable to resolve event name, skipping....');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
event_name = event_name.substring(1, event_name.length - 1);
|
|
179
|
+
}
|
|
180
|
+
event_name = this.lowerCaseEvents ? event_name.toLowerCase() : event_name;
|
|
181
|
+
const created_event = new GDPREvent(event_name);
|
|
182
|
+
// We want the second one because public log is in the form <Event, Classification> and we care about the classification
|
|
183
|
+
const type_properties = typeArgs[1].getType().getProperties();
|
|
184
|
+
type_properties.forEach((prop) => {
|
|
185
|
+
const propName = prop.getEscapedName().toLowerCase();
|
|
186
|
+
const node_visitor = new NodeVisitor(pl, propName, this.applyEndpoints);
|
|
187
|
+
created_event.properties = created_event.properties.concat(node_visitor.resolveProperties(prop));
|
|
188
|
+
});
|
|
189
|
+
// We don't want to overwrite an event if we have already defined it, we just want to add to it
|
|
190
|
+
if (!events[event_name]) {
|
|
191
|
+
events[event_name] = Object.create(null);
|
|
192
|
+
}
|
|
193
|
+
created_event.properties.forEach((prop) => {
|
|
194
|
+
Object.assign(events[event_name], prop);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
if (pl.getArguments()[0].getText() === "eventName") {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
// If the publicLog call isn't generic that means we're just sending an event name with no classifications
|
|
202
|
+
// that are unique to that event (it just has common properties)
|
|
203
|
+
let event_name = pl.getArguments()[0].getType().isStringLiteral() ? pl.getArguments()[0].getType().getText() : '';
|
|
204
|
+
// If we can't resolve the event_name this is most likely because it is not a public log call and therefore we skip it
|
|
205
|
+
if (event_name === '') {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
event_name = event_name.substring(1, event_name.length - 1);
|
|
210
|
+
}
|
|
211
|
+
event_name = this.lowerCaseEvents ? event_name.toLowerCase() : event_name;
|
|
212
|
+
events[event_name] = {};
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
return events;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
exports.TsParser = TsParser;
|
|
219
|
+
//# sourceMappingURL=ts-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ts-parser.js","sourceRoot":"","sources":["../../src/lib/ts-parser.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vscode/telemetry-extractor",
|
|
3
|
+
"version": "1.9.5",
|
|
4
|
+
"description": "Extracts telemetry from VS Code",
|
|
5
|
+
"main": "out/index.js",
|
|
6
|
+
"typings": "vscode-telemetry-extractor.d.ts",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/microsoft/vscode-telemetry-extractor"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "mocha out/tests/mocha/*.js",
|
|
13
|
+
"coverage": "nyc mocha out/tests/mocha/*.js",
|
|
14
|
+
"extract-core": "node ./out/extractor.js --sourceDir src/telemetry-sources/vscode --excludedDir src/telemetry-sources/vscode/extensions --eventPrefix monacoworkbench/ --outputDir . --applyEndpoints",
|
|
15
|
+
"extract-core-verbose": "node ./out/extractor.js --sourceDir src/telemetry-sources/vscode --excludedDir src/telemetry-sources/vscode/extensions --eventPrefix monacoworkbench/ --outputDir . --applyEndpoints --silent --verbose",
|
|
16
|
+
"compile": "tsc",
|
|
17
|
+
"watch": "tsc --watch"
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"vscode-telemetry-extractor": "./out/extractor.js"
|
|
21
|
+
},
|
|
22
|
+
"author": "lramos15",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"command-line-args": "^5.2.0",
|
|
26
|
+
"ts-morph": "^12.2.0",
|
|
27
|
+
"vscode-ripgrep": "^1.12.1"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/command-line-args": "^5.2.0",
|
|
31
|
+
"@types/mocha": "^9.0.0",
|
|
32
|
+
"@types/node": "^15.12.2",
|
|
33
|
+
"mocha": "^9.1.3",
|
|
34
|
+
"nyc": "^15.1.0",
|
|
35
|
+
"source-map-support": "^0.5.20",
|
|
36
|
+
"ts-node": "^10.4.0",
|
|
37
|
+
"typescript": "^4.4.4"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/cli-help.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
import { optionDefinitions } from './cli-options';
|
|
4
|
+
|
|
5
|
+
function buildHelpMessage() {
|
|
6
|
+
console.log('Allows the extraction of telemetry annotation from code. For more details please read: https://github.com/microsoft/vscode-telemetry-extractor/blob/master/README.md\n');
|
|
7
|
+
const optionLengths = optionDefinitions.map((opt) => {
|
|
8
|
+
if (opt.description) {
|
|
9
|
+
return opt.name.length
|
|
10
|
+
}
|
|
11
|
+
return -1;
|
|
12
|
+
});
|
|
13
|
+
const maxLength = Math.max(...optionLengths);
|
|
14
|
+
for (const opt of optionDefinitions) {
|
|
15
|
+
if (opt.description) {
|
|
16
|
+
let outputString = '';
|
|
17
|
+
if (opt.alias) {
|
|
18
|
+
outputString += `-${opt.alias} `;
|
|
19
|
+
}
|
|
20
|
+
// Extra padding if you don't have an alias
|
|
21
|
+
const extraPadding = opt.alias ? 0 : 3;
|
|
22
|
+
outputString += `--${opt.name.padEnd(maxLength + extraPadding)}\t\t\t`;
|
|
23
|
+
outputString += `${opt.description}`;
|
|
24
|
+
console.log(outputString);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
buildHelpMessage();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
import commandLineArgs from 'command-line-args';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
|
|
6
|
+
function resolveDirectories(options: commandLineArgs.CommandLineOptions): commandLineArgs.CommandLineOptions {
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
|
|
9
|
+
if (options.sourceDir) {
|
|
10
|
+
options.sourceDir = (options.sourceDir as string[]).map(d => path.resolve(cwd, d));
|
|
11
|
+
}
|
|
12
|
+
if (options.outputDir) {
|
|
13
|
+
options.outputDir = path.resolve(cwd, options.outputDir);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return options;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const optionDefinitions = [
|
|
20
|
+
{ name: 'sourceDir', alias: 's', description: 'The folder which you want to extract telemetry from (relative to the CWD)', type: String, multiple: true },
|
|
21
|
+
{ name: 'excludedDir', alias: 'x', description: 'A subdirectory which you would like to exclude from the extraction (relative to the CWD)', type: String, multiple: true, defaultValue: [] },
|
|
22
|
+
{ name: 'config', alias: 'c', description: 'A JSON Configuration file containing extraction details', type: String },
|
|
23
|
+
{ name: 'outputDir', alias: 'o', description: 'The directory which you would like the outputted JSON file to be placed in', type: String },
|
|
24
|
+
{ name: 'eventPrefix', alias: 'p', type: String, description: 'The string you wish to prepend to every telemetry event.', defaultValue: '' },
|
|
25
|
+
{ name: 'help', alias: 'h', type: Boolean, description: 'Displays the help dialog which provides more information on how to use the tool', defaultValue: false },
|
|
26
|
+
{ name: 'applyEndpoints', alias: 'e', type: Boolean, defaultValue: false },
|
|
27
|
+
{ name: 'silenceOutput', type: Boolean, description: 'Silences all progress messages.', defaultValue: false },
|
|
28
|
+
{ name: 'verbose', alias: 'v', type: Boolean, defaultValue: false }
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export const options = resolveDirectories(commandLineArgs(optionDefinitions, { partial: true }));
|
package/src/extractor.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT license.
|
|
4
|
+
import { options } from './cli-options';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { cwd } from 'process';
|
|
7
|
+
import { writeToFile, extractAndResolveDeclarations } from './lib/save-declarations';
|
|
8
|
+
import { convertConfigToSourceSpecs, ParserOptions, SourceSpec } from './lib/source-spec';
|
|
9
|
+
import { logMessage } from './lib/logger';
|
|
10
|
+
|
|
11
|
+
if (options.config) {
|
|
12
|
+
const sourceSpecs = convertConfigToSourceSpecs(options.config);
|
|
13
|
+
extractAndResolveDeclarations(sourceSpecs).then((declarations) => {
|
|
14
|
+
if (options.outputDir) {
|
|
15
|
+
writeToFile(options.outputDir, declarations, 'config-resolved', !options.silent);
|
|
16
|
+
} else {
|
|
17
|
+
console.log(JSON.stringify(declarations));
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
} else if (options.help || !options.sourceDir) {
|
|
21
|
+
require('./cli-help');
|
|
22
|
+
} else {
|
|
23
|
+
const parserOptions: ParserOptions = {
|
|
24
|
+
eventPrefix: options.eventPrefix,
|
|
25
|
+
applyEndpoints: options.applyEndpoints,
|
|
26
|
+
patchDebugEvents: false,
|
|
27
|
+
lowerCaseEvents: false,
|
|
28
|
+
silenceOutput: options.silenceOutput,
|
|
29
|
+
verbose: options.verbose
|
|
30
|
+
};
|
|
31
|
+
logMessage('....running.', parserOptions.silenceOutput);
|
|
32
|
+
const sourceSpec: SourceSpec = {
|
|
33
|
+
sourceDirs: resolveDirectories(options.sourceDir),
|
|
34
|
+
excludedDirs: options.excludedDir ? resolveDirectories(options.excludedDir) : [],
|
|
35
|
+
parserOptions: parserOptions
|
|
36
|
+
};
|
|
37
|
+
extractAndResolveDeclarations([sourceSpec]).then((declarations) => {
|
|
38
|
+
if (options.outputDir) {
|
|
39
|
+
writeToFile(options.outputDir, declarations, 'declarations-resolved', !options.silent);
|
|
40
|
+
} else {
|
|
41
|
+
console.log(JSON.stringify(declarations));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Resolves an array of paths
|
|
47
|
+
function resolveDirectories(dirs: string[], workingDir?: string): string[] {
|
|
48
|
+
if (workingDir) {
|
|
49
|
+
if (path.isAbsolute(workingDir)) {
|
|
50
|
+
return dirs.map(s => path.resolve(workingDir, s));
|
|
51
|
+
} else {
|
|
52
|
+
return dirs.map(s => path.resolve(cwd(), workingDir, s));
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
return dirs.map(s => path.resolve(cwd(), s));
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
import { IProperty } from "./telemetry-interfaces";
|
|
4
|
+
|
|
5
|
+
export class CommonProperties {
|
|
6
|
+
public properties: Array<Property>;
|
|
7
|
+
constructor () {
|
|
8
|
+
this.properties = [];
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class Property implements IProperty {
|
|
13
|
+
public name: string;
|
|
14
|
+
public classification: string;
|
|
15
|
+
public purpose: string;
|
|
16
|
+
public endPoint?: string;
|
|
17
|
+
public isMeasurement?: boolean;
|
|
18
|
+
public expiration?: string;
|
|
19
|
+
public owner?: string;
|
|
20
|
+
public comment?: string;
|
|
21
|
+
|
|
22
|
+
constructor (
|
|
23
|
+
name: string,
|
|
24
|
+
classification: string,
|
|
25
|
+
purpose: string,
|
|
26
|
+
expiration?: string,
|
|
27
|
+
owner?: string,
|
|
28
|
+
comment?: string,
|
|
29
|
+
endpoint?: string,
|
|
30
|
+
isMeasurement?: boolean
|
|
31
|
+
) {
|
|
32
|
+
this.name = name;
|
|
33
|
+
this.classification = classification;
|
|
34
|
+
this.purpose = purpose;
|
|
35
|
+
this.endPoint = endpoint;
|
|
36
|
+
this.isMeasurement = isMeasurement;
|
|
37
|
+
this.expiration = expiration;
|
|
38
|
+
this.owner = owner;
|
|
39
|
+
this.comment = comment;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
import { Events, Event } from "./events";
|
|
4
|
+
import { Property } from "./common-properties";
|
|
5
|
+
|
|
6
|
+
export function patchDebugEvents(currentEvents: Events, eventPrefix: string) {
|
|
7
|
+
const debugProtocolErrorResponse = new Event(eventPrefix + 'debugProtocolErrorResponse');
|
|
8
|
+
debugProtocolErrorResponse.properties.push(new Property('error', 'CallstackOrException', 'PerformanceAndHealth'));
|
|
9
|
+
const optin = new Event(eventPrefix + 'optinstatus');
|
|
10
|
+
optin.properties.push(new Property('optin', 'SystemMetaData', 'BusinessInsight', undefined, undefined, undefined, undefined, true));
|
|
11
|
+
currentEvents.dataPoints.push(debugProtocolErrorResponse);
|
|
12
|
+
currentEvents.dataPoints.push(optin);
|
|
13
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
import { Fragments } from "./fragments";
|
|
4
|
+
import { Events, Include, Inline } from "./events";
|
|
5
|
+
import { CommonProperties, Property } from "./common-properties";
|
|
6
|
+
|
|
7
|
+
export interface Declarations {
|
|
8
|
+
fragments: Fragments;
|
|
9
|
+
events: Events;
|
|
10
|
+
commonProperties: CommonProperties;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// We don't output Fragments as they don't really provide much context on their own
|
|
14
|
+
export interface OutputtedDeclarations {
|
|
15
|
+
events: Events;
|
|
16
|
+
commonProperties: CommonProperties;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveIncludes(target: Events | Fragments, fragments: Fragments) {
|
|
20
|
+
const notResolved: Array<String> = [];
|
|
21
|
+
const usedFragments: Array<String> = [];
|
|
22
|
+
for (const item of target.dataPoints) {
|
|
23
|
+
for (let i = 0; i < item.properties.length; i++) {
|
|
24
|
+
const property = item.properties[i];
|
|
25
|
+
if (property instanceof Include) {
|
|
26
|
+
// Removes the current include from the array since we have filled it in
|
|
27
|
+
item.properties = item.properties.filter((val) => {
|
|
28
|
+
return val != property;
|
|
29
|
+
});
|
|
30
|
+
for (let reference of property.includeNames) {
|
|
31
|
+
reference = reference.substring(2, reference.length - 1);
|
|
32
|
+
const fragment = fragments.dataPoints.find((f) => {
|
|
33
|
+
return f.name === reference;
|
|
34
|
+
});
|
|
35
|
+
if (fragment) {
|
|
36
|
+
item.properties = item.properties.concat(fragment.properties);
|
|
37
|
+
usedFragments.push(fragment.name);
|
|
38
|
+
} else {
|
|
39
|
+
notResolved.push(reference);
|
|
40
|
+
}
|
|
41
|
+
// This is an inefficient solution, but since adding and removing from the array
|
|
42
|
+
// messes up the iteration we just restart it until we can get through the whole loop without any includes
|
|
43
|
+
i = 0;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { target, notResolved, usedFragments };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resolveInlines(target: Events | Fragments, fragments: Fragments) {
|
|
52
|
+
const notResolved: Array<String> = [];
|
|
53
|
+
const usedFragments: Array<String> = [];
|
|
54
|
+
for (const item of target.dataPoints) {
|
|
55
|
+
for (let i = 0; i < item.properties.length; i++) {
|
|
56
|
+
const property = item.properties[i];
|
|
57
|
+
if (property instanceof Inline) {
|
|
58
|
+
// Removes the current inline from the array since we are resolving it
|
|
59
|
+
item.properties = item.properties.filter((val) => {
|
|
60
|
+
return val !== property;
|
|
61
|
+
});
|
|
62
|
+
for (let reference of property.inlines) {
|
|
63
|
+
// Gets rid of the ${}
|
|
64
|
+
reference = reference.substring(2, reference.length - 1);
|
|
65
|
+
const fragment = fragments.dataPoints.find((f) => {
|
|
66
|
+
return f.name === reference;
|
|
67
|
+
});
|
|
68
|
+
if (fragment) {
|
|
69
|
+
usedFragments.push(fragment.name);
|
|
70
|
+
fragment.properties.forEach((prop) => {
|
|
71
|
+
if (prop instanceof Property) {
|
|
72
|
+
// We create the new property in the format inlineName.propName keeping the rest the same
|
|
73
|
+
let currentProp = new Property(`${property.inlineName}.${prop.name}`, prop.classification, prop.purpose, prop.expiration, prop.owner, prop.comment);
|
|
74
|
+
if (prop.endPoint) {
|
|
75
|
+
currentProp.endPoint = prop.endPoint;
|
|
76
|
+
}
|
|
77
|
+
if (prop.isMeasurement) {
|
|
78
|
+
currentProp.isMeasurement = prop.isMeasurement;
|
|
79
|
+
}
|
|
80
|
+
item.properties.push(currentProp);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
} else {
|
|
84
|
+
notResolved.push(reference);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { target, notResolved, usedFragments };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function resolveDeclarations(declarations: Declarations, verbose: boolean) {
|
|
94
|
+
let notResolved: Array<String> = [];
|
|
95
|
+
let usedFragments: Array<String> = [];
|
|
96
|
+
if (verbose) {
|
|
97
|
+
const fragmentsResolveInlines = resolveInlines(declarations.fragments, declarations.fragments);
|
|
98
|
+
const eventsResolveInlines = resolveInlines(declarations.events, declarations.fragments);
|
|
99
|
+
const fragmentsResolveIncludes = resolveIncludes(declarations.fragments, declarations.fragments);
|
|
100
|
+
const eventsResolveIncludes = resolveIncludes(declarations.events, declarations.fragments);
|
|
101
|
+
notResolved = notResolved.concat(fragmentsResolveIncludes.notResolved);
|
|
102
|
+
notResolved = notResolved.concat(fragmentsResolveInlines.notResolved);
|
|
103
|
+
notResolved = notResolved.concat(eventsResolveIncludes.notResolved);
|
|
104
|
+
notResolved = notResolved.concat(eventsResolveInlines.notResolved);
|
|
105
|
+
// Remove duplicates
|
|
106
|
+
notResolved = [...new Set(notResolved)];
|
|
107
|
+
usedFragments = usedFragments.concat(fragmentsResolveIncludes.usedFragments);
|
|
108
|
+
usedFragments = usedFragments.concat(fragmentsResolveInlines.usedFragments);
|
|
109
|
+
usedFragments = usedFragments.concat(eventsResolveIncludes.usedFragments);
|
|
110
|
+
usedFragments = usedFragments.concat(eventsResolveInlines.usedFragments);
|
|
111
|
+
// Remove duplicates
|
|
112
|
+
const usedFragmentsSet = new Set(usedFragments);
|
|
113
|
+
const allFragments = declarations.fragments.dataPoints.map(f => f.name);
|
|
114
|
+
// The Symmetric difference of a set
|
|
115
|
+
let unusedFragments = [...new Set(allFragments.filter(x => !usedFragmentsSet.has(x)))];
|
|
116
|
+
notResolved = notResolved.length > 0 ? notResolved : ['None'];
|
|
117
|
+
unusedFragments = unusedFragments.length > 0 ? unusedFragments : ['None'];
|
|
118
|
+
console.log(`Unresolved References: ${notResolved}`);
|
|
119
|
+
console.log(`Unused Fragments: ${unusedFragments}`);
|
|
120
|
+
declarations.fragments = fragmentsResolveInlines.target;
|
|
121
|
+
declarations.events = eventsResolveInlines.target;
|
|
122
|
+
declarations.fragments = fragmentsResolveIncludes.target;
|
|
123
|
+
declarations.events = eventsResolveIncludes.target;
|
|
124
|
+
} else {
|
|
125
|
+
declarations.fragments = resolveInlines(declarations.fragments, declarations.fragments).target;
|
|
126
|
+
declarations.events = resolveInlines(declarations.events, declarations.fragments).target;
|
|
127
|
+
declarations.fragments = resolveIncludes(declarations.fragments, declarations.fragments).target;
|
|
128
|
+
declarations.events = resolveIncludes(declarations.events, declarations.fragments).target;
|
|
129
|
+
}
|
|
130
|
+
return { events: declarations.events, commonProperties: declarations.commonProperties, fragments: declarations.fragments };
|
|
131
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
import { IProperty, IInclude, ITelemetryDataPoint, ITelemetryData, IInline, IWildcard, IWildcardEntry } from './telemetry-interfaces';
|
|
4
|
+
import { Property } from './common-properties';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export class Events implements ITelemetryData {
|
|
8
|
+
public dataPoints: Array<Event>;
|
|
9
|
+
constructor () {
|
|
10
|
+
this.dataPoints = [];
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class Event implements ITelemetryDataPoint {
|
|
15
|
+
public name: string;
|
|
16
|
+
// It gets a little more complicated here as events can have a bunch of different things
|
|
17
|
+
public properties: Array<Property | Include | Inline | Wildcard>;
|
|
18
|
+
constructor (name: string) {
|
|
19
|
+
this.name = name;
|
|
20
|
+
this.properties = [];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class Include implements IInclude {
|
|
25
|
+
public includeNames: Array<string>;
|
|
26
|
+
constructor (includeNames: Array<string>) {
|
|
27
|
+
this.includeNames = includeNames;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class Inline implements IInline {
|
|
32
|
+
public inlineName: string;
|
|
33
|
+
public inlines: Array<string>;
|
|
34
|
+
constructor (name: string, inlines: Array<string>) {
|
|
35
|
+
this.inlineName = name;
|
|
36
|
+
this.inlines = inlines;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class Wildcard implements IWildcard {
|
|
41
|
+
public entries: Array<WildcardEntry>;
|
|
42
|
+
constructor () {
|
|
43
|
+
this.entries = [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class WildcardEntry implements IWildcardEntry {
|
|
48
|
+
public prefix: string;
|
|
49
|
+
public classification: {classification: string, purpose: string};
|
|
50
|
+
public endpoint: string | undefined;
|
|
51
|
+
constructor (prefix: string, classification: {classification: string, purpose: string}, endPoint?: string | undefined) {
|
|
52
|
+
this.prefix = prefix;
|
|
53
|
+
this.classification = classification;
|
|
54
|
+
this.endpoint = endPoint;
|
|
55
|
+
}
|
|
56
|
+
}
|