From 90d1b6ee62df799a148526ec8a15faf3a1dbf0e6 Mon Sep 17 00:00:00 2001 From: Christoph Pojer Date: Fri, 18 Dec 2015 16:13:25 -0800 Subject: [PATCH] Move Cache into DependencyGraph Reviewed By: martinbigio Differential Revision: D2758776 fb-gh-sync-id: 15fb232e00267698e386d5422cb70e2091dabcdd --- react-packager/src/Bundler/index.js | 23 +++-- .../__tests__/BundlesLayout-test.js | 2 +- .../BundlesLayoutIntegration-test.js | 4 +- react-packager/src/BundlesLayout/index.js | 6 +- .../Cache/__mocks__/index.js | 0 .../Cache/__tests__/Cache-test.js | 87 ++++++++----------- .../{ => DependencyResolver}/Cache/index.js | 82 ++++++----------- .../Cache}/lib/getCacheFilePath.js | 11 +-- .../Cache}/lib/loadCacheSync.js | 0 .../__tests__/Transformer-test.js | 2 +- 10 files changed, 90 insertions(+), 127 deletions(-) rename react-packager/src/{ => DependencyResolver}/Cache/__mocks__/index.js (100%) rename react-packager/src/{ => DependencyResolver}/Cache/__tests__/Cache-test.js (80%) rename react-packager/src/{ => DependencyResolver}/Cache/index.js (73%) rename react-packager/src/{ => DependencyResolver/Cache}/lib/getCacheFilePath.js (65%) rename react-packager/src/{ => DependencyResolver/Cache}/lib/loadCacheSync.js (100%) diff --git a/react-packager/src/Bundler/index.js b/react-packager/src/Bundler/index.js index 470c6821..31794a9b 100644 --- a/react-packager/src/Bundler/index.js +++ b/react-packager/src/Bundler/index.js @@ -14,7 +14,7 @@ const path = require('path'); const Promise = require('promise'); const ProgressBar = require('progress'); const BundlesLayout = require('../BundlesLayout'); -const Cache = require('../Cache'); +const Cache = require('../DependencyResolver/Cache'); const Transformer = require('../JSTransformer'); const Resolver = require('../Resolver'); const Bundle = require('./Bundle'); @@ -23,6 +23,7 @@ const Activity = require('../Activity'); const ModuleTransport = require('../lib/ModuleTransport'); const declareOpts = require('../lib/declareOpts'); const imageSize = require('image-size'); +const version = require('../../../../package.json').version; const sizeOf = Promise.denodeify(imageSize); const readFile = Promise.denodeify(fs.readFile); @@ -88,11 +89,23 @@ class Bundler { opts.projectRoots.forEach(verifyRootExists); + let mtime; + try { + ({mtime} = fs.statSync(opts.transformModulePath)); + mtime = String(mtime.getTime()); + } catch (error) { + mtime = ''; + } + this._cache = new Cache({ resetCache: opts.resetCache, - cacheVersion: opts.cacheVersion, - projectRoots: opts.projectRoots, - transformModulePath: opts.transformModulePath, + cacheKey: [ + 'react-packager-cache', + version, + opts.cacheVersion, + opts.projectRoots.join(',').split(path.sep).join('-'), + mtime + ].join('$'), }); this._resolver = new Resolver({ @@ -365,7 +378,7 @@ class Bundler { generateAssetModule(bundle, module, platform = null) { const relPath = getPathRelativeToRoot(this._projectRoots, module.path); var assetUrlPath = path.join('/assets', path.dirname(relPath)); - + // On Windows, change backslashes to slashes to get proper URL path from file path. if (path.sep === '\\') { assetUrlPath = assetUrlPath.replace(/\\/g, '/'); diff --git a/react-packager/src/BundlesLayout/__tests__/BundlesLayout-test.js b/react-packager/src/BundlesLayout/__tests__/BundlesLayout-test.js index bc4fac8a..dafdb368 100644 --- a/react-packager/src/BundlesLayout/__tests__/BundlesLayout-test.js +++ b/react-packager/src/BundlesLayout/__tests__/BundlesLayout-test.js @@ -14,7 +14,7 @@ jest.dontMock('../index') var Promise = require('promise'); var BundlesLayout = require('../index'); var Resolver = require('../../Resolver'); -var loadCacheSync = require('../../lib/loadCacheSync'); +var loadCacheSync = require('../../DependencyResolver/Cache/lib/loadCacheSync'); describe('BundlesLayout', () => { function newBundlesLayout(options) { diff --git a/react-packager/src/BundlesLayout/__tests__/BundlesLayoutIntegration-test.js b/react-packager/src/BundlesLayout/__tests__/BundlesLayoutIntegration-test.js index 1cade30c..37701392 100644 --- a/react-packager/src/BundlesLayout/__tests__/BundlesLayoutIntegration-test.js +++ b/react-packager/src/BundlesLayout/__tests__/BundlesLayoutIntegration-test.js @@ -10,7 +10,7 @@ jest .autoMockOff() - .mock('../../Cache') + .mock('../../DependencyResolver/Cache') .mock('../../Activity'); const Promise = require('promise'); @@ -19,7 +19,7 @@ const path = require('path'); jest.mock('fs'); var BundlesLayout = require('../index'); -var Cache = require('../../Cache'); +var Cache = require('../../DependencyResolver/Cache'); var Resolver = require('../../Resolver'); var fs = require('fs'); diff --git a/react-packager/src/BundlesLayout/index.js b/react-packager/src/BundlesLayout/index.js index d946cefe..2fd90623 100644 --- a/react-packager/src/BundlesLayout/index.js +++ b/react-packager/src/BundlesLayout/index.js @@ -13,10 +13,11 @@ const Activity = require('../Activity'); const _ = require('underscore'); const declareOpts = require('../lib/declareOpts'); const fs = require('fs'); -const getCacheFilePath = require('../lib/getCacheFilePath'); -const loadCacheSync = require('../lib/loadCacheSync'); +const getCacheFilePath = require('../DependencyResolver/Cache/lib/getCacheFilePath'); +const loadCacheSync = require('../DependencyResolver/Cache/lib/loadCacheSync'); const version = require('../../../../package.json').version; const path = require('path'); +const tmpdir = require('os').tmpDir(); const validateOpts = declareOpts({ dependencyResolver: { @@ -189,6 +190,7 @@ class BundlesLayout { _getCacheFilePath(options) { return getCacheFilePath( + tmpdir, 'react-packager-bundles-cache-', version, options.projectRoots.join(',').split(path.sep).join('-'), diff --git a/react-packager/src/Cache/__mocks__/index.js b/react-packager/src/DependencyResolver/Cache/__mocks__/index.js similarity index 100% rename from react-packager/src/Cache/__mocks__/index.js rename to react-packager/src/DependencyResolver/Cache/__mocks__/index.js diff --git a/react-packager/src/Cache/__tests__/Cache-test.js b/react-packager/src/DependencyResolver/Cache/__tests__/Cache-test.js similarity index 80% rename from react-packager/src/Cache/__tests__/Cache-test.js rename to react-packager/src/DependencyResolver/Cache/__tests__/Cache-test.js index ee06dd22..62220f6b 100644 --- a/react-packager/src/Cache/__tests__/Cache-test.js +++ b/react-packager/src/DependencyResolver/Cache/__tests__/Cache-test.js @@ -9,38 +9,35 @@ 'use strict'; jest - .dontMock('underscore') .dontMock('absolute-path') .dontMock('../') - .dontMock('../../lib/loadCacheSync') - .dontMock('../../lib/getCacheFilePath'); + .dontMock('../lib/loadCacheSync') + .dontMock('../lib/getCacheFilePath'); jest .mock('fs') .setMock('os', { - tmpDir() { return 'tmpDir'; } + tmpDir() { return 'tmpDir'; }, }); var Promise = require('promise'); var fs = require('fs'); -var _ = require('underscore'); var Cache = require('../'); -describe('JSTransformer Cache', () => { +describe('Cache', () => { describe('getting/setting', () => { pit('calls loader callback for uncached file', () => { fs.stat.mockImpl((file, callback) => { callback(null, { mtime: { - getTime: () => {} - } + getTime: () => {}, + }, }); }); var cache = new Cache({ - projectRoots: ['/rootDir'], - transformModulePath: 'x.js', + cacheKey: 'cache', }); var loaderCb = jest.genMockFn().mockImpl(() => Promise.resolve()); @@ -55,14 +52,13 @@ describe('JSTransformer Cache', () => { fs.stat.mockImpl((file, callback) => { callback(null, { mtime: { - getTime: () => {} - } + getTime: () => {}, + }, }); }); var cache = new Cache({ - projectRoots: ['/rootDir'], - transformModulePath: 'x.js', + cacheKey: 'cache', }); var index = 0; var loaderCb = jest.genMockFn().mockImpl(() => @@ -83,14 +79,13 @@ describe('JSTransformer Cache', () => { fs.stat.mockImpl((file, callback) => callback(null, { mtime: { - getTime: () => {} - } + getTime: () => {}, + }, }) ); var cache = new Cache({ - projectRoots: ['/rootDir'], - transformModulePath: 'x.js', + cacheKey: 'cache', }); var loaderCb = jest.genMockFn().mockImpl(() => Promise.resolve('lol') @@ -105,14 +100,13 @@ describe('JSTransformer Cache', () => { fs.stat.mockImpl((file, callback) => { callback(null, { mtime: { - getTime: () => {} - } + getTime: () => {}, + }, }); }); var cache = new Cache({ - projectRoots: ['/rootDir'], - transformModulePath: 'x.js', + cacheKey: 'cache', }); var loaderCb = jest.genMockFn().mockImpl(() => Promise.resolve('lol') @@ -135,14 +129,13 @@ describe('JSTransformer Cache', () => { fs.stat.mockImpl((file, callback) => { callback(null, { mtime: { - getTime: () => mtime++ - } + getTime: () => mtime++, + }, }); }); var cache = new Cache({ - projectRoots: ['/rootDir'], - transformModulePath: 'x.js', + cacheKey: 'cache', }); var loaderCb = jest.genMockFn().mockImpl(() => Promise.resolve('lol' + mtime) @@ -167,14 +160,14 @@ describe('JSTransformer Cache', () => { fileStats = { '/rootDir/someFile': { mtime: { - getTime: () => 22 - } + getTime: () => 22, + }, }, '/rootDir/foo': { mtime: { - getTime: () => 11 - } - } + getTime: () => 11, + }, + }, }; fs.existsSync.mockImpl(() => true); @@ -189,14 +182,13 @@ describe('JSTransformer Cache', () => { '/rootDir/foo': { metadata: {mtime: 11}, data: {field: 'lol wat'}, - } + }, })); }); pit('should load cache from disk', () => { var cache = new Cache({ - projectRoots: ['/rootDir'], - transformModulePath: 'x.js', + cacheKey: 'cache', }); var loaderCb = jest.genMockFn(); @@ -219,16 +211,15 @@ describe('JSTransformer Cache', () => { fs.stat.mockImpl((file, callback) => callback(null, { mtime: { - getTime: () => {} - } + getTime: () => {}, + }, }) ); fileStats['/rootDir/foo'].mtime.getTime = () => 123; var cache = new Cache({ - projectRoots: ['/rootDir'], - transformModulePath: 'x.js', + cacheKey: 'cache', }); var loaderCb = jest.genMockFn().mockImpl(() => Promise.resolve('new value') @@ -254,26 +245,17 @@ describe('JSTransformer Cache', () => { it('should write cache to disk', () => { var index = 0; var mtimes = [10, 20, 30]; - var debounceIndex = 0; - _.debounce = callback => { - return () => { - if (++debounceIndex === 3) { - callback(); - } - }; - }; fs.stat.mockImpl((file, callback) => callback(null, { mtime: { - getTime: () => mtimes[index++] - } + getTime: () => mtimes[index++], + }, }) ); var cache = new Cache({ - projectRoots: ['/rootDir'], - transformModulePath: 'x.js', + cacheKey: 'cache', }); cache.get('/rootDir/bar', 'field', () => @@ -286,7 +268,10 @@ describe('JSTransformer Cache', () => { Promise.resolve('baz value') ); - jest.runAllTicks(); + // jest has some trouble with promises and timeouts within promises :( + jest.runAllTimers(); + jest.runAllTimers(); + expect(fs.writeFile).toBeCalled(); }); }); diff --git a/react-packager/src/Cache/index.js b/react-packager/src/DependencyResolver/Cache/index.js similarity index 73% rename from react-packager/src/Cache/index.js rename to react-packager/src/DependencyResolver/Cache/index.js index cbd9e215..8552e0dc 100644 --- a/react-packager/src/Cache/index.js +++ b/react-packager/src/DependencyResolver/Cache/index.js @@ -9,50 +9,37 @@ 'use strict'; const Promise = require('promise'); -const _ = require('underscore'); -const declareOpts = require('../lib/declareOpts'); const fs = require('fs'); -const getCacheFilePath = require('../lib/getCacheFilePath'); +const getCacheFilePath = require('./lib/getCacheFilePath'); const isAbsolutePath = require('absolute-path'); -const loadCacheSync = require('../lib/loadCacheSync'); -const path = require('path'); -const version = require('../../../../package.json').version; +const loadCacheSync = require('./lib/loadCacheSync'); +const tmpdir = require('os').tmpDir(); -const validateOpts = declareOpts({ - resetCache: { - type: 'boolean', - default: false, - }, - cacheVersion: { - type: 'string', - default: '1.0', - }, - projectRoots: { - type: 'array', - required: true, - }, - transformModulePath: { - type:'string', - required: true, - }, -}); +function getObjectValues(object) { + return Object.keys(object).map(key => object[key]); +} + +function debounce(fn, delay) { + var timeout; + return () => { + clearTimeout(timeout); + timeout = setTimeout(fn, delay); + }; +} -// 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); + constructor({ + resetCache, + cacheKey, + }) { + this._cacheFilePath = getCacheFilePath(tmpdir, cacheKey); + if (!resetCache) { + this._data = this._loadCacheSync(this._cacheFilePath); } else { - data = Object.create(null); + this._data = Object.create(null); } - this._data = data; - this._persistEventually = _.debounce( + this._persistEventually = debounce( this._persistCache.bind(this), 2000, ); @@ -124,10 +111,10 @@ class Cache { var data = this._data; var cacheFilepath = this._cacheFilePath; - var allPromises = _.values(data) + var allPromises = getObjectValues(data) .map(record => { var fieldNames = Object.keys(record.data); - var fieldValues = _.values(record.data); + var fieldValues = getObjectValues(record.data); return Promise .all(fieldValues) @@ -187,25 +174,6 @@ class Cache { return ret; } - - _getCacheFilePath(options) { - let mtime; - try { - ({mtime} = fs.statSync(options.transformModulePath)); - mtime = String(mtime.getTime()); - } catch (error) { - mtime = ''; - } - - return getCacheFilePath( - 'react-packager-cache-', - version, - options.projectRoots.join(',').split(path.sep).join('-'), - options.cacheVersion || '0', - options.transformModulePath, - mtime - ); - } } module.exports = Cache; diff --git a/react-packager/src/lib/getCacheFilePath.js b/react-packager/src/DependencyResolver/Cache/lib/getCacheFilePath.js similarity index 65% rename from react-packager/src/lib/getCacheFilePath.js rename to react-packager/src/DependencyResolver/Cache/lib/getCacheFilePath.js index 1d858529..3975b65a 100644 --- a/react-packager/src/lib/getCacheFilePath.js +++ b/react-packager/src/DependencyResolver/Cache/lib/getCacheFilePath.js @@ -10,16 +10,11 @@ const crypto = require('crypto'); const path = require('path'); -const tmpdir = require('os').tmpDir(); -function getCacheFilePath(...args) { - args = Array.prototype.slice.call(args); - const prefix = args.shift(); - - let hash = crypto.createHash('md5'); +function getCacheFilePath(tmpdir, ...args) { + const hash = crypto.createHash('md5'); args.forEach(arg => hash.update(arg)); - - return path.join(tmpdir, prefix + hash.digest('hex')); + return path.join(tmpdir, hash.digest('hex')); } module.exports = getCacheFilePath; diff --git a/react-packager/src/lib/loadCacheSync.js b/react-packager/src/DependencyResolver/Cache/lib/loadCacheSync.js similarity index 100% rename from react-packager/src/lib/loadCacheSync.js rename to react-packager/src/DependencyResolver/Cache/lib/loadCacheSync.js diff --git a/react-packager/src/JSTransformer/__tests__/Transformer-test.js b/react-packager/src/JSTransformer/__tests__/Transformer-test.js index b182e1eb..83275f14 100644 --- a/react-packager/src/JSTransformer/__tests__/Transformer-test.js +++ b/react-packager/src/JSTransformer/__tests__/Transformer-test.js @@ -14,7 +14,7 @@ jest jest.mock('fs'); -var Cache = require('../../Cache'); +var Cache = require('../../DependencyResolver/Cache'); var Transformer = require('../'); var fs = require('fs');