<commit_before>// TODO: This is expensive... minifies and gzips a set of scripts !! at every startup...
//       to provide single /client/api script to browser.
//       Better if this was cached to disk (when in production mode)

// BUG: more than one meshnode in same process and packager does not apply
// post-start changes to script package into all mesh nodes.

// A preliminary work toward asset packaging (minify, gzip, etag, etc.)
// The ideal would be a per component widget assembly line.
// Allows for serving individual scripts development.
// This one packages only the core /client/api script from ./api.js and it's dependancies
// Make it not do all this on every startup.

module.exports = Packager;

var fs = require('fs')
  , zlib = require('zlib')
  , md5 = require('md5')
  , Promise = require('bluebird')
  , minifyJS = require('uglify-js').minify
  , normalize = require('path').normalize
  , dirname = require('path').dirname
  , sep = require('path').sep
  , Happn = require('happn-3')
  , homedir = require('homedir')
  , fsExtra = require('fs-extra')
  , version = require('../../package.json').version
  , cachedDirname = homedir() + sep + '.happner'
  , cachedFilename = cachedDirname + sep + 'api-client-' + version + '.min.js.gz'
  ;

var watchedFiles = {}; // Properly support only one packager per process!

function Packager(mesh) {

  try{

    this.log = mesh.log.createLogger('Packager');
    mesh.tools.packages = this.packages = {};

    if (process.env.NODE_ENV == 'production') {
      this.ensureCacheDirectory();
      if (this.loadCachedScript()) {
        return;
      }
    }

    var script1 = dirname(dirname(require.resolve('bluebird'))) +  sep + 'browser' + sep + 'bluebird.js';
    var script2 = normalize(mesh._mesh.happn.server.services.session.script);
    var script3 = Happn.packager.browserClient();

    var script4 = normalize(__dirname + sep + 'shared' + sep + 'logger.js');
    var script5 = normalize(__dirname + sep + 'shared' + sep + 'promisify.js');
    var script6 = normalize(__dirname + sep + 'shared' + sep + 'mesh-error.js');
    var script7 = normalize(__dirname + sep + 'shared' + sep + 'messenger.js');
    var script8 = normalize(__dirname + sep + 'shared' + sep + 'internals.js');
    var script9 = normalize(__dirname + sep + 'shared' + sep + 'mesh-client.js');

    var script10 = dirname(require.resolve('happner-client')) + sep + 'lib' + sep + 'semver.js';
    var script11 = dirname(require.resolve('happner-client')) + sep + 'lib' + sep + 'builders' + sep + 'request-builder.js';
    var script12 = dirname(require.resolve('happner-client')) + sep + 'lib' + sep + 'providers' + sep + 'connection-provider.js';
    var script13 = dirname(require.resolve('happner-client')) + sep + 'lib' + sep + 'providers' + sep + 'implementors-provider.js';
    var script14 = dirname(require.resolve('happner-client')) + sep + 'lib' + sep + 'providers' + sep + 'operations-provider.js';
    var script15 = dirname(require.resolve('happner-client')) + sep + 'lib' + sep + 'happner-client.js';

    this.scripts = [
      {watch: false, min: false, file: script1},
      {watch: false, min: false, file: script2},
      {watch: true, min: false, file: script3},
      {watch: true, min: false, file: script4},
      {watch: true, min: false, file: script5},
      {watch: true, min: false, file: script6},
      {watch: true, min: false, file: script7},
      {watch: true, min: false, file: script8},
      {watch: true, min: false, file: script9},
      {watch: true, min: false, file: script10},
      {watch: true, min: false, file: script11},
      {watch: true, min: false, file: script12},
      {watch: true, min: false, file: script13},
      {watch: true, min: false, file: script14},
      {watch: true, min: false, file: script15}
      // With watch = true When not running NODE_ENV=production the package is re-assembled
      // when the file changes.
    ];

    this.merged = {}; // final product, merges scripts

  } catch (e) {
    throw e; // caught in mesh.js
  }
}

Packager.prototype.ensureCacheDirectory = function () {
  fsExtra.ensureDirSync(cachedDirname);
};

Packager.prototype.loadCachedScript = function () {
  try {
    fs.lstatSync(cachedFilename);
  } catch (e) {
    return false;
  }

  this.merged = {
    gzip: true
  };

  try {
    this.merged.data = fs.readFileSync(cachedFilename);
  } catch (e) {
    return false;
  }

  this.merged.md5 = md5(this.merged.data);

  var _this = this;
  Object.defineProperty(this.packages, 'api', {
    get: function () {
      return _this.merged;
    },
    enumerable: true,
    configurable: true
  });

  return true;
};

Packager.prototype.saveCachedScript = function (merged) {
  fs.writeFileSync(cachedFilename, merged.data);
};

Packager.prototype.initialize = function (callback) {

  if (!this.scripts) {
    return callback();
  }

  this.log.$$DEBUG('Building /api/client package');

  var readFilePromise = Promise.promisify(fs.readFile);
  var lstatPromise = Promise.promisify(fs.lstat);
  var _this = this;

  return Promise.delay(100)
  // happn write script Happn.packager.browserClient()
  // not ready on slow filesystems (eg. travis)

    .then(function () {
      return Promise.all(
        _this.scripts.map(function (script) {

          return new Promise(function (resolve, reject) {

            // handle each script object

            if (process.env.NODE_ENV == 'production' && !script.min) {

              var gotMinFile = script.file.replace(/\.js$/, '.min.js');

              // Production
              return lstatPromise(gotMinFile)
                .then(function () {
                  script.file = gotMinFile;
                  script.min = true;
                  resolve(script);
                })
                .catch(function () {
                  script.min = false;
                  resolve(script);
                });
            }<commit_msg>pend the packager further<commit_after>// TODO: This is expensive... minifies and gzips a set of scripts !! at every startup...
//       to provide single /client/api script to browser.
//       Better if this was cached to disk (when in production mode)

// BUG: more than one meshnode in same process and packager does not apply
// post-start changes to script package into all mesh nodes.

// A preliminary work toward asset packaging (minify, gzip, etag, etc.)
// The ideal would be a per component widget assembly line.
// Allows for serving individual scripts development.
// This one packages only the core /client/api script from ./api.js and it's dependancies
// Make it not do all this on every startup.

module.exports = Packager;

var fs = require('fs')
  , zlib = require('zlib')
  , md5 = require('md5')
  , Promise = require('bluebird')
  , minifyJS = require('uglify-js').minify
  , normalize = require('path').normalize
  , dirname = require('path').dirname
  , sep = require('path').sep
  , Happn = require('happn-3')
  , homedir = require('homedir')
  , fsExtra = require('fs-extra')
  , version = require('../../package.json').version
  , cachedDirname = homedir() + sep + '.happner'
  , cachedFilename = cachedDirname + sep + 'api-client-' + version + '.min.js.gz'
  ;

var watchedFiles = {}; // Properly support only one packager per process!

function Packager(mesh) {

  try{

    this.log = mesh.log.createLogger('Packager');
    mesh.tools.packages = this.packages = {};

    if (process.env.NODE_ENV == 'production') {
      this.ensureCacheDirectory();
      if (this.loadCachedScript()) {
        return;
      }
    }

    var script1 = dirname(dirname(require.resolve('bluebird'))) +  sep + 'browser' + sep + 'bluebird.js';
    var script2 = normalize(mesh._mesh.happn.server.services.session.script);
    var script3 = Happn.packager.browserClient();

    var script4 = normalize(__dirname + sep + 'shared' + sep + 'logger.js');
    var script5 = normalize(__dirname + sep + 'shared' + sep + 'promisify.js');
    var script6 = normalize(__dirname + sep + 'shared' + sep + 'mesh-error.js');
    var script7 = normalize(__dirname + sep + 'shared' + sep + 'messenger.js');
    var script8 = normalize(__dirname + sep + 'shared' + sep + 'internals.js');
    var script9 = normalize(__dirname + sep + 'shared' + sep + 'mesh-client.js');

    var script10 = dirname(require.resolve('happner-client')) + sep + 'lib' + sep + 'semver.js';
    var script11 = dirname(require.resolve('happner-client')) + sep + 'lib' + sep + 'builders' + sep + 'request-builder.js';
    var script12 = dirname(require.resolve('happner-client')) + sep + 'lib' + sep + 'providers' + sep + 'connection-provider.js';
    var script13 = dirname(require.resolve('happner-client')) + sep + 'lib' + sep + 'providers' + sep + 'implementors-provider.js';
    var script14 = dirname(require.resolve('happner-client')) + sep + 'lib' + sep + 'providers' + sep + 'operations-provider.js';
    var script15 = dirname(require.resolve('happner-client')) + sep + 'lib' + sep + 'happner-client.js';

    this.scripts = [
      {watch: false, min: false, file: script1},
      {watch: false, min: false, file: script2},
      {watch: true, min: false, file: script3},
      {watch: true, min: false, file: script4},
      {watch: true, min: false, file: script5},
      {watch: true, min: false, file: script6},
      {watch: true, min: false, file: script7},
      {watch: true, min: false, file: script8},
      {watch: true, min: false, file: script9},
      {watch: true, min: false, file: script10},
      {watch: true, min: false, file: script11},
      {watch: true, min: false, file: script12},
      {watch: true, min: false, file: script13},
      {watch: true, min: false, file: script14},
      {watch: true, min: false, file: script15}
      // With watch = true When not running NODE_ENV=production the package is re-assembled
      // when the file changes.
    ];

    this.merged = {}; // final product, merges scripts

  } catch (e) {
    throw e; // caught in mesh.js
  }
}

Packager.prototype.ensureCacheDirectory = function () {
  fsExtra.ensureDirSync(cachedDirname);
};

Packager.prototype.loadCachedScript = function () {
  try {
    fs.lstatSync(cachedFilename);
  } catch (e) {
    return false;
  }

  this.merged = {
    gzip: true
  };

  try {
    this.merged.data = fs.readFileSync(cachedFilename);
  } catch (e) {
    return false;
  }

  this.merged.md5 = md5(this.merged.data);

  var _this = this;
  Object.defineProperty(this.packages, 'api', {
    get: function () {
      return _this.merged;
    },
    enumerable: true,
    configurable: true
  });

  return true;
};

Packager.prototype.saveCachedScript = function (merged) {
  fs.writeFileSync(cachedFilename, merged.data);
};

Packager.prototype.initialize = function (callback) {

  if (!this.scripts) {
    return callback();
  }

  this.log.$$DEBUG('Building /api/client package');

  var readFilePromise = Promise.promisify(fs.readFile);
  var lstatPromise = Promise.promisify(fs.lstat);
  var _this = this;

  return Promise.delay(200)
  // happn write script Happn.packager.browserClient()
  // not ready on slow filesystems (eg. travis)

    .then(function () {
      return Promise.all(
        _this.scripts.map(function (script) {

          return new Promise(function (resolve, reject) {

            // handle each script object

            if (process.env.NODE_ENV == 'production' && !script.min) {

              var gotMinFile = script.file.replace(/\.js$/, '.min.js');

              // Production
              return lstatPromise(gotMinFile)
                .then(function () {
                  script.file = gotMinFile;
                  script.min = true;
                  resolve(script);
                })
                .catch(function () {
                  script.min = false;
                  resolve(script);
                });
            }
{'fraction_non_alphanumeric': 0.14393812006053472, 'fraction_numerical': 0.008407600470825626, 'mean_word_length': 2.837096774193548, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1269313', 'n_tokens_mistral': 3759, 'n_tokens_neox': 3593, 'n_words': 995}