analytics.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. var Q = require('q');
  2. var mout = require('mout');
  3. var analytics = module.exports;
  4. var insight;
  5. var enableAnalytics = false;
  6. // Insight takes long to load, and often causes problems
  7. // in non-interactive environment, so we load it lazily
  8. //
  9. // Insight is used in two cases:
  10. //
  11. // 1. Read insight configuration (whether track user actions)
  12. // 2. Track user actions (Tracker.track method)
  13. //
  14. // We don't want to instantiate Insight in non-interactive mode
  15. // because it takes time to read config and configstore has concurrency issues:
  16. //
  17. // https://github.com/yeoman/configstore/issues/20
  18. function ensureInsight () {
  19. if (!insight) {
  20. var Insight = require('insight');
  21. var pkg = require('../../package.json');
  22. insight = new Insight({
  23. trackingCode: 'UA-43531210-1',
  24. packageName: pkg.name,
  25. packageVersion: pkg.version
  26. });
  27. }
  28. }
  29. // Initializes the application-wide insight singleton and asks for the
  30. // permission on the CLI during the first run.
  31. //
  32. // This method is called only from bin/bower. Programmatic API skips it.
  33. analytics.setup = function setup (config) {
  34. var deferred = Q.defer();
  35. // No need for asking if analytics is set in bower config
  36. if (config.analytics === undefined) {
  37. ensureInsight();
  38. // For non-interactive call from bin/bower we disable analytics
  39. if (config.interactive) {
  40. if (insight.optOut !== undefined) {
  41. deferred.resolve(!insight.optOut);
  42. } else {
  43. insight.askPermission(null, function(err, optIn) {
  44. // optIn callback param was exactly opposite before 0.4.3
  45. // so we force at least insight@0.4.3 in package.json
  46. deferred.resolve(optIn);
  47. });
  48. }
  49. } else {
  50. // no specified value, no stored value, and can't prompt for one
  51. // most likely CI environment; defaults to false to reduce data noise
  52. deferred.resolve(false);
  53. }
  54. } else {
  55. // use the specified value
  56. deferred.resolve(config.analytics);
  57. }
  58. return deferred.promise.then(function (enabled) {
  59. enableAnalytics = enabled;
  60. return enabled;
  61. });
  62. };
  63. var Tracker = analytics.Tracker = function Tracker(config) {
  64. function analyticsEnabled () {
  65. // Allow for overriding analytics default
  66. if (config && config.analytics !== undefined) {
  67. return config.analytics;
  68. }
  69. // TODO: let bower pass this variable from bin/bower instead closure
  70. return enableAnalytics;
  71. }
  72. if (analyticsEnabled()) {
  73. ensureInsight();
  74. } else {
  75. this.track = function noop () {};
  76. this.trackDecomposedEndpoints = function noop () {};
  77. this.trackPackages = function noop () {};
  78. this.trackNames = function noop () {};
  79. }
  80. };
  81. Tracker.prototype.track = function track() {
  82. insight.track.apply(insight, arguments);
  83. };
  84. Tracker.prototype.trackDecomposedEndpoints = function trackDecomposedEndpoints(command, endpoints) {
  85. endpoints.forEach(function (endpoint) {
  86. this.track(command, endpoint.source, endpoint.target);
  87. }.bind(this));
  88. };
  89. Tracker.prototype.trackPackages = function trackPackages(command, packages) {
  90. mout.object.forOwn(packages, function (package) {
  91. var meta = package.pkgMeta;
  92. this.track(command, meta.name, meta.version);
  93. }.bind(this));
  94. };
  95. Tracker.prototype.trackNames = function trackNames(command, names) {
  96. names.forEach(function (name) {
  97. this.track(command, name);
  98. }.bind(this));
  99. };