mirror of https://github.com/status-im/metro.git
packager: remove Cache
Summary: It seems we don't need that one much anymore, as we have a FS-based cache for files, and removing the last callsites doesn't really change perf, as we have to read these files eventually (plus some of these are read from `HasteMap`, that I believe is already dead code). Reviewed By: davidaurelio Differential Revision: D4884220 fbshipit-source-id: 4cf59f16a6f0bdf275abe81e9de2f34816866bae
This commit is contained in:
parent
c0533fc57c
commit
edcaf438a7
|
@ -15,7 +15,6 @@ const assert = require('assert');
|
|||
const crypto = require('crypto');
|
||||
const debug = require('debug')('RNP:Bundler');
|
||||
const fs = require('fs');
|
||||
const Cache = require('../node-haste').Cache;
|
||||
const Transformer = require('../JSTransformer');
|
||||
const Resolver = require('../Resolver');
|
||||
const Bundle = require('./Bundle');
|
||||
|
@ -114,7 +113,6 @@ class Bundler {
|
|||
|
||||
_opts: Options;
|
||||
_getModuleId: (opts: Module) => number;
|
||||
_cache: Cache;
|
||||
_transformer: Transformer;
|
||||
_resolverPromise: Promise<Resolver>;
|
||||
_projectRoots: Array<string>;
|
||||
|
@ -165,11 +163,6 @@ class Bundler {
|
|||
|
||||
debug(`Using transform cache key "${transformCacheKey}"`);
|
||||
|
||||
this._cache = new Cache({
|
||||
resetCache: opts.resetCache,
|
||||
cacheKey: transformCacheKey,
|
||||
});
|
||||
|
||||
const maxWorkerCount = Bundler.getMaxWorkerCount();
|
||||
|
||||
/* $FlowFixMe: in practice it's always here. */
|
||||
|
@ -182,7 +175,6 @@ class Bundler {
|
|||
this._resolverPromise = Resolver.load({
|
||||
assetExts: opts.assetExts,
|
||||
blacklistRE: opts.blacklistRE,
|
||||
cache: this._cache,
|
||||
extraNodeModules: opts.extraNodeModules,
|
||||
getTransformCacheKey,
|
||||
globalTransformCache: opts.globalTransformCache,
|
||||
|
@ -213,12 +205,9 @@ class Bundler {
|
|||
|
||||
end() {
|
||||
this._transformer.kill();
|
||||
return Promise.all([
|
||||
this._cache.end(),
|
||||
this._resolverPromise.then(
|
||||
resolver => resolver.getDependencyGraph().getWatcher().end(),
|
||||
),
|
||||
]);
|
||||
return this._resolverPromise.then(
|
||||
resolver => resolver.getDependencyGraph().getWatcher().end(),
|
||||
);
|
||||
}
|
||||
|
||||
bundle(options: {
|
||||
|
@ -478,10 +467,6 @@ class Bundler {
|
|||
});
|
||||
}
|
||||
|
||||
invalidateFile(filePath: string) {
|
||||
this._cache.invalidate(filePath);
|
||||
}
|
||||
|
||||
getShallowDependencies({
|
||||
entryFile,
|
||||
platform,
|
||||
|
|
|
@ -22,7 +22,6 @@ import type {SourceMap} from '../lib/SourceMap';
|
|||
import type {Options as TransformOptions} from '../JSTransformer/worker/worker';
|
||||
import type {Reporter} from '../lib/reporting';
|
||||
import type {TransformCode} from '../node-haste/Module';
|
||||
import type Cache from '../node-haste/Cache';
|
||||
import type {GetTransformCacheKey} from '../lib/TransformCache';
|
||||
import type {GlobalTransformCache} from '../lib/GlobalTransformCache';
|
||||
|
||||
|
@ -32,7 +31,6 @@ type MinifyCode = (filePath: string, code: string, map: SourceMap) =>
|
|||
type Options = {|
|
||||
+assetExts: Array<string>,
|
||||
+blacklistRE?: RegExp,
|
||||
+cache: Cache,
|
||||
+extraNodeModules: ?{},
|
||||
+getTransformCacheKey: GetTransformCacheKey,
|
||||
+globalTransformCache: ?GlobalTransformCache,
|
||||
|
|
|
@ -220,15 +220,6 @@ describe('processRequest', () => {
|
|||
});
|
||||
|
||||
describe('file changes', () => {
|
||||
it('invalides files in bundle when file is updated', () => {
|
||||
return makeRequest(
|
||||
requestHandler,
|
||||
'mybundle.bundle?runModule=true'
|
||||
).then(() => {
|
||||
server.onFileChange('all', options.projectRoots[0] + '/path/file.js');
|
||||
expect(invalidatorFunc.mock.calls[0][0]).toEqual('root/path/file.js');
|
||||
});
|
||||
});
|
||||
|
||||
it('does not rebuild the bundles that contain a file when that file is changed', () => {
|
||||
const bundleFunc = jest.fn();
|
||||
|
|
|
@ -329,7 +329,6 @@ class Server {
|
|||
|
||||
onFileChange(type: string, filePath: string, stat: Stats) {
|
||||
this._assetServer.onFileChange(type, filePath, stat);
|
||||
this._bundler.invalidateFile(filePath);
|
||||
|
||||
// If Hot Loading is enabled avoid rebuilding bundles and sending live
|
||||
// updates. Instead, send the HMR updates right away and clear the bundles
|
||||
|
|
|
@ -1,20 +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';
|
||||
|
||||
class Cache {
|
||||
get(filepath, field, cb) {
|
||||
return cb(filepath);
|
||||
}
|
||||
|
||||
invalidate(filepath) { }
|
||||
end() { }
|
||||
}
|
||||
|
||||
module.exports = Cache;
|
|
@ -1,360 +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('absolute-path')
|
||||
.dontMock('../');
|
||||
|
||||
jest
|
||||
.mock('fs')
|
||||
.setMock('os', {
|
||||
tmpdir() { return 'tmpdir'; },
|
||||
});
|
||||
|
||||
jest.useRealTimers();
|
||||
|
||||
|
||||
describe('Cache', () => {
|
||||
let Cache, fs;
|
||||
beforeEach(() => {
|
||||
Cache = require('../');
|
||||
fs = require('graceful-fs');
|
||||
});
|
||||
|
||||
describe('getting/setting', () => {
|
||||
it('calls loader callback for uncached file', () => {
|
||||
fs.stat.mockImplementation((file, callback) => {
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
var cache = new Cache({
|
||||
cacheKey: 'cache',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImplementation(() => Promise.resolve());
|
||||
|
||||
return cache
|
||||
.get('/rootDir/someFile', 'field', loaderCb)
|
||||
.then($ =>
|
||||
expect(loaderCb).toBeCalledWith('/rootDir/someFile')
|
||||
);
|
||||
});
|
||||
|
||||
it('supports storing multiple fields', () => {
|
||||
fs.stat.mockImplementation((file, callback) => {
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
var cache = new Cache({
|
||||
cacheKey: 'cache',
|
||||
});
|
||||
var index = 0;
|
||||
var loaderCb = jest.genMockFn().mockImplementation(() =>
|
||||
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));
|
||||
});
|
||||
});
|
||||
|
||||
it('gets the value from the loader callback', () => {
|
||||
fs.stat.mockImplementation((file, callback) =>
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => {},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
var cache = new Cache({
|
||||
cacheKey: 'cache',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImplementation(() =>
|
||||
Promise.resolve('lol')
|
||||
);
|
||||
|
||||
return cache
|
||||
.get('/rootDir/someFile', 'field', loaderCb)
|
||||
.then(value => expect(value).toBe('lol'));
|
||||
});
|
||||
|
||||
it('caches the value after the first call', () => {
|
||||
fs.stat.mockImplementation((file, callback) => {
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
var cache = new Cache({
|
||||
cacheKey: 'cache',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImplementation(() =>
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('clears old field when getting new field and mtime changed', () => {
|
||||
var mtime = 0;
|
||||
fs.stat.mockImplementation((file, callback) => {
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => mtime++,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
var cache = new Cache({
|
||||
cacheKey: 'cache',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImplementation(() =>
|
||||
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'))
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('does not cache rejections', () => {
|
||||
fs.stat.mockImplementation((file, callback) => {
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
var cache = new Cache({
|
||||
cacheKey: 'cache',
|
||||
});
|
||||
var loaderCb = () => Promise.reject('lol');
|
||||
|
||||
return cache
|
||||
.get('/rootDir/someFile', 'field', loaderCb)
|
||||
.catch(() => {
|
||||
var shouldBeCalled = jest.fn(() => Promise.resolve());
|
||||
const assert = value => expect(shouldBeCalled).toBeCalled();
|
||||
return cache.get('/rootDir/someFile', 'field', shouldBeCalled)
|
||||
.then(assert, assert);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('loading cache from disk', () => {
|
||||
var fileStats;
|
||||
|
||||
beforeEach(() => {
|
||||
fileStats = {
|
||||
'/rootDir/someFile': {
|
||||
mtime: {
|
||||
getTime: () => 22,
|
||||
},
|
||||
},
|
||||
'/rootDir/foo': {
|
||||
mtime: {
|
||||
getTime: () => 11,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
fs.existsSync.mockImplementation(() => true);
|
||||
|
||||
fs.statSync.mockImplementation(filePath => fileStats[filePath]);
|
||||
|
||||
fs.readFileSync.mockImplementation(() => JSON.stringify({
|
||||
'/rootDir/someFile': {
|
||||
metadata: {mtime: 22},
|
||||
data: {field: 'oh hai'},
|
||||
},
|
||||
'/rootDir/foo': {
|
||||
metadata: {mtime: 11},
|
||||
data: {field: 'lol wat'},
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
it('should load cache from disk', () => {
|
||||
var cache = new Cache({
|
||||
cacheKey: 'cache',
|
||||
});
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not load outdated cache', () => {
|
||||
fs.stat.mockImplementation((file, callback) =>
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => {},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
fileStats['/rootDir/foo'].mtime.getTime = () => 123;
|
||||
|
||||
var cache = new Cache({
|
||||
cacheKey: 'cache',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImplementation(() =>
|
||||
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', done => {
|
||||
var index = 0;
|
||||
var mtimes = [10, 20, 30];
|
||||
|
||||
fs.stat.mockImplementation((file, callback) =>
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => mtimes[index++],
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
var cache = new Cache({
|
||||
cacheKey: 'cache',
|
||||
});
|
||||
|
||||
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')
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(fs.writeFile).toBeCalled();
|
||||
done();
|
||||
}, 2020);
|
||||
});
|
||||
});
|
||||
|
||||
describe('check for cache presence', () => {
|
||||
it('synchronously resolves cache presence', () => {
|
||||
fs.stat.mockImplementation((file, callback) =>
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => {},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
var cache = new Cache({
|
||||
cacheKey: 'cache',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImplementation(() =>
|
||||
Promise.resolve('banana')
|
||||
);
|
||||
var file = '/rootDir/someFile';
|
||||
|
||||
return cache
|
||||
.get(file, 'field', loaderCb)
|
||||
.then(() => {
|
||||
expect(cache.has(file)).toBe(true);
|
||||
expect(cache.has(file, 'field')).toBe(true);
|
||||
expect(cache.has(file, 'foo')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalidate', () => {
|
||||
it('invalidates the cache per file or per-field', () => {
|
||||
fs.stat.mockImplementation((file, callback) =>
|
||||
callback(null, {
|
||||
mtime: {
|
||||
getTime: () => {},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
var cache = new Cache({
|
||||
cacheKey: 'cache',
|
||||
});
|
||||
var loaderCb = jest.genMockFn().mockImplementation(() =>
|
||||
Promise.resolve('banana')
|
||||
);
|
||||
var file = '/rootDir/someFile';
|
||||
|
||||
return cache.get(file, 'field', loaderCb).then(() => {
|
||||
expect(cache.has(file)).toBe(true);
|
||||
cache.invalidate(file, 'field');
|
||||
expect(cache.has(file)).toBe(true);
|
||||
expect(cache.has(file, 'field')).toBe(false);
|
||||
cache.invalidate(file);
|
||||
expect(cache.has(file)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,254 +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.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
const denodeify = require('denodeify');
|
||||
const fs = require('graceful-fs');
|
||||
const isAbsolutePath = require('absolute-path');
|
||||
const path = require('path');
|
||||
const tmpDir = require('os').tmpdir();
|
||||
|
||||
function getObjectValues<T>(object: {[key: string]: T}): Array<T> {
|
||||
return Object.keys(object).map(key => object[key]);
|
||||
}
|
||||
|
||||
function debounce(fn, delay) {
|
||||
var timeout;
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(fn, delay);
|
||||
};
|
||||
}
|
||||
|
||||
type Record = {
|
||||
data: {[field: string]: Promise<mixed>},
|
||||
metadata: {[field: string]: Promise<mixed>},
|
||||
};
|
||||
|
||||
class Cache {
|
||||
|
||||
_cacheFilePath: string;
|
||||
_data: {[filename: string]: Record};
|
||||
_persistEventually: () => void;
|
||||
_persisting: ?Promise<boolean> | void;
|
||||
|
||||
constructor({
|
||||
resetCache,
|
||||
cacheKey,
|
||||
cacheDirectory = tmpDir,
|
||||
}: {
|
||||
resetCache: boolean,
|
||||
cacheKey: string,
|
||||
cacheDirectory?: string,
|
||||
}) {
|
||||
this._cacheFilePath = Cache.getCacheFilePath(cacheDirectory, cacheKey);
|
||||
if (!resetCache) {
|
||||
this._data = this._loadCacheSync(this._cacheFilePath);
|
||||
} else {
|
||||
this._data = Object.create(null);
|
||||
}
|
||||
|
||||
this._persistEventually = debounce(this._persistCache.bind(this), 2000);
|
||||
}
|
||||
|
||||
static getCacheFilePath(tmpdir, ...args) {
|
||||
const hash = crypto.createHash('md5');
|
||||
args.forEach(arg => hash.update(arg));
|
||||
return path.join(tmpdir, hash.digest('hex'));
|
||||
}
|
||||
|
||||
get<T>(
|
||||
filepath: string,
|
||||
field: string,
|
||||
loaderCb: (filepath: string) => Promise<T>,
|
||||
): Promise<T> {
|
||||
if (!isAbsolutePath(filepath)) {
|
||||
throw new Error('Use absolute paths');
|
||||
}
|
||||
|
||||
return this.has(filepath, field)
|
||||
/* $FlowFixMe: this class is unsound as a whole because it uses
|
||||
* untyped storage where in fact each "field" has a particular type.
|
||||
* We cannot express this using Flow. */
|
||||
? (this._data[filepath].data[field]: Promise<T>)
|
||||
: this.set(filepath, field, loaderCb(filepath));
|
||||
}
|
||||
|
||||
invalidate(filepath: string, field: ?string) {
|
||||
if (this.has(filepath, field)) {
|
||||
if (field == null) {
|
||||
delete this._data[filepath];
|
||||
} else {
|
||||
delete this._data[filepath].data[field];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end() {
|
||||
return this._persistCache();
|
||||
}
|
||||
|
||||
has(filepath: string, field: ?string) {
|
||||
return Object.prototype.hasOwnProperty.call(this._data, filepath) &&
|
||||
(field == null || Object.prototype.hasOwnProperty.call(this._data[filepath].data, field));
|
||||
}
|
||||
|
||||
set<T>(
|
||||
filepath: string,
|
||||
field: string,
|
||||
loaderPromise: Promise<T>,
|
||||
): Promise<T> {
|
||||
let record = this._data[filepath];
|
||||
if (!record) {
|
||||
// $FlowFixMe: temporarily invalid record.
|
||||
record = (Object.create(null): Record);
|
||||
this._data[filepath] = record;
|
||||
this._data[filepath].data = Object.create(null);
|
||||
this._data[filepath].metadata = Object.create(null);
|
||||
}
|
||||
|
||||
const cachedPromise = record.data[field] = loaderPromise
|
||||
.then(data => Promise.all([
|
||||
data,
|
||||
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;
|
||||
});
|
||||
|
||||
// don't cache rejected promises
|
||||
cachedPromise.catch(error => delete record.data[field]);
|
||||
return cachedPromise;
|
||||
}
|
||||
|
||||
_persistCache() {
|
||||
if (this._persisting != null) {
|
||||
return this._persisting;
|
||||
}
|
||||
|
||||
const data = this._data;
|
||||
const cacheFilepath = this._cacheFilePath;
|
||||
|
||||
const allPromises = getObjectValues(data)
|
||||
.map(record => {
|
||||
const fieldNames = Object.keys(record.data);
|
||||
const fieldValues = getObjectValues(record.data);
|
||||
|
||||
return Promise
|
||||
.all(fieldValues)
|
||||
.then(ref => {
|
||||
// $FlowFixMe: temporarily invalid record.
|
||||
const ret = (Object.create(null): Record);
|
||||
ret.metadata = record.metadata;
|
||||
ret.data = Object.create(null);
|
||||
/* $FlowFixMe(>=0.36.0 site=react_native_fb,react_native_oss) Flow
|
||||
* error detected during the deploy of Flow v0.36.0. To see the
|
||||
* error, remove this comment and run Flow */
|
||||
fieldNames.forEach((field, index) =>
|
||||
ret.data[field] = ref[index]
|
||||
);
|
||||
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this._persisting = Promise.all(allPromises)
|
||||
.then(values => {
|
||||
const json = Object.create(null);
|
||||
Object.keys(data).forEach((key, i) => {
|
||||
// make sure the key wasn't added nor removed after we started
|
||||
// persisting the cache
|
||||
const value = values[i];
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
json[key] = Object.create(null);
|
||||
json[key].metadata = data[key].metadata;
|
||||
json[key].data = value.data;
|
||||
});
|
||||
return denodeify(fs.writeFile)(cacheFilepath, JSON.stringify(json));
|
||||
})
|
||||
.catch(e => console.error(
|
||||
'[node-haste] Encountered an error while persisting cache:\n%s',
|
||||
e.stack.split('\n').map(line => '> ' + line).join('\n')
|
||||
))
|
||||
.then(() => {
|
||||
this._persisting = null;
|
||||
return true;
|
||||
});
|
||||
|
||||
return this._persisting;
|
||||
}
|
||||
|
||||
_loadCacheSync(cachePath) {
|
||||
var ret = Object.create(null);
|
||||
var cacheOnDisk = loadCacheSync(cachePath);
|
||||
|
||||
// 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);
|
||||
// $FlowFixMe: we should maybe avoid Object.create().
|
||||
ret[key].metadata.mtime = record.metadata.mtime;
|
||||
|
||||
Object.keys(record.data).forEach(field => {
|
||||
ret[key].data[field] = Promise.resolve(record.data[field]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
function loadCacheSync(cachePath) {
|
||||
if (!fs.existsSync(cachePath)) {
|
||||
return Object.create(null);
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(cachePath, 'utf8'));
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError) {
|
||||
console.warn('Unable to parse cache file. Will clear and continue.');
|
||||
try {
|
||||
fs.unlinkSync(cachePath);
|
||||
} catch (err) {
|
||||
// Someone else might've deleted it.
|
||||
}
|
||||
return Object.create(null);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Cache;
|
|
@ -28,7 +28,6 @@ import type {SourceMap} from '../lib/SourceMap';
|
|||
import type {GetTransformCacheKey} from '../lib/TransformCache';
|
||||
import type {ReadTransformProps} from '../lib/TransformCache';
|
||||
import type {Reporter} from '../lib/reporting';
|
||||
import type Cache from './Cache';
|
||||
import type DependencyGraphHelpers from './DependencyGraph/DependencyGraphHelpers';
|
||||
import type ModuleCache from './ModuleCache';
|
||||
|
||||
|
@ -68,7 +67,6 @@ export type Options = {
|
|||
};
|
||||
|
||||
export type ConstructorArgs = {
|
||||
cache: Cache,
|
||||
depGraphHelpers: DependencyGraphHelpers,
|
||||
globalTransformCache: ?GlobalTransformCache,
|
||||
file: string,
|
||||
|
@ -87,7 +85,6 @@ class Module {
|
|||
type: string;
|
||||
|
||||
_moduleCache: ModuleCache;
|
||||
_cache: Cache;
|
||||
_transformCode: ?TransformCode;
|
||||
_getTransformCacheKey: GetTransformCacheKey;
|
||||
_depGraphHelpers: DependencyGraphHelpers;
|
||||
|
@ -103,7 +100,6 @@ class Module {
|
|||
_readResultsByOptionsKey: Map<string, CachedReadResult>;
|
||||
|
||||
constructor({
|
||||
cache,
|
||||
depGraphHelpers,
|
||||
file,
|
||||
getTransformCacheKey,
|
||||
|
@ -121,7 +117,6 @@ class Module {
|
|||
this.type = 'Module';
|
||||
|
||||
this._moduleCache = moduleCache;
|
||||
this._cache = cache;
|
||||
this._transformCode = transformCode;
|
||||
this._getTransformCacheKey = getTransformCacheKey;
|
||||
this._depGraphHelpers = depGraphHelpers;
|
||||
|
@ -134,11 +129,7 @@ class Module {
|
|||
}
|
||||
|
||||
isHaste(): Promise<boolean> {
|
||||
return this._cache.get(
|
||||
this.path,
|
||||
'isHaste',
|
||||
() => Promise.resolve().then(() => this._getHasteName() != null),
|
||||
);
|
||||
return Promise.resolve().then(() => this._getHasteName() != null);
|
||||
}
|
||||
|
||||
getCode(transformOptions: TransformOptions) {
|
||||
|
@ -150,32 +141,28 @@ class Module {
|
|||
}
|
||||
|
||||
getName(): Promise<string> {
|
||||
return this._cache.get(
|
||||
this.path,
|
||||
'name',
|
||||
() => Promise.resolve().then(() => {
|
||||
const name = this._getHasteName();
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
return Promise.resolve().then(() => {
|
||||
const name = this._getHasteName();
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
|
||||
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(packageName => {
|
||||
if (!packageName) {
|
||||
return this.path;
|
||||
}
|
||||
return p.getName()
|
||||
.then(packageName => {
|
||||
if (!packageName) {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
return joinPath(packageName, relativePath(p.root, this.path)).replace(/\\/g, '/');
|
||||
});
|
||||
})
|
||||
);
|
||||
return joinPath(packageName, relativePath(p.root, this.path)).replace(/\\/g, '/');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getPackage() {
|
||||
|
@ -192,7 +179,6 @@ class Module {
|
|||
* code.
|
||||
*/
|
||||
invalidate() {
|
||||
this._cache.invalidate(this.path);
|
||||
this._readPromises.clear();
|
||||
this._readResultsByOptionsKey.clear();
|
||||
this._sourceCode = null;
|
||||
|
|
|
@ -19,7 +19,6 @@ const Polyfill = require('./Polyfill');
|
|||
import type {GlobalTransformCache} from '../lib/GlobalTransformCache';
|
||||
import type {GetTransformCacheKey} from '../lib/TransformCache';
|
||||
import type {Reporter} from '../lib/reporting';
|
||||
import type Cache from './Cache';
|
||||
import type DependencyGraphHelpers from './DependencyGraph/DependencyGraphHelpers';
|
||||
import type {TransformCode, Options as ModuleOptions} from './Module';
|
||||
|
||||
|
@ -28,7 +27,6 @@ type GetClosestPackageFn = (filePath: string) => ?string;
|
|||
class ModuleCache {
|
||||
|
||||
_assetDependencies: Array<string>;
|
||||
_cache: Cache;
|
||||
_depGraphHelpers: DependencyGraphHelpers;
|
||||
_getClosestPackage: GetClosestPackageFn;
|
||||
_getTransformCacheKey: GetTransformCacheKey;
|
||||
|
@ -43,7 +41,6 @@ class ModuleCache {
|
|||
|
||||
constructor({
|
||||
assetDependencies,
|
||||
cache,
|
||||
depGraphHelpers,
|
||||
extractRequires,
|
||||
getClosestPackage,
|
||||
|
@ -54,7 +51,6 @@ class ModuleCache {
|
|||
transformCode,
|
||||
}: {
|
||||
assetDependencies: Array<string>,
|
||||
cache: Cache,
|
||||
depGraphHelpers: DependencyGraphHelpers,
|
||||
getClosestPackage: GetClosestPackageFn,
|
||||
getTransformCacheKey: GetTransformCacheKey,
|
||||
|
@ -67,7 +63,6 @@ class ModuleCache {
|
|||
this._getClosestPackage = getClosestPackage;
|
||||
this._getTransformCacheKey = getTransformCacheKey;
|
||||
this._globalTransformCache = globalTransformCache;
|
||||
this._cache = cache;
|
||||
this._depGraphHelpers = depGraphHelpers;
|
||||
this._moduleCache = Object.create(null);
|
||||
this._moduleOptions = moduleOptions;
|
||||
|
@ -81,7 +76,6 @@ class ModuleCache {
|
|||
getModule(filePath: string): Module {
|
||||
if (!this._moduleCache[filePath]) {
|
||||
this._moduleCache[filePath] = new Module({
|
||||
cache: this._cache,
|
||||
depGraphHelpers: this._depGraphHelpers,
|
||||
file: filePath,
|
||||
getTransformCacheKey: this._getTransformCacheKey,
|
||||
|
@ -107,7 +101,6 @@ class ModuleCache {
|
|||
this._moduleCache[filePath] = new AssetModule({
|
||||
file: filePath,
|
||||
moduleCache: this,
|
||||
cache: this._cache,
|
||||
dependencies: this._assetDependencies,
|
||||
}, this._platforms);
|
||||
}
|
||||
|
@ -118,7 +111,6 @@ class ModuleCache {
|
|||
if (!this._packageCache[filePath]) {
|
||||
this._packageCache[filePath] = new Package({
|
||||
file: filePath,
|
||||
cache: this._cache,
|
||||
});
|
||||
}
|
||||
return this._packageCache[filePath];
|
||||
|
@ -148,7 +140,6 @@ class ModuleCache {
|
|||
/* $FlowFixMe: there are missing arguments. */
|
||||
return new Polyfill({
|
||||
file,
|
||||
cache: this._cache,
|
||||
depGraphHelpers: this._depGraphHelpers,
|
||||
getTransformCacheKey: this._getTransformCacheKey,
|
||||
moduleCache: this,
|
||||
|
|
|
@ -15,8 +15,6 @@ const fs = require('fs');
|
|||
const isAbsolutePath = require('absolute-path');
|
||||
const path = require('path');
|
||||
|
||||
import type Cache from './Cache';
|
||||
|
||||
type PackageContent = {
|
||||
name: string,
|
||||
'react-native': mixed,
|
||||
|
@ -29,18 +27,15 @@ class Package {
|
|||
path: string;
|
||||
root: string;
|
||||
type: string;
|
||||
_cache: Cache;
|
||||
|
||||
_content: ?PackageContent;
|
||||
|
||||
constructor({file, cache}: {
|
||||
constructor({file}: {
|
||||
file: string,
|
||||
cache: Cache,
|
||||
}) {
|
||||
this.path = path.resolve(file);
|
||||
this.root = path.dirname(this.path);
|
||||
this.type = 'Package';
|
||||
this._cache = cache;
|
||||
this._content = null;
|
||||
}
|
||||
|
||||
|
@ -65,20 +60,16 @@ class Package {
|
|||
return path.join(this.root, main);
|
||||
}
|
||||
|
||||
isHaste() {
|
||||
return this._cache.get(this.path, 'package-haste', () =>
|
||||
Promise.resolve().then(() => !!this.read().name)
|
||||
);
|
||||
isHaste(): Promise<boolean> {
|
||||
return Promise.resolve().then(() => !!this.read().name);
|
||||
}
|
||||
|
||||
getName(): Promise<string> {
|
||||
return this._cache.get(this.path, 'package-name', () =>
|
||||
Promise.resolve().then(() => this.read().name)
|
||||
);
|
||||
return Promise.resolve().then(() => this.read().name);
|
||||
}
|
||||
|
||||
invalidate() {
|
||||
this._cache.invalidate(this.path);
|
||||
this._content = null;
|
||||
}
|
||||
|
||||
redirectRequire(name: string): string | false {
|
||||
|
|
|
@ -63,46 +63,8 @@ describe('DependencyGraph', function() {
|
|||
Module = require('../Module');
|
||||
ResolutionRequest = require('../DependencyGraph/ResolutionRequest');
|
||||
|
||||
const Cache = jest.genMockFn().mockImplementation(function() {
|
||||
this._maps = Object.create(null);
|
||||
});
|
||||
Cache.prototype.has = jest.genMockFn()
|
||||
.mockImplementation(function(filepath, field) {
|
||||
if (!(filepath in this._maps)) {
|
||||
return false;
|
||||
}
|
||||
return !field || field in this._maps[filepath];
|
||||
});
|
||||
Cache.prototype.get = jest.genMockFn()
|
||||
.mockImplementation(function(filepath, field, factory) {
|
||||
let cacheForPath = this._maps[filepath];
|
||||
if (this.has(filepath, field)) {
|
||||
return field ? cacheForPath[field] : cacheForPath;
|
||||
}
|
||||
|
||||
if (!cacheForPath) {
|
||||
cacheForPath = this._maps[filepath] = Object.create(null);
|
||||
}
|
||||
const value = cacheForPath[field] = factory();
|
||||
return value;
|
||||
});
|
||||
Cache.prototype.invalidate = jest.genMockFn()
|
||||
.mockImplementation(function(filepath, field) {
|
||||
if (!this.has(filepath, field)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (field) {
|
||||
delete this._maps[filepath][field];
|
||||
} else {
|
||||
delete this._maps[filepath];
|
||||
}
|
||||
});
|
||||
Cache.prototype.end = jest.genMockFn();
|
||||
|
||||
defaults = {
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: new Cache(),
|
||||
extensions: ['js', 'json'],
|
||||
forceNodeFilesystemAPI: true,
|
||||
providesModuleNodeModules: [
|
||||
|
@ -4970,7 +4932,7 @@ describe('DependencyGraph', function() {
|
|||
},
|
||||
{
|
||||
dependencies: [],
|
||||
id: 'aPackage/main.js',
|
||||
id: 'bPackage/main.js',
|
||||
isAsset: false,
|
||||
isJSON: false,
|
||||
isPolyfill: false,
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const Cache = require('./Cache');
|
||||
const DependencyGraphHelpers = require('./DependencyGraph/DependencyGraphHelpers');
|
||||
const FilesByDirNameIndex = require('./FilesByDirNameIndex');
|
||||
const JestHasteMap = require('jest-haste-map');
|
||||
|
@ -52,7 +51,6 @@ import type {HasteFS} from './types';
|
|||
type Options = {|
|
||||
+assetDependencies: Array<string>,
|
||||
+assetExts: Array<string>,
|
||||
+cache: Cache,
|
||||
+extensions: Array<string>,
|
||||
+extraNodeModules: ?{},
|
||||
+forceNodeFilesystemAPI: boolean,
|
||||
|
@ -164,7 +162,6 @@ class DependencyGraph extends EventEmitter {
|
|||
_createModuleCache() {
|
||||
const {_opts} = this;
|
||||
return new ModuleCache({
|
||||
cache: _opts.cache,
|
||||
getTransformCacheKey: _opts.getTransformCacheKey,
|
||||
globalTransformCache: _opts.globalTransformCache,
|
||||
transformCode: _opts.transformCode,
|
||||
|
@ -290,7 +287,6 @@ class DependencyGraph extends EventEmitter {
|
|||
return this._moduleCache.createPolyfill(options);
|
||||
}
|
||||
|
||||
static Cache;
|
||||
static Module;
|
||||
static Polyfill;
|
||||
static getAssetDataFromName;
|
||||
|
@ -301,7 +297,6 @@ class DependencyGraph extends EventEmitter {
|
|||
}
|
||||
|
||||
Object.assign(DependencyGraph, {
|
||||
Cache,
|
||||
Module,
|
||||
Polyfill,
|
||||
getAssetDataFromName,
|
||||
|
|
Loading…
Reference in New Issue