123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- 'use strict';
- const path = require('path');
- const childProcess = require('child_process');
- const osName = require('os-name');
- const Conf = require('conf');
- const chalk = require('chalk');
- const debounce = require('lodash.debounce');
- const inquirer = require('inquirer');
- const uuid = require('uuid');
- const providers = require('./providers');
-
- const DEBOUNCE_MS = 100;
-
- class Insight {
- constructor(options) {
- options = options || {};
- options.pkg = options.pkg || {};
-
- // Deprecated options
- // TODO: Remove these at some point in the future
- if (options.packageName) {
- options.pkg.name = options.packageName;
- }
-
- if (options.packageVersion) {
- options.pkg.version = options.packageVersion;
- }
-
- if (!options.trackingCode || !options.pkg.name) {
- throw new Error('trackingCode and pkg.name required');
- }
-
- this.trackingCode = options.trackingCode;
- this.trackingProvider = options.trackingProvider || 'google';
- this.packageName = options.pkg.name;
- this.packageVersion = options.pkg.version || 'undefined';
- this.os = osName();
- this.nodeVersion = process.version;
- this.appVersion = this.packageVersion;
- this.config = options.config || new Conf({
- configName: `insight-${this.packageName}`,
- defaults: {
- clientId: options.clientId || Math.floor(Date.now() * Math.random())
- }
- });
- this._queue = {};
- this._permissionTimeout = 30;
- this._debouncedSend = debounce(this._send, DEBOUNCE_MS, {leading: true});
- }
-
- get optOut() {
- return this.config.get('optOut');
- }
-
- set optOut(val) {
- this.config.set('optOut', val);
- }
-
- get clientId() {
- return this.config.get('clientId');
- }
-
- set clientId(val) {
- this.config.set('clientId', val);
- }
-
- _save() {
- setImmediate(() => {
- this._debouncedSend();
- });
- }
-
- _send() {
- const pending = Object.keys(this._queue).length;
- if (pending === 0) {
- return;
- }
-
- this._fork(this._getPayload());
- this._queue = {};
- }
-
- _fork(payload) {
- // Extracted to a method so it can be easily mocked
- const cp = childProcess.fork(path.join(__dirname, 'push.js'), {silent: true});
- cp.send(payload);
- cp.unref();
- cp.disconnect();
- }
-
- _getPayload() {
- return {
- queue: Object.assign({}, this._queue),
- packageName: this.packageName,
- packageVersion: this.packageVersion,
- trackingCode: this.trackingCode,
- trackingProvider: this.trackingProvider
- };
- }
-
- _getRequestObj(...args) {
- return providers[this.trackingProvider].apply(this, args);
- }
-
- track(...args) {
- if (this.optOut) {
- return;
- }
-
- const path = '/' + args.map(el => String(el).trim().replace(/ /, '-')).join('/');
-
- // Timestamp isn't unique enough since it can end up with duplicate entries
- this._queue[`${Date.now()} ${uuid.v4()}`] = {
- path,
- type: 'pageview'
- };
- this._save();
- }
-
- trackEvent(options) {
- if (this.optOut) {
- return;
- }
-
- if (this.trackingProvider !== 'google') {
- throw new Error('Event tracking is supported only for Google Analytics');
- }
-
- if (!options || !options.category || !options.action) {
- throw new Error('`category` and `action` required');
- }
-
- // Timestamp isn't unique enough since it can end up with duplicate entries
- this._queue[`${Date.now()} ${uuid.v4()}`] = {
- category: options.category,
- action: options.action,
- label: options.label,
- value: options.value,
- type: 'event'
- };
- this._save();
- }
-
- askPermission(msg, cb) {
- const defaultMsg = `May ${chalk.cyan(this.packageName)} anonymously report usage statistics to improve the tool over time?`;
-
- cb = cb || (() => {});
-
- if (!process.stdout.isTTY || process.argv.indexOf('--no-insight') !== -1 || process.env.CI) {
- setImmediate(cb, null, false);
- return;
- }
-
- const prompt = inquirer.prompt({
- type: 'confirm',
- name: 'optIn',
- message: msg || defaultMsg,
- default: true
- });
-
- // Add a 30 sec timeout before giving up on getting an answer
- const permissionTimeout = setTimeout(() => {
- // Stop listening for stdin
- prompt.ui.close();
-
- // Automatically opt out
- this.optOut = true;
- cb(null, false);
- }, this._permissionTimeout * 1000);
-
- prompt.then(result => {
- // Clear the permission timeout upon getting an answer
- clearTimeout(permissionTimeout);
-
- this.optOut = !result.optIn;
- cb(null, result.optIn);
- });
- }
- }
-
- module.exports = Insight;
|