mirror of
https://github.com/status-im/metro.git
synced 2025-03-02 11:40:55 +00:00
[react-packager] Promote Cache to top level
Summary: The cache is only used for JSTransformer at the moment. We're doing IO and some computation to get each module's name, whether is a haste or node module and it's dependencies. This work happens on startup so by caching this value we shouldbe able to reduce the start up time. Lets promote the Cache to the Packager level to be able to use it by any of the components of the packager. For now, on this diff we'll start using it to cache the mentioned fields. Also we had to introduce the concept of fields in the cache as manually merging the date we had for each path is not possible as we're using promisses all around. With the new API, each field is a promise. @amasad and I did some manual testing to measure the impact of this change and looks like it's saves 1 second when building the haste map (which represents 50% of the time). Overall this reduces 1 second of start up time which was currently about 8s on my mac book pro.
This commit is contained in:
parent
cc87e5173f
commit
49fde903b8
20
react-packager/src/Cache/__mocks__/index.js
vendored
Normal file
20
react-packager/src/Cache/__mocks__/index.js
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
class Cache {
|
||||
get(filepath, field, cb) {
|
||||
return cb(filepath);
|
||||
}
|
||||
|
||||
invalidate(filepath) { }
|
||||
end() { }
|
||||
}
|
||||
|
||||
module.exports = Cache;
|
288
react-packager/src/Cache/__tests__/Cache-test.js
vendored
Normal file
288
react-packager/src/Cache/__tests__/Cache-test.js
vendored
Normal file
@ -0,0 +1,288 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
jest
|
||||
.dontMock('underscore')
|
||||
.dontMock('absolute-path')
|
||||
.dontMock('../');
|
||||
|
||||
jest
|
||||
.mock('os')
|
||||
.mock('fs');
|
||||
|
||||
var Promise = require('promise');
|
||||
|
||||
describe('JSTransformer Cache', () => {
|
||||
var Cache;
|
||||
|
||||
beforeEach(() => {
|
||||
require('os').tmpDir.mockImpl(() => 'tmpDir');
|
||||
|
||||
Cache = require('../');
|
||||
});
|
||||
|
||||
describe('getting/setting', () => {
|
||||
pit('calls loader callback for uncached file', () => {
|
||||
require('fs').stat.mockImpl((file, callback) => {
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => {}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImpl(() => Promise.resolve());
|
||||
|
||||
return cache
|
||||
.get('/rootDir/someFile', 'field', loaderCb)
|
||||
.then($ =>
|
||||
expect(loaderCb).toBeCalledWith('/rootDir/someFile')
|
||||
);
|
||||
});
|
||||
|
||||
pit('supports storing multiple fields', () => {
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
var index = 0;
|
||||
var loaderCb = jest.genMockFn().mockImpl(() =>
|
||||
Promise.resolve(index++)
|
||||
);
|
||||
|
||||
return cache
|
||||
.get('/rootDir/someFile', 'field1', loaderCb)
|
||||
.then(value => {
|
||||
expect(value).toBe(0);
|
||||
return cache
|
||||
.get('/rootDir/someFile', 'field2', loaderCb)
|
||||
.then(value2 => expect(value2).toBe(1));
|
||||
});
|
||||
});
|
||||
|
||||
pit('gets the value from the loader callback', () => {
|
||||
require('fs').stat.mockImpl((file, callback) =>
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => {}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImpl(() =>
|
||||
Promise.resolve('lol')
|
||||
);
|
||||
|
||||
return cache
|
||||
.get('/rootDir/someFile', 'field', loaderCb)
|
||||
.then(value => expect(value).toBe('lol'));
|
||||
});
|
||||
|
||||
pit('caches the value after the first call', () => {
|
||||
require('fs').stat.mockImpl((file, callback) => {
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => {}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImpl(() =>
|
||||
Promise.resolve('lol')
|
||||
);
|
||||
|
||||
return cache
|
||||
.get('/rootDir/someFile', 'field', loaderCb)
|
||||
.then(() => {
|
||||
var shouldNotBeCalled = jest.genMockFn();
|
||||
return cache.get('/rootDir/someFile', 'field', shouldNotBeCalled)
|
||||
.then(value => {
|
||||
expect(shouldNotBeCalled).not.toBeCalled();
|
||||
expect(value).toBe('lol');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
pit('clears old field when getting new field and mtime changed', () => {
|
||||
var mtime = 0;
|
||||
require('fs').stat.mockImpl((file, callback) => {
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => mtime++
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImpl(() =>
|
||||
Promise.resolve('lol' + mtime)
|
||||
);
|
||||
|
||||
return cache
|
||||
.get('/rootDir/someFile', 'field1', loaderCb)
|
||||
.then(value => cache
|
||||
.get('/rootDir/someFile', 'field2', loaderCb)
|
||||
.then(value2 => cache
|
||||
.get('/rootDir/someFile', 'field1', loaderCb)
|
||||
.then(value3 => expect(value3).toBe('lol2'))
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loading cache from disk', () => {
|
||||
var fileStats;
|
||||
|
||||
beforeEach(() => {
|
||||
fileStats = {
|
||||
'/rootDir/someFile': {
|
||||
mtime: {
|
||||
getTime: () => 22
|
||||
}
|
||||
},
|
||||
'/rootDir/foo': {
|
||||
mtime: {
|
||||
getTime: () => 11
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var fs = require('fs');
|
||||
|
||||
fs.existsSync.mockImpl(() => true);
|
||||
|
||||
fs.statSync.mockImpl(filePath => fileStats[filePath]);
|
||||
|
||||
fs.readFileSync.mockImpl(() => JSON.stringify({
|
||||
'/rootDir/someFile': {
|
||||
metadata: {mtime: 22},
|
||||
data: {field: 'oh hai'},
|
||||
},
|
||||
'/rootDir/foo': {
|
||||
metadata: {mtime: 11},
|
||||
data: {field: 'lol wat'},
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
pit('should load cache from disk', () => {
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
var loaderCb = jest.genMockFn();
|
||||
|
||||
return cache
|
||||
.get('/rootDir/someFile', 'field', loaderCb)
|
||||
.then(value => {
|
||||
expect(loaderCb).not.toBeCalled();
|
||||
expect(value).toBe('oh hai');
|
||||
|
||||
return cache
|
||||
.get('/rootDir/foo', 'field', loaderCb)
|
||||
.then(val => {
|
||||
expect(loaderCb).not.toBeCalled();
|
||||
expect(val).toBe('lol wat');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
pit('should not load outdated cache', () => {
|
||||
require('fs').stat.mockImpl((file, callback) =>
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => {}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
fileStats['/rootDir/foo'].mtime.getTime = () => 123;
|
||||
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImpl(() =>
|
||||
Promise.resolve('new value')
|
||||
);
|
||||
|
||||
return cache
|
||||
.get('/rootDir/someFile', 'field', loaderCb)
|
||||
.then(value => {
|
||||
expect(loaderCb).not.toBeCalled();
|
||||
expect(value).toBe('oh hai');
|
||||
|
||||
return cache
|
||||
.get('/rootDir/foo', 'field', loaderCb)
|
||||
.then(val => {
|
||||
expect(loaderCb).toBeCalled();
|
||||
expect(val).toBe('new value');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('writing cache to disk', () => {
|
||||
it('should write cache to disk', () => {
|
||||
var index = 0;
|
||||
var mtimes = [10, 20, 30];
|
||||
var debounceIndex = 0;
|
||||
require('underscore').debounce = callback => {
|
||||
return () => {
|
||||
if (++debounceIndex === 3) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var fs = require('fs');
|
||||
fs.stat.mockImpl((file, callback) =>
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => mtimes[index++]
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
|
||||
cache.get('/rootDir/bar', 'field', () =>
|
||||
Promise.resolve('bar value')
|
||||
);
|
||||
cache.get('/rootDir/foo', 'field', () =>
|
||||
Promise.resolve('foo value')
|
||||
);
|
||||
cache.get('/rootDir/baz', 'field', () =>
|
||||
Promise.resolve('baz value')
|
||||
);
|
||||
|
||||
jest.runAllTicks();
|
||||
expect(fs.writeFile).toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
222
react-packager/src/Cache/index.js
vendored
Normal file
222
react-packager/src/Cache/index.js
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
/**
|
||||
* 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('promise');
|
||||
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,
|
||||
},
|
||||
transformModulePath: {
|
||||
type:'string',
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
// TODO: move to Packager directory
|
||||
class Cache {
|
||||
constructor(options) {
|
||||
var opts = validateOpts(options);
|
||||
|
||||
this._cacheFilePath = this._getCacheFilePath(opts);
|
||||
|
||||
var data;
|
||||
if (!opts.resetCache) {
|
||||
data = this._loadCacheSync(this._cacheFilePath);
|
||||
} else {
|
||||
data = Object.create(null);
|
||||
}
|
||||
this._data = data;
|
||||
|
||||
this._persistEventually = _.debounce(
|
||||
this._persistCache.bind(this),
|
||||
2000,
|
||||
);
|
||||
}
|
||||
|
||||
get(filepath, field, loaderCb) {
|
||||
if (!isAbsolutePath(filepath)) {
|
||||
throw new Error('Use absolute paths');
|
||||
}
|
||||
|
||||
var recordP = this._has(filepath, field)
|
||||
? this._data[filepath].data[field]
|
||||
: this._set(filepath, field, loaderCb(filepath));
|
||||
|
||||
return recordP.then(record => record);
|
||||
}
|
||||
|
||||
invalidate(filepath) {
|
||||
if (this._has(filepath)) {
|
||||
delete this._data[filepath];
|
||||
}
|
||||
}
|
||||
|
||||
end() {
|
||||
return this._persistCache();
|
||||
}
|
||||
|
||||
_has(filepath, field) {
|
||||
return Object.prototype.hasOwnProperty.call(this._data, filepath) &&
|
||||
(!field || Object.prototype.hasOwnProperty.call(this._data[filepath].data, field));
|
||||
}
|
||||
|
||||
_set(filepath, field, loaderPromise) {
|
||||
let record = this._data[filepath];
|
||||
if (!record) {
|
||||
record = Object.create(null);
|
||||
this._data[filepath] = record;
|
||||
this._data[filepath].data = Object.create(null);
|
||||
this._data[filepath].metadata = Object.create(null);
|
||||
}
|
||||
|
||||
record.data[field] = loaderPromise
|
||||
.then(data => Promise.all([
|
||||
data,
|
||||
Promise.denodeify(fs.stat)(filepath),
|
||||
]))
|
||||
.then(([data, stat]) => {
|
||||
this._persistEventually();
|
||||
|
||||
// Evict all existing field data from the cache if we're putting new
|
||||
// more up to date data
|
||||
var mtime = stat.mtime.getTime();
|
||||
if (record.metadata.mtime !== mtime) {
|
||||
record.data = Object.create(null);
|
||||
}
|
||||
record.metadata.mtime = mtime;
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
return record.data[field];
|
||||
}
|
||||
|
||||
_persistCache() {
|
||||
if (this._persisting != null) {
|
||||
return this._persisting;
|
||||
}
|
||||
|
||||
var data = this._data;
|
||||
var cacheFilepath = this._cacheFilePath;
|
||||
|
||||
var allPromises = _.values(data)
|
||||
.map(record => {
|
||||
var fieldNames = Object.keys(record.data);
|
||||
var fieldValues = _.values(record.data);
|
||||
|
||||
return Promise
|
||||
.all(fieldValues)
|
||||
.then(ref => {
|
||||
var ret = Object.create(null);
|
||||
ret.metadata = record.metadata;
|
||||
ret.data = Object.create(null);
|
||||
fieldNames.forEach((field, index) =>
|
||||
ret.data[field] = ref[index]
|
||||
);
|
||||
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this._persisting = Promise.all(allPromises)
|
||||
.then(values => {
|
||||
var json = Object.create(null);
|
||||
Object.keys(data).forEach((key, i) => {
|
||||
json[key] = Object.create(null);
|
||||
json[key].metadata = data[key].metadata;
|
||||
json[key].data = values[i].data;
|
||||
});
|
||||
return Promise.denodeify(fs.writeFile)(cacheFilepath, JSON.stringify(json));
|
||||
})
|
||||
.then(() => {
|
||||
this._persisting = null;
|
||||
return true;
|
||||
});
|
||||
|
||||
return this._persisting;
|
||||
}
|
||||
|
||||
_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(key => {
|
||||
if (!fs.existsSync(key)) {
|
||||
return;
|
||||
}
|
||||
var record = cacheOnDisk[key];
|
||||
var stat = fs.statSync(key);
|
||||
if (stat.mtime.getTime() === record.metadata.mtime) {
|
||||
ret[key] = Object.create(null);
|
||||
ret[key].metadata = Object.create(null);
|
||||
ret[key].data = Object.create(null);
|
||||
ret[key].metadata.mtime = record.metadata.mtime;
|
||||
|
||||
Object.keys(record.data).forEach(field => {
|
||||
ret[key].data[field] = Promise.resolve(record.data[field]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
_getCacheFilePath(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);
|
||||
|
||||
hash.update(options.transformModulePath);
|
||||
|
||||
var name = 'react-packager-cache-' + hash.digest('hex');
|
||||
return path.join(tmpdir, name);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Cache;
|
@ -29,12 +29,15 @@ const Promise = require('promise');
|
||||
jest.mock('fs');
|
||||
|
||||
describe('DependencyGraph', function() {
|
||||
var cache;
|
||||
var Cache;
|
||||
var DependencyGraph;
|
||||
var fileWatcher;
|
||||
var fs;
|
||||
|
||||
beforeEach(function() {
|
||||
fs = require('fs');
|
||||
Cache = require('../../../Cache');
|
||||
DependencyGraph = require('../index');
|
||||
|
||||
fileWatcher = {
|
||||
@ -43,6 +46,8 @@ describe('DependencyGraph', function() {
|
||||
},
|
||||
isWatchman: () => Promise.resolve(false)
|
||||
};
|
||||
|
||||
cache = new Cache({});
|
||||
});
|
||||
|
||||
describe('getOrderedDependencies', function() {
|
||||
@ -68,6 +73,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -125,6 +131,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -176,6 +183,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -235,6 +243,7 @@ describe('DependencyGraph', function() {
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
assetRoots_DEPRECATED: ['/root/imgs'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -286,6 +295,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -342,6 +352,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -419,6 +430,7 @@ describe('DependencyGraph', function() {
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
assetRoots_DEPRECATED: ['/root/imgs'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -480,6 +492,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -532,6 +545,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -584,6 +598,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -644,6 +659,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -700,6 +716,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -750,6 +767,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -799,6 +817,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -845,6 +864,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -895,6 +915,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -943,6 +964,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -996,6 +1018,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/somedir/somefile.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1053,6 +1076,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1100,6 +1124,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1146,6 +1171,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1204,6 +1230,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1262,6 +1289,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1340,6 +1368,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1396,6 +1425,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1452,6 +1482,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1508,6 +1539,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1579,6 +1611,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1681,6 +1714,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1761,6 +1795,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1842,6 +1877,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.ios.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -1924,6 +1960,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -2020,6 +2057,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -2105,6 +2143,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -2209,6 +2248,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -2277,6 +2317,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/react-tools/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -2333,6 +2374,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -2377,6 +2419,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -2437,6 +2480,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.ios.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -2492,6 +2536,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.ios.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -2540,6 +2585,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.ios.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -2623,6 +2669,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
||||
filesystem.root['index.js'] =
|
||||
@ -2687,6 +2734,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
||||
filesystem.root['index.js'] =
|
||||
@ -2751,6 +2799,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
||||
delete filesystem.root.foo;
|
||||
@ -2814,6 +2863,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
||||
filesystem.root['bar.js'] = [
|
||||
@ -2895,6 +2945,7 @@ describe('DependencyGraph', function() {
|
||||
assetRoots_DEPRECATED: [root],
|
||||
assetExts: ['png'],
|
||||
fileWatcher: fileWatcher,
|
||||
cache: cache,
|
||||
});
|
||||
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
@ -2966,6 +3017,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
assetExts: ['png'],
|
||||
fileWatcher: fileWatcher,
|
||||
cache: cache,
|
||||
});
|
||||
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
@ -3052,7 +3104,8 @@ describe('DependencyGraph', function() {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
||||
filesystem.root['bar.js'] = [
|
||||
@ -3137,6 +3190,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
||||
triggerFileChange('change', 'aPackage', '/root', {
|
||||
@ -3207,6 +3261,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
||||
filesystem.root['index.js'] = filesystem.root['index.js'].replace(/aPackage/, 'bPackage');
|
||||
@ -3273,6 +3328,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
||||
filesystem.root.aPackage['package.json'] = JSON.stringify({
|
||||
@ -3337,6 +3393,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
||||
filesystem.root.aPackage['package.json'] = JSON.stringify({
|
||||
@ -3399,6 +3456,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
expect(deps)
|
||||
@ -3498,6 +3556,7 @@ describe('DependencyGraph', function() {
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
||||
filesystem.root.node_modules.foo['package.json'] = JSON.stringify({
|
||||
|
@ -59,7 +59,11 @@ const validateOpts = declareOpts({
|
||||
platforms: {
|
||||
type: 'array',
|
||||
default: ['ios', 'android'],
|
||||
}
|
||||
},
|
||||
cache: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
class DependencyGraph {
|
||||
@ -67,6 +71,7 @@ class DependencyGraph {
|
||||
this._opts = validateOpts(options);
|
||||
this._hasteMap = Object.create(null);
|
||||
this._immediateResolutionCache = Object.create(null);
|
||||
this._cache = this._opts.cache;
|
||||
this.load();
|
||||
}
|
||||
|
||||
@ -84,17 +89,21 @@ class DependencyGraph {
|
||||
});
|
||||
this._crawling.then((files) => Activity.endEvent(crawlActivity));
|
||||
|
||||
this._fastfs = new Fastfs(this._opts.roots,this._opts.fileWatcher, {
|
||||
this._fastfs = new Fastfs(this._opts.roots, this._opts.fileWatcher, {
|
||||
ignore: this._opts.ignoreFilePath,
|
||||
crawling: this._crawling,
|
||||
});
|
||||
|
||||
this._fastfs.on('change', this._processFileChange.bind(this));
|
||||
|
||||
this._moduleCache = new ModuleCache(this._fastfs);
|
||||
this._moduleCache = new ModuleCache(this._fastfs, this._cache);
|
||||
|
||||
this._loading = Promise.all([
|
||||
this._fastfs.build().then(() => this._buildHasteMap()),
|
||||
this._fastfs.build()
|
||||
.then(() => {
|
||||
const hasteActivity = Activity.startEvent('haste map');
|
||||
this._buildHasteMap().then(() => Activity.endEvent(hasteActivity));
|
||||
}),
|
||||
this._buildAssetMap_DEPRECATED(),
|
||||
]);
|
||||
|
||||
@ -141,7 +150,7 @@ class DependencyGraph {
|
||||
() => this._resolveNodeDependency(fromModule, toModuleName)
|
||||
).then(
|
||||
cacheResult,
|
||||
forgive
|
||||
forgive,
|
||||
);
|
||||
}
|
||||
|
||||
|
53
react-packager/src/DependencyResolver/Module.js
vendored
53
react-packager/src/DependencyResolver/Module.js
vendored
@ -8,7 +8,7 @@ const replacePatterns = require('./replacePatterns');
|
||||
|
||||
class Module {
|
||||
|
||||
constructor(file, fastfs, moduleCache) {
|
||||
constructor(file, fastfs, moduleCache, cache) {
|
||||
if (!isAbsolutePath(file)) {
|
||||
throw new Error('Expected file to be absolute path but got ' + file);
|
||||
}
|
||||
@ -18,34 +18,41 @@ class Module {
|
||||
|
||||
this._fastfs = fastfs;
|
||||
this._moduleCache = moduleCache;
|
||||
this._cache = cache;
|
||||
}
|
||||
|
||||
isHaste() {
|
||||
return this._read().then(data => !!data.id);
|
||||
return this._cache.get(this.path, 'haste', () =>
|
||||
this._read().then(data => !!data.id)
|
||||
);
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this._read().then(data => {
|
||||
if (data.id) {
|
||||
return data.id;
|
||||
}
|
||||
return this._cache.get(
|
||||
this.path,
|
||||
'name',
|
||||
() => this._read().then(data => {
|
||||
if (data.id) {
|
||||
return data.id;
|
||||
}
|
||||
|
||||
const p = this.getPackage();
|
||||
const p = this.getPackage();
|
||||
|
||||
if (!p) {
|
||||
// Name is full path
|
||||
return this.path;
|
||||
}
|
||||
if (!p) {
|
||||
// Name is full path
|
||||
return this.path;
|
||||
}
|
||||
|
||||
return p.getName()
|
||||
.then(name => {
|
||||
if (!name) {
|
||||
return this.path;
|
||||
}
|
||||
return p.getName()
|
||||
.then(name => {
|
||||
if (!name) {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
return path.join(name, path.relative(p.root, this.path));
|
||||
});
|
||||
});
|
||||
return path.join(name, path.relative(p.root, this.path));
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getPackage() {
|
||||
@ -53,7 +60,13 @@ class Module {
|
||||
}
|
||||
|
||||
getDependencies() {
|
||||
return this._read().then(data => data.dependencies);
|
||||
return this._cache.get(this.path, 'dependencies', () =>
|
||||
this._read().then(data => data.dependencies)
|
||||
);
|
||||
}
|
||||
|
||||
invalidate() {
|
||||
this._cache.invalidate(this.path);
|
||||
}
|
||||
|
||||
_read() {
|
||||
|
@ -7,17 +7,23 @@ const path = require('path');
|
||||
|
||||
class ModuleCache {
|
||||
|
||||
constructor(fastfs) {
|
||||
constructor(fastfs, cache) {
|
||||
this._moduleCache = Object.create(null);
|
||||
this._packageCache = Object.create(null);
|
||||
this._fastfs = fastfs;
|
||||
this._cache = cache;
|
||||
fastfs.on('change', this._processFileChange.bind(this));
|
||||
}
|
||||
|
||||
getModule(filePath) {
|
||||
filePath = path.resolve(filePath);
|
||||
if (!this._moduleCache[filePath]) {
|
||||
this._moduleCache[filePath] = new Module(filePath, this._fastfs, this);
|
||||
this._moduleCache[filePath] = new Module(
|
||||
filePath,
|
||||
this._fastfs,
|
||||
this,
|
||||
this._cache,
|
||||
);
|
||||
}
|
||||
return this._moduleCache[filePath];
|
||||
}
|
||||
@ -28,7 +34,8 @@ class ModuleCache {
|
||||
this._moduleCache[filePath] = new AssetModule(
|
||||
filePath,
|
||||
this._fastfs,
|
||||
this
|
||||
this,
|
||||
this._cache,
|
||||
);
|
||||
}
|
||||
return this._moduleCache[filePath];
|
||||
@ -37,7 +44,11 @@ class ModuleCache {
|
||||
getPackage(filePath) {
|
||||
filePath = path.resolve(filePath);
|
||||
if (!this._packageCache[filePath]){
|
||||
this._packageCache[filePath] = new Package(filePath, this._fastfs);
|
||||
this._packageCache[filePath] = new Package(
|
||||
filePath,
|
||||
this._fastfs,
|
||||
this._cache,
|
||||
);
|
||||
}
|
||||
return this._packageCache[filePath];
|
||||
}
|
||||
@ -64,8 +75,15 @@ class ModuleCache {
|
||||
|
||||
_processFileChange(type, filePath, root) {
|
||||
const absPath = path.join(root, filePath);
|
||||
delete this._moduleCache[absPath];
|
||||
delete this._packageCache[absPath];
|
||||
|
||||
if (this._moduleCache[absPath]) {
|
||||
this._moduleCache[absPath].invalidate();
|
||||
delete this._moduleCache[absPath];
|
||||
}
|
||||
if (this._packageCache[absPath]) {
|
||||
this._packageCache[absPath].invalidate();
|
||||
delete this._packageCache[absPath];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
15
react-packager/src/DependencyResolver/Package.js
vendored
15
react-packager/src/DependencyResolver/Package.js
vendored
@ -5,11 +5,12 @@ const path = require('path');
|
||||
|
||||
class Package {
|
||||
|
||||
constructor(file, fastfs) {
|
||||
constructor(file, fastfs, cache) {
|
||||
this.path = path.resolve(file);
|
||||
this.root = path.dirname(this.path);
|
||||
this._fastfs = fastfs;
|
||||
this.type = 'Package';
|
||||
this._cache = cache;
|
||||
}
|
||||
|
||||
getMain() {
|
||||
@ -33,11 +34,19 @@ class Package {
|
||||
}
|
||||
|
||||
isHaste() {
|
||||
return this._read().then(json => !!json.name);
|
||||
return this._cache.get(this.path, 'haste', () =>
|
||||
this._read().then(json => !!json.name)
|
||||
);
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this._read().then(json => json.name);
|
||||
return this._cache.get(this.path, 'name', () =>
|
||||
this._read().then(json => json.name)
|
||||
);
|
||||
}
|
||||
|
||||
invalidate() {
|
||||
this._cache.invalidate(this.path);
|
||||
}
|
||||
|
||||
redirectRequire(name) {
|
||||
|
@ -45,7 +45,11 @@ var validateOpts = declareOpts({
|
||||
assetExts: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
cache: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
function HasteDependencyResolver(options) {
|
||||
@ -60,6 +64,7 @@ function HasteDependencyResolver(options) {
|
||||
(opts.blacklistRE && opts.blacklistRE.test(filepath));
|
||||
},
|
||||
fileWatcher: opts.fileWatcher,
|
||||
cache: opts.cache,
|
||||
});
|
||||
|
||||
|
||||
|
175
react-packager/src/JSTransformer/Cache.js
vendored
175
react-packager/src/JSTransformer/Cache.js
vendored
@ -1,175 +0,0 @@
|
||||
/**
|
||||
* 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('promise');
|
||||
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,
|
||||
},
|
||||
transformModulePath: {
|
||||
type:'string',
|
||||
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 Promise.all([
|
||||
data,
|
||||
Promise.denodeify(fs.stat)(filepath)
|
||||
]);
|
||||
}).then(function(ref) {
|
||||
var data = ref[0];
|
||||
var stat = ref[1];
|
||||
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.denodeify(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);
|
||||
|
||||
hash.update(options.transformModulePath);
|
||||
|
||||
var name = 'react-packager-cache-' + hash.digest('hex');
|
||||
return path.join(tmpdir, name);
|
||||
}
|
@ -1,236 +0,0 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
jest
|
||||
.dontMock('underscore')
|
||||
.dontMock('absolute-path')
|
||||
.dontMock('../Cache');
|
||||
|
||||
jest
|
||||
.mock('os')
|
||||
.mock('fs');
|
||||
|
||||
var Promise = require('promise');
|
||||
|
||||
describe('JSTransformer Cache', function() {
|
||||
var Cache;
|
||||
|
||||
beforeEach(function() {
|
||||
require('os').tmpDir.mockImpl(function() {
|
||||
return 'tmpDir';
|
||||
});
|
||||
|
||||
Cache = require('../Cache');
|
||||
});
|
||||
|
||||
describe('getting/setting', function() {
|
||||
it('calls loader callback for uncached file', function() {
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImpl(function() {
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
cache.get('/rootDir/someFile', loaderCb);
|
||||
expect(loaderCb).toBeCalledWith('/rootDir/someFile');
|
||||
});
|
||||
|
||||
pit('gets the value from the loader callback', function() {
|
||||
require('fs').stat.mockImpl(function(file, callback) {
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: function() {}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImpl(function() {
|
||||
return Promise.resolve('lol');
|
||||
});
|
||||
|
||||
return cache.get('/rootDir/someFile', loaderCb).then(function(value) {
|
||||
expect(value).toBe('lol');
|
||||
});
|
||||
});
|
||||
|
||||
pit('caches the value after the first call', function() {
|
||||
require('fs').stat.mockImpl(function(file, callback) {
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: function() {}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImpl(function() {
|
||||
return Promise.resolve('lol');
|
||||
});
|
||||
|
||||
return cache.get('/rootDir/someFile', loaderCb).then(function() {
|
||||
var shouldNotBeCalled = jest.genMockFn();
|
||||
return cache.get('/rootDir/someFile', shouldNotBeCalled)
|
||||
.then(function(value) {
|
||||
expect(shouldNotBeCalled).not.toBeCalled();
|
||||
expect(value).toBe('lol');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('loading cache from disk', function() {
|
||||
var fileStats;
|
||||
|
||||
beforeEach(function() {
|
||||
fileStats = {
|
||||
'/rootDir/someFile': {
|
||||
mtime: {
|
||||
getTime: function() {
|
||||
return 22;
|
||||
}
|
||||
}
|
||||
},
|
||||
'/rootDir/foo': {
|
||||
mtime: {
|
||||
getTime: function() {
|
||||
return 11;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var fs = require('fs');
|
||||
|
||||
fs.existsSync.mockImpl(function() {
|
||||
return true;
|
||||
});
|
||||
|
||||
fs.statSync.mockImpl(function(filePath) {
|
||||
return fileStats[filePath];
|
||||
});
|
||||
|
||||
fs.readFileSync.mockImpl(function() {
|
||||
return JSON.stringify({
|
||||
'/rootDir/someFile': {
|
||||
mtime: 22,
|
||||
data: 'oh hai'
|
||||
},
|
||||
'/rootDir/foo': {
|
||||
mtime: 11,
|
||||
data: 'lol wat'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
pit('should load cache from disk', function() {
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
var loaderCb = jest.genMockFn();
|
||||
|
||||
return cache.get('/rootDir/someFile', loaderCb).then(function(value) {
|
||||
expect(loaderCb).not.toBeCalled();
|
||||
expect(value).toBe('oh hai');
|
||||
|
||||
return cache.get('/rootDir/foo', loaderCb).then(function(value) {
|
||||
expect(loaderCb).not.toBeCalled();
|
||||
expect(value).toBe('lol wat');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
pit('should not load outdated cache', function() {
|
||||
require('fs').stat.mockImpl(function(file, callback) {
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: function() {}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
fileStats['/rootDir/foo'].mtime.getTime = function() {
|
||||
return 123;
|
||||
};
|
||||
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImpl(function() {
|
||||
return Promise.resolve('new value');
|
||||
});
|
||||
|
||||
return cache.get('/rootDir/someFile', loaderCb).then(function(value) {
|
||||
expect(loaderCb).not.toBeCalled();
|
||||
expect(value).toBe('oh hai');
|
||||
|
||||
return cache.get('/rootDir/foo', loaderCb).then(function(value) {
|
||||
expect(loaderCb).toBeCalled();
|
||||
expect(value).toBe('new value');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('writing cache to disk', function() {
|
||||
it('should write cache to disk', function() {
|
||||
var index = 0;
|
||||
var mtimes = [10, 20, 30];
|
||||
var debounceIndex = 0;
|
||||
require('underscore').debounce = function(callback) {
|
||||
return function () {
|
||||
if (++debounceIndex === 3) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var fs = require('fs');
|
||||
fs.stat.mockImpl(function(file, callback) {
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: function() {
|
||||
return mtimes[index++];
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var cache = new Cache({
|
||||
projectRoots: ['/rootDir'],
|
||||
transformModulePath: 'x.js',
|
||||
});
|
||||
|
||||
cache.get('/rootDir/bar', function() {
|
||||
return Promise.resolve('bar value');
|
||||
});
|
||||
cache.get('/rootDir/foo', function() {
|
||||
return Promise.resolve('foo value');
|
||||
});
|
||||
cache.get('/rootDir/baz', function() {
|
||||
return Promise.resolve('baz value');
|
||||
});
|
||||
|
||||
jest.runAllTicks();
|
||||
expect(fs.writeFile).toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
@ -15,8 +15,11 @@ jest
|
||||
|
||||
jest.mock('fs');
|
||||
|
||||
var Cache = require('../../Cache');
|
||||
|
||||
var OPTIONS = {
|
||||
transformModulePath: '/foo/bar'
|
||||
transformModulePath: '/foo/bar',
|
||||
cache: new Cache({}),
|
||||
};
|
||||
|
||||
describe('Transformer', function() {
|
||||
@ -28,9 +31,6 @@ describe('Transformer', function() {
|
||||
jest.setMock('worker-farm', jest.genMockFn().mockImpl(function() {
|
||||
return workers;
|
||||
}));
|
||||
require('../Cache').prototype.get.mockImpl(function(filePath, callback) {
|
||||
return callback();
|
||||
});
|
||||
require('fs').readFile.mockImpl(function(file, callback) {
|
||||
callback(null, 'content');
|
||||
});
|
||||
|
35
react-packager/src/JSTransformer/index.js
vendored
35
react-packager/src/JSTransformer/index.js
vendored
@ -10,7 +10,6 @@
|
||||
|
||||
var fs = require('fs');
|
||||
var Promise = require('promise');
|
||||
var Cache = require('./Cache');
|
||||
var workerFarm = require('worker-farm');
|
||||
var declareOpts = require('../lib/declareOpts');
|
||||
var util = require('util');
|
||||
@ -33,35 +32,20 @@ var validateOpts = declareOpts({
|
||||
type: 'array',
|
||||
default: [],
|
||||
},
|
||||
cacheVersion: {
|
||||
type: 'string',
|
||||
default: '1.0',
|
||||
},
|
||||
resetCache: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
transformModulePath: {
|
||||
type:'string',
|
||||
required: false,
|
||||
},
|
||||
nonPersistent: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
cache: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
function Transformer(options) {
|
||||
var opts = validateOpts(options);
|
||||
|
||||
this._cache = opts.nonPersistent
|
||||
? new DummyCache()
|
||||
: new Cache({
|
||||
resetCache: options.resetCache,
|
||||
cacheVersion: options.cacheVersion,
|
||||
projectRoots: options.projectRoots,
|
||||
transformModulePath: options.transformModulePath,
|
||||
});
|
||||
this._cache = opts.cache;
|
||||
|
||||
if (options.transformModulePath != null) {
|
||||
this._workers = workerFarm(
|
||||
@ -75,7 +59,6 @@ function Transformer(options) {
|
||||
|
||||
Transformer.prototype.kill = function() {
|
||||
this._workers && workerFarm.end(this._workers);
|
||||
return this._cache.end();
|
||||
};
|
||||
|
||||
Transformer.prototype.invalidateFile = function(filePath) {
|
||||
@ -88,7 +71,8 @@ Transformer.prototype.loadFileAndTransform = function(filePath) {
|
||||
}
|
||||
|
||||
var transform = this._transform;
|
||||
return this._cache.get(filePath, function() {
|
||||
return this._cache.get(filePath, 'transformedSource', function() {
|
||||
// TODO: use fastfs to avoid reading file from disk again
|
||||
return readFile(filePath)
|
||||
.then(function(buffer) {
|
||||
var sourceCode = buffer.toString();
|
||||
@ -157,10 +141,3 @@ function formatBabelError(err, filename) {
|
||||
error.description = err.message;
|
||||
return error;
|
||||
}
|
||||
|
||||
function DummyCache() {}
|
||||
DummyCache.prototype.get = function(filePath, loaderCb) {
|
||||
return loaderCb();
|
||||
};
|
||||
DummyCache.prototype.end =
|
||||
DummyCache.prototype.invalidate = function(){};
|
||||
|
27
react-packager/src/Packager/index.js
vendored
27
react-packager/src/Packager/index.js
vendored
@ -12,6 +12,7 @@ var assert = require('assert');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var Promise = require('promise');
|
||||
var Cache = require('../Cache');
|
||||
var Transformer = require('../JSTransformer');
|
||||
var DependencyResolver = require('../DependencyResolver');
|
||||
var Package = require('./Package');
|
||||
@ -78,6 +79,15 @@ function Packager(options) {
|
||||
|
||||
opts.projectRoots.forEach(verifyRootExists);
|
||||
|
||||
this._cache = opts.nonPersistent
|
||||
? new DummyCache()
|
||||
: new Cache({
|
||||
resetCache: opts.resetCache,
|
||||
cacheVersion: opts.cacheVersion,
|
||||
projectRoots: opts.projectRoots,
|
||||
transformModulePath: opts.transformModulePath,
|
||||
});
|
||||
|
||||
this._resolver = new DependencyResolver({
|
||||
projectRoots: opts.projectRoots,
|
||||
blacklistRE: opts.blacklistRE,
|
||||
@ -87,15 +97,14 @@ function Packager(options) {
|
||||
assetRoots: opts.assetRoots,
|
||||
fileWatcher: opts.fileWatcher,
|
||||
assetExts: opts.assetExts,
|
||||
cache: this._cache,
|
||||
});
|
||||
|
||||
this._transformer = new Transformer({
|
||||
projectRoots: opts.projectRoots,
|
||||
blacklistRE: opts.blacklistRE,
|
||||
cacheVersion: opts.cacheVersion,
|
||||
resetCache: opts.resetCache,
|
||||
cache: this._cache,
|
||||
transformModulePath: opts.transformModulePath,
|
||||
nonPersistent: opts.nonPersistent,
|
||||
});
|
||||
|
||||
this._projectRoots = opts.projectRoots;
|
||||
@ -103,7 +112,8 @@ function Packager(options) {
|
||||
}
|
||||
|
||||
Packager.prototype.kill = function() {
|
||||
return this._transformer.kill();
|
||||
this._transformer.kill();
|
||||
return this._cache.end();
|
||||
};
|
||||
|
||||
Packager.prototype.package = function(main, runModule, sourceMapUrl, isDev) {
|
||||
@ -267,4 +277,13 @@ function verifyRootExists(root) {
|
||||
assert(fs.statSync(root).isDirectory(), 'Root has to be a valid directory');
|
||||
}
|
||||
|
||||
class DummyCache {
|
||||
get(filepath, field, loaderCb) {
|
||||
return loaderCb();
|
||||
}
|
||||
|
||||
end(){}
|
||||
invalidate(filepath){}
|
||||
}
|
||||
|
||||
module.exports = Packager;
|
||||
|
Loading…
x
Reference in New Issue
Block a user