Files
react-native/packager/react-packager/src/JSTransformer/Cache.js
Pilwon Huh 87599bfcd1 [react-packager] Switch from Q to Bluebird as promises library
Summary:
This PR improves performance of `react-packager` by switching the promises library from the [Q](https://github.com/kriskowal/q) to [Bluebird](https://github.com/petkaantonov/bluebird).

[Here is the test result](https://github.com/facebook/react-native/issues/361#issuecomment-87829808) showing a noticeable difference. (2x speed improvement)

Please refer to [this issue](https://github.com/facebook/react-native/issues/361) for more details.
Closes https://github.com/facebook/react-native/pull/516
Github Author: Pilwon Huh <pilwon@gmail.com>

Test Plan:
./runJestTests
start app and click around
2015-03-31 21:24:13 -08:00

168 lines
3.9 KiB
JavaScript

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
var _ = require('underscore');
var crypto = require('crypto');
var declareOpts = require('../lib/declareOpts');
var fs = require('fs');
var isAbsolutePath = require('absolute-path');
var path = require('path');
var Promise = require('bluebird');
var tmpdir = require('os').tmpDir();
var version = require('../../../../package.json').version;
var validateOpts = declareOpts({
resetCache: {
type: 'boolean',
default: false,
},
cacheVersion: {
type: 'string',
default: '1.0',
},
projectRoots: {
type: 'array',
required: true,
},
});
module.exports = Cache;
function Cache(options) {
var opts = validateOpts(options);
this._cacheFilePath = cacheFilePath(opts);
var data;
if (!opts.resetCache) {
data = loadCacheSync(this._cacheFilePath);
} else {
data = Object.create(null);
}
this._data = data;
this._has = Object.prototype.hasOwnProperty.bind(data);
this._persistEventually = _.debounce(
this._persistCache.bind(this),
2000
);
}
Cache.prototype.get = function(filepath, loaderCb) {
if (!isAbsolutePath(filepath)) {
throw new Error('Use absolute paths');
}
var recordP = this._has(filepath)
? this._data[filepath]
: this._set(filepath, loaderCb(filepath));
return recordP.then(function(record) {
return record.data;
});
};
Cache.prototype._set = function(filepath, loaderPromise) {
this._data[filepath] = loaderPromise.then(function(data) {
return [
data,
Promise.promisify(fs.stat)(filepath)
];
}).spread(function(data, stat) {
this._persistEventually();
return {
data: data,
mtime: stat.mtime.getTime(),
};
}.bind(this));
return this._data[filepath];
};
Cache.prototype.invalidate = function(filepath){
if (this._has(filepath)) {
delete this._data[filepath];
}
};
Cache.prototype.end = function() {
return this._persistCache();
};
Cache.prototype._persistCache = function() {
if (this._persisting != null) {
return this._persisting;
}
var data = this._data;
var cacheFilepath = this._cacheFilePath;
this._persisting = Promise.all(_.values(data))
.then(function(values) {
var json = Object.create(null);
Object.keys(data).forEach(function(key, i) {
json[key] = values[i];
});
return Promise.promisify(fs.writeFile)(cacheFilepath, JSON.stringify(json));
})
.then(function() {
this._persisting = null;
return true;
}.bind(this));
return this._persisting;
};
function loadCacheSync(cachePath) {
var ret = Object.create(null);
if (!fs.existsSync(cachePath)) {
return ret;
}
var cacheOnDisk;
try {
cacheOnDisk = JSON.parse(fs.readFileSync(cachePath));
} catch (e) {
if (e instanceof SyntaxError) {
console.warn('Unable to parse cache file. Will clear and continue.');
fs.unlinkSync(cachePath);
return ret;
}
throw e;
}
// Filter outdated cache and convert to promises.
Object.keys(cacheOnDisk).forEach(function(key) {
if (!fs.existsSync(key)) {
return;
}
var value = cacheOnDisk[key];
var stat = fs.statSync(key);
if (stat.mtime.getTime() === value.mtime) {
ret[key] = Promise.resolve(value);
}
});
return ret;
}
function cacheFilePath(options) {
var hash = crypto.createHash('md5');
hash.update(version);
var roots = options.projectRoots.join(',').split(path.sep).join('-');
hash.update(roots);
var cacheVersion = options.cacheVersion || '0';
hash.update(cacheVersion);
var name = 'react-packager-cache-' + hash.digest('hex');
return path.join(tmpdir, name);
}