Pass cacheStores

Reviewed By: davidaurelio

Differential Revision: D7023425

fbshipit-source-id: 84d0d6585af4075bd7ac11ac3c97520caeae7b01
This commit is contained in:
Miguel Jimenez Esun 2018-02-23 03:49:06 -08:00 committed by Facebook Github Bot
parent eab7030b2e
commit 7486444a0a
9 changed files with 79 additions and 22 deletions

View File

@ -12,14 +12,14 @@
import type {CacheStore} from 'metro-cache'; import type {CacheStore} from 'metro-cache';
class Cache { class Cache<T> {
_stores: $ReadOnlyArray<CacheStore>; _stores: $ReadOnlyArray<CacheStore<T>>;
constructor(stores: $ReadOnlyArray<CacheStore>) { constructor(stores: $ReadOnlyArray<CacheStore<T>>) {
this._stores = stores; this._stores = stores;
} }
async get(key: Buffer): Promise<mixed> { async get(key: Buffer): Promise<?T> {
const stores = this._stores; const stores = this._stores;
const length = stores.length; const length = stores.length;
@ -38,7 +38,7 @@ class Cache {
return null; return null;
} }
set(key: Buffer, value: mixed): void { set(key: Buffer, value: T): void {
Promise.all(this._stores.map(store => store.set(key, value))).catch(err => { Promise.all(this._stores.map(store => store.set(key, value))).catch(err => {
process.nextTick(() => { process.nextTick(() => {
throw err; throw err;

View File

@ -52,6 +52,8 @@ describe('Cache', () => {
}); });
it('awaits for promises on stores, even if they return undefined', async () => { it('awaits for promises on stores, even if they return undefined', async () => {
jest.useFakeTimers();
let resolve; let resolve;
const store1 = createStore(); const store1 = createStore();
@ -67,7 +69,9 @@ describe('Cache', () => {
expect(store2.get).not.toHaveBeenCalled(); expect(store2.get).not.toHaveBeenCalled();
resolve(undefined); resolve(undefined);
await promise; await promise;
jest.runAllTimers();
expect(store1.get).toHaveBeenCalledTimes(1); expect(store1.get).toHaveBeenCalledTimes(1);
expect(store2.get).toHaveBeenCalledTimes(1); expect(store2.get).toHaveBeenCalledTimes(1);

View File

@ -12,6 +12,9 @@
const Cache = require('./Cache'); const Cache = require('./Cache');
const stableHash = require('./stableHash');
export type {CacheStore} from './types.flow'; export type {CacheStore} from './types.flow';
module.exports.Cache = Cache; module.exports.Cache = Cache;
module.exports.stableHash = stableHash;

View File

@ -10,7 +10,7 @@
'use strict'; 'use strict';
export type CacheStore = { export type CacheStore<T> = {
get(key: Buffer): ?mixed | Promise<?mixed>, get(key: Buffer): ?T | Promise<?T>,
set(key: Buffer, value: mixed): void | Promise<void>, set(key: Buffer, value: T): void | Promise<void>,
}; };

View File

@ -34,6 +34,7 @@ var commonOptions = {
allowBundleUpdates: false, allowBundleUpdates: false,
assetExts: defaults.assetExts, assetExts: defaults.assetExts,
assetRegistryPath: '/AssetRegistry.js', assetRegistryPath: '/AssetRegistry.js',
cacheStores: [],
cacheVersion: 'smth', cacheVersion: 'smth',
enableBabelRCLookup: true, enableBabelRCLookup: true,
extraNodeModules: {}, extraNodeModules: {},

View File

@ -18,6 +18,7 @@ const defaults = require('../defaults');
const fs = require('fs'); const fs = require('fs');
const getTransformCacheKeyFn = require('../lib/getTransformCacheKeyFn'); const getTransformCacheKeyFn = require('../lib/getTransformCacheKeyFn');
const {Cache, stableHash} = require('metro-cache');
const { const {
toSegmentTuple, toSegmentTuple,
fromRawMappings, fromRawMappings,
@ -25,12 +26,16 @@ const {
} = require('metro-source-map'); } = require('metro-source-map');
import type {PostProcessModules} from '../DeltaBundler'; import type {PostProcessModules} from '../DeltaBundler';
import type {Options as JSTransformerOptions} from '../JSTransformer/worker'; import type {
Options as JSTransformerOptions,
TransformedCode,
} from '../JSTransformer/worker';
import type {DynamicRequiresBehavior} from '../ModuleGraph/worker/collectDependencies'; import type {DynamicRequiresBehavior} from '../ModuleGraph/worker/collectDependencies';
import type {GlobalTransformCache} from '../lib/GlobalTransformCache'; import type {GlobalTransformCache} from '../lib/GlobalTransformCache';
import type {TransformCache} from '../lib/TransformCaching'; import type {TransformCache} from '../lib/TransformCaching';
import type {Reporter} from '../lib/reporting'; import type {Reporter} from '../lib/reporting';
import type {BabelSourceMap} from '@babel/core'; import type {BabelSourceMap} from '@babel/core';
import type {CacheStore} from 'metro-cache';
import type { import type {
MetroSourceMapSegmentTuple, MetroSourceMapSegmentTuple,
MetroSourceMap, MetroSourceMap,
@ -80,6 +85,7 @@ export type Options = {|
+assetRegistryPath: string, +assetRegistryPath: string,
+asyncRequireModulePath: string, +asyncRequireModulePath: string,
+blacklistRE?: RegExp, +blacklistRE?: RegExp,
+cacheStores: $ReadOnlyArray<CacheStore<TransformedCode>>,
+cacheVersion: string, +cacheVersion: string,
+dynamicDepsInPackages: DynamicRequiresBehavior, +dynamicDepsInPackages: DynamicRequiresBehavior,
+enableBabelRCLookup: boolean, +enableBabelRCLookup: boolean,
@ -108,16 +114,18 @@ export type Options = {|
class Bundler { class Bundler {
_opts: Options; _opts: Options;
_cache: ?Cache<TransformedCode>;
_transformer: Transformer; _transformer: Transformer;
_depGraphPromise: Promise<DependencyGraph>; _depGraphPromise: Promise<DependencyGraph>;
_projectRoots: $ReadOnlyArray<string>; _projectRoots: $ReadOnlyArray<string>;
_getTransformOptions: void | GetTransformOptions; _getTransformOptions: void | GetTransformOptions;
constructor(opts: Options) { constructor(opts: Options) {
this._opts = opts;
opts.projectRoots.forEach(verifyRootExists); opts.projectRoots.forEach(verifyRootExists);
this._opts = opts;
this._cache = opts.cacheStores.length ? new Cache(opts.cacheStores) : null;
this._transformer = new Transformer({ this._transformer = new Transformer({
asyncRequireModulePath: opts.asyncRequireModulePath, asyncRequireModulePath: opts.asyncRequireModulePath,
maxWorkers: opts.maxWorkers, maxWorkers: opts.maxWorkers,
@ -157,16 +165,7 @@ class Bundler {
reporter: opts.reporter, reporter: opts.reporter,
resetCache: opts.resetCache, resetCache: opts.resetCache,
sourceExts: opts.sourceExts, sourceExts: opts.sourceExts,
transformCode: (module, code, transformCodeOptions) => transformCode: this._cachedTransformCode.bind(this),
this._transformer.transform(
module.path,
module.localPath,
code,
module.isPolyfill(),
transformCodeOptions,
this._opts.assetExts,
this._opts.assetRegistryPath,
),
transformCache: opts.transformCache, transformCache: opts.transformCache,
watch: opts.watch, watch: opts.watch,
}); });
@ -276,6 +275,50 @@ class Bundler {
map: result.map ? toBabelSegments(result.map).map(toSegmentTuple) : [], map: result.map ? toBabelSegments(result.map).map(toSegmentTuple) : [],
}; };
} }
async _cachedTransformCode(
module,
code,
transformCodeOptions,
): Promise<TransformedCode> {
const cache = this._cache;
let result;
let key;
// First, try getting the result from the cache if enabled.
if (cache) {
key = stableHash([
module.localPath,
code,
transformCodeOptions,
this._opts.assetExts,
this._opts.assetRegistryPath,
this._opts.cacheVersion,
]);
result = await cache.get(key);
}
// Second, if there was no result, compute it ourselves.
if (!result) {
result = await this._transformer.transform(
module.path,
module.localPath,
code,
module.isPolyfill(),
transformCodeOptions,
this._opts.assetExts,
this._opts.assetRegistryPath,
);
}
// Third, propagate the result to all cache layers.
if (key && cache) {
cache.set(key, result);
}
return result;
}
} }
function verifyRootExists(root) { function verifyRootExists(root) {

View File

@ -20,6 +20,7 @@ import type {
PostProcessBundleSourcemap, PostProcessBundleSourcemap,
} from './Bundler'; } from './Bundler';
import type {PostProcessModules} from './DeltaBundler'; import type {PostProcessModules} from './DeltaBundler';
import type {TransformedCode} from './JSTransformer/worker';
import type {DynamicRequiresBehavior} from './ModuleGraph/worker/collectDependencies'; import type {DynamicRequiresBehavior} from './ModuleGraph/worker/collectDependencies';
import type {IncomingMessage, ServerResponse} from 'http'; import type {IncomingMessage, ServerResponse} from 'http';
import type {CacheStore} from 'metro-cache'; import type {CacheStore} from 'metro-cache';
@ -35,7 +36,7 @@ export type ConfigT = {
/** /**
* List of all store caches. * List of all store caches.
*/ */
cacheStores: Array<CacheStore>, cacheStores: Array<CacheStore<TransformedCode>>,
/** /**
* Can be used to generate a key that will invalidate the whole metro cache * Can be used to generate a key that will invalidate the whole metro cache

View File

@ -30,6 +30,7 @@ const defaults = require('../../defaults');
const bundlerOptions = { const bundlerOptions = {
allowBundleUpdates: false, allowBundleUpdates: false,
assetExts: defaults.assetExts, assetExts: defaults.assetExts,
cacheStores: [],
cacheVersion: 'smth', cacheVersion: 'smth',
enableBabelRCLookup: true, enableBabelRCLookup: true,
extraNodeModules: {}, extraNodeModules: {},

View File

@ -40,12 +40,14 @@ import type {
PostMinifyProcess, PostMinifyProcess,
PostProcessBundleSourcemap, PostProcessBundleSourcemap,
} from '../Bundler'; } from '../Bundler';
import type {CacheStore} from 'metro-cache';
import type {MetroSourceMap} from 'metro-source-map'; import type {MetroSourceMap} from 'metro-source-map';
import type {TransformCache} from '../lib/TransformCaching'; import type {TransformCache} from '../lib/TransformCaching';
import type {Symbolicate} from './symbolicate'; import type {Symbolicate} from './symbolicate';
import type {AssetData} from '../Assets'; import type {AssetData} from '../Assets';
import type {RamBundleInfo} from '../DeltaBundler/Serializers'; import type {RamBundleInfo} from '../DeltaBundler/Serializers';
import type {PostProcessModules} from '../DeltaBundler'; import type {PostProcessModules} from '../DeltaBundler';
import type {TransformedCode} from '../JSTransformer/worker';
const { const {
Logger: {createActionStartEntry, createActionEndEntry, log}, Logger: {createActionStartEntry, createActionEndEntry, log},
} = require('metro-core'); } = require('metro-core');
@ -66,6 +68,7 @@ class Server {
_opts: { _opts: {
assetExts: Array<string>, assetExts: Array<string>,
blacklistRE: void | RegExp, blacklistRE: void | RegExp,
cacheStores: $ReadOnlyArray<CacheStore<TransformedCode>>,
cacheVersion: string, cacheVersion: string,
createModuleIdFactory?: () => (path: string) => number, createModuleIdFactory?: () => (path: string) => number,
enableBabelRCLookup: boolean, enableBabelRCLookup: boolean,
@ -117,6 +120,7 @@ class Server {
assetExts: options.assetTransforms ? [] : assetExts, assetExts: options.assetTransforms ? [] : assetExts,
assetRegistryPath: options.assetRegistryPath, assetRegistryPath: options.assetRegistryPath,
blacklistRE: options.blacklistRE, blacklistRE: options.blacklistRE,
cacheStores: options.cacheStores || [],
cacheVersion: options.cacheVersion, cacheVersion: options.cacheVersion,
dynamicDepsInPackages: options.dynamicDepsInPackages || 'throwAtRuntime', dynamicDepsInPackages: options.dynamicDepsInPackages || 'throwAtRuntime',
createModuleIdFactory: options.createModuleIdFactory, createModuleIdFactory: options.createModuleIdFactory,