1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | const core_1 = require("@angular-devkit/core");
|
11 | const child_process = require("child_process");
|
12 | const debug = require("debug");
|
13 | const fs_1 = require("fs");
|
14 | const inquirer = require("inquirer");
|
15 | const os = require("os");
|
16 | const ua = require("universal-analytics");
|
17 | const uuid_1 = require("uuid");
|
18 | const color_1 = require("../utilities/color");
|
19 | const config_1 = require("../utilities/config");
|
20 | const tty_1 = require("../utilities/tty");
|
21 |
|
22 | const analyticsDebug = debug('ng:analytics');
|
23 | const analyticsLogDebug = debug('ng:analytics:log');
|
24 | const BYTES_PER_GIGABYTES = 1024 * 1024 * 1024;
|
25 | let _defaultAngularCliPropertyCache;
|
26 | exports.AnalyticsProperties = {
|
27 | AngularCliProd: 'UA-8594346-29',
|
28 | AngularCliStaging: 'UA-8594346-32',
|
29 | get AngularCliDefault() {
|
30 | if (_defaultAngularCliPropertyCache) {
|
31 | return _defaultAngularCliPropertyCache;
|
32 | }
|
33 | const v = require('../package.json').version;
|
34 |
|
35 | if (/^\d+\.\d+\.\d+$/.test(v) && v !== '0.0.0') {
|
36 | _defaultAngularCliPropertyCache = exports.AnalyticsProperties.AngularCliProd;
|
37 | }
|
38 | else {
|
39 | _defaultAngularCliPropertyCache = exports.AnalyticsProperties.AngularCliStaging;
|
40 | }
|
41 | return _defaultAngularCliPropertyCache;
|
42 | },
|
43 | };
|
44 |
|
45 |
|
46 |
|
47 | exports.analyticsPackageSafelist = [
|
48 | /^@angular\
|
49 | /^@angular-devkit\
|
50 | /^@ngtools\
|
51 | '@schematics/angular',
|
52 | '@schematics/schematics',
|
53 | '@schematics/update',
|
54 | ];
|
55 | function isPackageNameSafeForAnalytics(name) {
|
56 | return exports.analyticsPackageSafelist.some(pattern => {
|
57 | if (typeof pattern == 'string') {
|
58 | return pattern === name;
|
59 | }
|
60 | else {
|
61 | return pattern.test(name);
|
62 | }
|
63 | });
|
64 | }
|
65 | exports.isPackageNameSafeForAnalytics = isPackageNameSafeForAnalytics;
|
66 |
|
67 |
|
68 |
|
69 |
|
70 | function _getWindowsLanguageCode() {
|
71 | if (!os.platform().startsWith('win')) {
|
72 | return undefined;
|
73 | }
|
74 | try {
|
75 |
|
76 |
|
77 | return child_process
|
78 | .execSync('wmic.exe os get locale')
|
79 | .toString()
|
80 | .trim();
|
81 | }
|
82 | catch (_) { }
|
83 | return undefined;
|
84 | }
|
85 |
|
86 |
|
87 |
|
88 |
|
89 | function _getLanguage() {
|
90 |
|
91 | return (process.env.LANG ||
|
92 | process.env.LC_CTYPE ||
|
93 | process.env.LANGSPEC ||
|
94 | _getWindowsLanguageCode() ||
|
95 | '??');
|
96 | }
|
97 |
|
98 |
|
99 |
|
100 |
|
101 | function _getCpuCount() {
|
102 | const cpus = os.cpus();
|
103 |
|
104 | return cpus.length;
|
105 | }
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 | function _getCpuSpeed() {
|
112 | const cpus = os.cpus();
|
113 | return Math.floor(cpus[0].speed);
|
114 | }
|
115 |
|
116 |
|
117 |
|
118 |
|
119 | function _getRamSize() {
|
120 |
|
121 | return Math.round(os.totalmem() / BYTES_PER_GIGABYTES);
|
122 | }
|
123 |
|
124 |
|
125 |
|
126 |
|
127 | function _getNodeVersion() {
|
128 |
|
129 |
|
130 | const p = process;
|
131 | const name = (typeof p.release == 'object' && typeof p.release.name == 'string' && p.release.name) ||
|
132 | process.argv0;
|
133 | return name + ' ' + process.version;
|
134 | }
|
135 |
|
136 |
|
137 |
|
138 |
|
139 | function _getNumericNodeVersion() {
|
140 | const p = process.version;
|
141 | const m = p.match(/\d+\.\d+/);
|
142 | return (m && m[0] && parseFloat(m[0])) || 0;
|
143 | }
|
144 |
|
145 |
|
146 |
|
147 | const osVersionMap = {
|
148 | darwin: {
|
149 | '1.3.1': '10_0_4',
|
150 | '1.4.1': '10_1_0',
|
151 | '5.1': '10_1_1',
|
152 | '5.2': '10_1_5',
|
153 | '6.0.1': '10_2',
|
154 | '6.8': '10_2_8',
|
155 | '7.0': '10_3_0',
|
156 | '7.9': '10_3_9',
|
157 | '8.0': '10_4_0',
|
158 | '8.11': '10_4_11',
|
159 | '9.0': '10_5_0',
|
160 | '9.8': '10_5_8',
|
161 | '10.0': '10_6_0',
|
162 | '10.8': '10_6_8',
|
163 | },
|
164 | win32: {
|
165 | '6.3.9600': 'Windows 8.1',
|
166 | '6.2.9200': 'Windows 8',
|
167 | '6.1.7601': 'Windows 7 SP1',
|
168 | '6.1.7600': 'Windows 7',
|
169 | '6.0.6002': 'Windows Vista SP2',
|
170 | '6.0.6000': 'Windows Vista',
|
171 | '5.1.2600': 'Windows XP',
|
172 | },
|
173 | };
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 | function _buildUserAgentStringForOsx() {
|
180 | let v = osVersionMap.darwin[os.release()];
|
181 | if (!v) {
|
182 |
|
183 | const x = parseFloat(os.release());
|
184 | if (x > 10) {
|
185 | v = `10_` + (x - 4).toString().replace('.', '_');
|
186 | }
|
187 | }
|
188 | const cpuModel = os.cpus()[0].model.match(/^[a-z]+/i);
|
189 | const cpu = cpuModel ? cpuModel[0] : os.cpus()[0].model;
|
190 | return `(Macintosh; ${cpu} Mac OS X ${v || os.release()})`;
|
191 | }
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 | function _buildUserAgentStringForWindows() {
|
198 | return `(Windows NT ${os.release()})`;
|
199 | }
|
200 |
|
201 |
|
202 |
|
203 |
|
204 |
|
205 | function _buildUserAgentStringForLinux() {
|
206 | return `(X11; Linux i686; ${os.release()}; ${os.cpus()[0].model})`;
|
207 | }
|
208 |
|
209 |
|
210 |
|
211 |
|
212 | function _buildUserAgentString() {
|
213 | switch (os.platform()) {
|
214 | case 'darwin':
|
215 | return _buildUserAgentStringForOsx();
|
216 | case 'win32':
|
217 | return _buildUserAgentStringForWindows();
|
218 | case 'linux':
|
219 | return _buildUserAgentStringForLinux();
|
220 | default:
|
221 | return os.platform() + ' ' + os.release();
|
222 | }
|
223 | }
|
224 |
|
225 |
|
226 |
|
227 | class UniversalAnalytics {
|
228 | |
229 |
|
230 |
|
231 |
|
232 | constructor(trackingId, uid) {
|
233 | this._dirty = false;
|
234 | this._metrics = [];
|
235 | this._dimensions = [];
|
236 | this._ua = ua(trackingId, uid, {
|
237 | enableBatching: true,
|
238 | batchSize: 5,
|
239 | });
|
240 |
|
241 | this._ua.set('ds', 'cli');
|
242 | this._ua.set('ua', _buildUserAgentString());
|
243 | this._ua.set('ul', _getLanguage());
|
244 |
|
245 | this._ua.set('an', require('../package.json').name);
|
246 | this._ua.set('av', require('../package.json').version);
|
247 |
|
248 |
|
249 | this._ua.set('aid', _getNodeVersion());
|
250 |
|
251 | this._dimensions[core_1.analytics.NgCliAnalyticsDimensions.CpuCount] = _getCpuCount();
|
252 | this._dimensions[core_1.analytics.NgCliAnalyticsDimensions.CpuSpeed] = _getCpuSpeed();
|
253 | this._dimensions[core_1.analytics.NgCliAnalyticsDimensions.RamInGigabytes] = _getRamSize();
|
254 | this._dimensions[core_1.analytics.NgCliAnalyticsDimensions.NodeVersion] = _getNumericNodeVersion();
|
255 | }
|
256 | |
257 |
|
258 |
|
259 |
|
260 | _customVariables(options) {
|
261 | const additionals = {};
|
262 | this._dimensions.forEach((v, i) => (additionals['cd' + i] = v));
|
263 | (options.dimensions || []).forEach((v, i) => (additionals['cd' + i] = v));
|
264 | this._metrics.forEach((v, i) => (additionals['cm' + i] = v));
|
265 | (options.metrics || []).forEach((v, i) => (additionals['cm' + i] = v));
|
266 | return additionals;
|
267 | }
|
268 | event(ec, ea, options = {}) {
|
269 | const vars = this._customVariables(options);
|
270 | analyticsLogDebug('event ec=%j, ea=%j, %j', ec, ea, vars);
|
271 | const { label: el, value: ev } = options;
|
272 | this._dirty = true;
|
273 | this._ua.event({ ec, ea, el, ev, ...vars });
|
274 | }
|
275 | screenview(cd, an, options = {}) {
|
276 | const vars = this._customVariables(options);
|
277 | analyticsLogDebug('screenview cd=%j, an=%j, %j', cd, an, vars);
|
278 | const { appVersion: av, appId: aid, appInstallerId: aiid } = options;
|
279 | this._dirty = true;
|
280 | this._ua.screenview({ cd, an, av, aid, aiid, ...vars });
|
281 | }
|
282 | pageview(dp, options = {}) {
|
283 | const vars = this._customVariables(options);
|
284 | analyticsLogDebug('pageview dp=%j, %j', dp, vars);
|
285 | const { hostname: dh, title: dt } = options;
|
286 | this._dirty = true;
|
287 | this._ua.pageview({ dp, dh, dt, ...vars });
|
288 | }
|
289 | timing(utc, utv, utt, options = {}) {
|
290 | const vars = this._customVariables(options);
|
291 | analyticsLogDebug('timing utc=%j, utv=%j, utl=%j, %j', utc, utv, utt, vars);
|
292 | const { label: utl } = options;
|
293 | this._dirty = true;
|
294 | this._ua.timing({ utc, utv, utt, utl, ...vars });
|
295 | }
|
296 | flush() {
|
297 | if (!this._dirty) {
|
298 | return Promise.resolve();
|
299 | }
|
300 | this._dirty = false;
|
301 | return new Promise(resolve => this._ua.send(resolve));
|
302 | }
|
303 | }
|
304 | exports.UniversalAnalytics = UniversalAnalytics;
|
305 |
|
306 |
|
307 |
|
308 |
|
309 |
|
310 | function setAnalyticsConfig(level, value) {
|
311 | analyticsDebug('setting %s level analytics to: %s', level, value);
|
312 | const [config, configPath] = config_1.getWorkspaceRaw(level);
|
313 | if (!config || !configPath) {
|
314 | throw new Error(`Could not find ${level} workspace.`);
|
315 | }
|
316 | const configValue = config.value;
|
317 | const cli = configValue['cli'] || (configValue['cli'] = {});
|
318 | if (!core_1.json.isJsonObject(cli)) {
|
319 | throw new Error(`Invalid config found at ${configPath}. CLI should be an object.`);
|
320 | }
|
321 | if (value === true) {
|
322 | value = uuid_1.v4();
|
323 | }
|
324 | cli['analytics'] = value;
|
325 | const output = JSON.stringify(configValue, null, 2);
|
326 | fs_1.writeFileSync(configPath, output);
|
327 | analyticsDebug('done');
|
328 | }
|
329 | exports.setAnalyticsConfig = setAnalyticsConfig;
|
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 | async function promptGlobalAnalytics(force = false) {
|
336 | analyticsDebug('prompting global analytics.');
|
337 | if (force || tty_1.isTTY()) {
|
338 | const answers = await inquirer.prompt([
|
339 | {
|
340 | type: 'confirm',
|
341 | name: 'analytics',
|
342 | message: core_1.tags.stripIndents `
|
343 | Would you like to share anonymous usage data with the Angular Team at Google under
|
344 | Google’s Privacy Policy at https://policies.google.com/privacy? For more details and
|
345 | how to change this setting, see http://angular.io/analytics.
|
346 | `,
|
347 | default: false,
|
348 | },
|
349 | ]);
|
350 | setAnalyticsConfig('global', answers.analytics);
|
351 | if (answers.analytics) {
|
352 | console.log('');
|
353 | console.log(core_1.tags.stripIndent `
|
354 | Thank you for sharing anonymous usage data. If you change your mind, the following
|
355 | command will disable this feature entirely:
|
356 |
|
357 | ${color_1.colors.yellow('ng analytics off')}
|
358 | `);
|
359 | console.log('');
|
360 |
|
361 | const ua = new UniversalAnalytics(exports.AnalyticsProperties.AngularCliDefault, 'optin');
|
362 | ua.pageview('/telemetry/optin');
|
363 | await ua.flush();
|
364 | }
|
365 | else {
|
366 |
|
367 | const ua = new UniversalAnalytics(exports.AnalyticsProperties.AngularCliDefault, 'optout');
|
368 | ua.pageview('/telemetry/optout');
|
369 | await ua.flush();
|
370 | }
|
371 | return true;
|
372 | }
|
373 | else {
|
374 | analyticsDebug('Either STDOUT or STDIN are not TTY and we skipped the prompt.');
|
375 | }
|
376 | return false;
|
377 | }
|
378 | exports.promptGlobalAnalytics = promptGlobalAnalytics;
|
379 |
|
380 |
|
381 |
|
382 |
|
383 |
|
384 |
|
385 | async function promptProjectAnalytics(force = false) {
|
386 | analyticsDebug('prompting user');
|
387 | const [config, configPath] = config_1.getWorkspaceRaw('local');
|
388 | if (!config || !configPath) {
|
389 | throw new Error(`Could not find a local workspace. Are you in a project?`);
|
390 | }
|
391 | if (force || tty_1.isTTY()) {
|
392 | const answers = await inquirer.prompt([
|
393 | {
|
394 | type: 'confirm',
|
395 | name: 'analytics',
|
396 | message: core_1.tags.stripIndents `
|
397 | Would you like to share anonymous usage data about this project with the Angular Team at
|
398 | Google under Google’s Privacy Policy at https://policies.google.com/privacy? For more
|
399 | details and how to change this setting, see http://angular.io/analytics.
|
400 |
|
401 | `,
|
402 | default: false,
|
403 | },
|
404 | ]);
|
405 | setAnalyticsConfig('local', answers.analytics);
|
406 | if (answers.analytics) {
|
407 | console.log('');
|
408 | console.log(core_1.tags.stripIndent `
|
409 | Thank you for sharing anonymous usage data. Would you change your mind, the following
|
410 | command will disable this feature entirely:
|
411 |
|
412 | ${color_1.colors.yellow('ng analytics project off')}
|
413 | `);
|
414 | console.log('');
|
415 |
|
416 | const ua = new UniversalAnalytics(exports.AnalyticsProperties.AngularCliDefault, 'optin');
|
417 | ua.pageview('/telemetry/project/optin');
|
418 | await ua.flush();
|
419 | }
|
420 | else {
|
421 |
|
422 | const ua = new UniversalAnalytics(exports.AnalyticsProperties.AngularCliDefault, 'optout');
|
423 | ua.pageview('/telemetry/project/optout');
|
424 | await ua.flush();
|
425 | }
|
426 | return true;
|
427 | }
|
428 | return false;
|
429 | }
|
430 | exports.promptProjectAnalytics = promptProjectAnalytics;
|
431 | async function hasGlobalAnalyticsConfiguration() {
|
432 | try {
|
433 | const globalWorkspace = await config_1.getWorkspace('global');
|
434 | const analyticsConfig = globalWorkspace && globalWorkspace.getCli() && globalWorkspace.getCli()['analytics'];
|
435 | if (analyticsConfig !== null && analyticsConfig !== undefined) {
|
436 | return true;
|
437 | }
|
438 | }
|
439 | catch (_a) { }
|
440 | return false;
|
441 | }
|
442 | exports.hasGlobalAnalyticsConfiguration = hasGlobalAnalyticsConfiguration;
|
443 |
|
444 |
|
445 |
|
446 |
|
447 |
|
448 |
|
449 | async function getGlobalAnalytics() {
|
450 | analyticsDebug('getGlobalAnalytics');
|
451 | const propertyId = exports.AnalyticsProperties.AngularCliDefault;
|
452 | if ('NG_CLI_ANALYTICS' in process.env) {
|
453 | if (process.env['NG_CLI_ANALYTICS'] == 'false' || process.env['NG_CLI_ANALYTICS'] == '') {
|
454 | analyticsDebug('NG_CLI_ANALYTICS is false');
|
455 | return undefined;
|
456 | }
|
457 | if (process.env['NG_CLI_ANALYTICS'] === 'ci') {
|
458 | analyticsDebug('Running in CI mode');
|
459 | return new UniversalAnalytics(propertyId, 'ci');
|
460 | }
|
461 | }
|
462 |
|
463 | try {
|
464 | const globalWorkspace = await config_1.getWorkspace('global');
|
465 | const analyticsConfig = globalWorkspace && globalWorkspace.getCli() && globalWorkspace.getCli()['analytics'];
|
466 | analyticsDebug('Client Analytics config found: %j', analyticsConfig);
|
467 | if (analyticsConfig === false) {
|
468 | analyticsDebug('Analytics disabled. Ignoring all analytics.');
|
469 | return undefined;
|
470 | }
|
471 | else if (analyticsConfig === undefined || analyticsConfig === null) {
|
472 | analyticsDebug('Analytics settings not found. Ignoring all analytics.');
|
473 |
|
474 |
|
475 |
|
476 | return undefined;
|
477 | }
|
478 | else {
|
479 | let uid = undefined;
|
480 | if (typeof analyticsConfig == 'string') {
|
481 | uid = analyticsConfig;
|
482 | }
|
483 | else if (typeof analyticsConfig == 'object' && typeof analyticsConfig['uid'] == 'string') {
|
484 | uid = analyticsConfig['uid'];
|
485 | }
|
486 | analyticsDebug('client id: %j', uid);
|
487 | if (uid == undefined) {
|
488 | return undefined;
|
489 | }
|
490 | return new UniversalAnalytics(propertyId, uid);
|
491 | }
|
492 | }
|
493 | catch (err) {
|
494 | analyticsDebug('Error happened during reading of analytics config: %s', err.message);
|
495 | return undefined;
|
496 | }
|
497 | }
|
498 | exports.getGlobalAnalytics = getGlobalAnalytics;
|
499 | async function hasWorkspaceAnalyticsConfiguration() {
|
500 | try {
|
501 | const globalWorkspace = await config_1.getWorkspace('local');
|
502 | const analyticsConfig = globalWorkspace
|
503 | && globalWorkspace.getCli()
|
504 | && globalWorkspace.getCli()['analytics'];
|
505 | if (analyticsConfig !== undefined) {
|
506 | return true;
|
507 | }
|
508 | }
|
509 | catch (_a) { }
|
510 | return false;
|
511 | }
|
512 | exports.hasWorkspaceAnalyticsConfiguration = hasWorkspaceAnalyticsConfiguration;
|
513 |
|
514 |
|
515 |
|
516 |
|
517 |
|
518 |
|
519 | async function getWorkspaceAnalytics() {
|
520 | analyticsDebug('getWorkspaceAnalytics');
|
521 | try {
|
522 | const globalWorkspace = await config_1.getWorkspace('local');
|
523 | const analyticsConfig = globalWorkspace
|
524 | && globalWorkspace.getCli()
|
525 | && globalWorkspace.getCli()['analytics'];
|
526 | analyticsDebug('Workspace Analytics config found: %j', analyticsConfig);
|
527 | if (analyticsConfig === false) {
|
528 | analyticsDebug('Analytics disabled. Ignoring all analytics.');
|
529 | return undefined;
|
530 | }
|
531 | else if (analyticsConfig === undefined || analyticsConfig === null) {
|
532 | analyticsDebug('Analytics settings not found. Ignoring all analytics.');
|
533 | return undefined;
|
534 | }
|
535 | else {
|
536 | let uid = undefined;
|
537 | if (typeof analyticsConfig == 'string') {
|
538 | uid = analyticsConfig;
|
539 | }
|
540 | else if (typeof analyticsConfig == 'object' && typeof analyticsConfig['uid'] == 'string') {
|
541 | uid = analyticsConfig['uid'];
|
542 | }
|
543 | analyticsDebug('client id: %j', uid);
|
544 | if (uid == undefined) {
|
545 | return undefined;
|
546 | }
|
547 | return new UniversalAnalytics(exports.AnalyticsProperties.AngularCliDefault, uid);
|
548 | }
|
549 | }
|
550 | catch (err) {
|
551 | analyticsDebug('Error happened during reading of analytics config: %s', err.message);
|
552 | return undefined;
|
553 | }
|
554 | }
|
555 | exports.getWorkspaceAnalytics = getWorkspaceAnalytics;
|
556 |
|
557 |
|
558 |
|
559 |
|
560 | async function getSharedAnalytics() {
|
561 | analyticsDebug('getSharedAnalytics');
|
562 | const envVarName = 'NG_CLI_ANALYTICS_SHARE';
|
563 | if (envVarName in process.env) {
|
564 | if (process.env[envVarName] == 'false' || process.env[envVarName] == '') {
|
565 | analyticsDebug('NG_CLI_ANALYTICS is false');
|
566 | return undefined;
|
567 | }
|
568 | }
|
569 |
|
570 | try {
|
571 | const globalWorkspace = await config_1.getWorkspace('global');
|
572 | const analyticsConfig = globalWorkspace && globalWorkspace.getCli() && globalWorkspace.getCli()['analyticsSharing'];
|
573 | if (!analyticsConfig || !analyticsConfig.tracking || !analyticsConfig.uuid) {
|
574 | return undefined;
|
575 | }
|
576 | else {
|
577 | analyticsDebug('Analytics sharing info: %j', analyticsConfig);
|
578 | return new UniversalAnalytics(analyticsConfig.tracking, analyticsConfig.uuid);
|
579 | }
|
580 | }
|
581 | catch (err) {
|
582 | analyticsDebug('Error happened during reading of analytics sharing config: %s', err.message);
|
583 | return undefined;
|
584 | }
|
585 | }
|
586 | exports.getSharedAnalytics = getSharedAnalytics;
|