metro-cache
Advanced tools
Comparing version 0.28.0 to 0.29.0
{ | ||
"version": "0.28.0", | ||
"version": "0.29.0", | ||
"name": "metro-cache", | ||
@@ -4,0 +4,0 @@ "description": "🚇 Cache layers for Metro", |
@@ -9,2 +9,3 @@ /** | ||
* @format | ||
* @flow | ||
*/ | ||
@@ -19,3 +20,2 @@ | ||
return { | ||
name: 'store' + i, | ||
get: jest.fn().mockImplementation(() => null), | ||
@@ -26,2 +26,6 @@ set: jest.fn(), | ||
afterEach(() => { | ||
jest.restoreAllMocks(); | ||
}); | ||
it('returns null when no result is found', async () => { | ||
@@ -33,3 +37,3 @@ const store1 = createStore(); | ||
// Calling a wrapped method. | ||
const result = await cache.get('arg'); | ||
const result = await cache.get(Buffer.from('foo')); | ||
@@ -48,7 +52,7 @@ expect(result).toBe(null); | ||
// Only cache 2 can return results. | ||
store2.get.mockImplementation(() => 'foo'); | ||
store2.get.mockImplementation(() => 'hit!'); | ||
const result = await cache.get('arg'); | ||
const result = await cache.get(Buffer.from('foo')); | ||
expect(result).toBe('foo'); | ||
expect(result).toBe('hit!'); | ||
expect(store1.get).toHaveBeenCalledTimes(1); | ||
@@ -59,5 +63,21 @@ expect(store2.get).toHaveBeenCalledTimes(1); | ||
it('skips all cache stores when a hit is produced, based on the same key', () => { | ||
const store1 = createStore(); | ||
const store2 = createStore(); | ||
const store3 = createStore(); | ||
const cache = new Cache([store1, store2, store3]); | ||
const key = Buffer.from('foo'); | ||
store2.get.mockImplementation(() => 'hit!'); | ||
// Get and set. Set should only affect store 1, not 2 (hit) and 3 (after). | ||
cache.get(key); | ||
cache.set(key); | ||
expect(store1.set).toHaveBeenCalledTimes(1); | ||
expect(store2.set).not.toHaveBeenCalled(); | ||
expect(store3.set).not.toHaveBeenCalled(); | ||
}); | ||
it('awaits for promises on stores, even if they return undefined', async () => { | ||
jest.useFakeTimers(); | ||
let resolve; | ||
@@ -71,3 +91,3 @@ | ||
store1.get.mockImplementation(() => promise); | ||
cache.get('foo'); | ||
const get = cache.get(Buffer.from('foo')); | ||
@@ -78,7 +98,9 @@ // Store 1 returns a promise, so store 2 is not called until it resolves. | ||
if (!resolve) { | ||
throw new Error('Flow needs this'); | ||
} | ||
resolve(undefined); | ||
await Promise.all([promise, get]); | ||
await promise; | ||
jest.runAllTimers(); | ||
expect(store1.get).toHaveBeenCalledTimes(1); | ||
@@ -88,3 +110,3 @@ expect(store2.get).toHaveBeenCalledTimes(1); | ||
it('throws on a buggy store', async () => { | ||
it('throws on a buggy store set', async () => { | ||
jest.useFakeTimers(); | ||
@@ -95,32 +117,34 @@ | ||
const cache = new Cache([store1, store2]); | ||
let error = null; | ||
let err1 = null; | ||
let err2 = null; | ||
store1.set.mockImplementation(() => null); | ||
store2.set.mockImplementation(() => Promise.reject(new RangeError('foo'))); | ||
// Try sets. | ||
store1.set.mockImplementation(() => Promise.reject(new RangeError('foo'))); | ||
store2.set.mockImplementation(() => null); | ||
expect(() => cache.set('arg')).not.toThrow(); // Async throw. | ||
try { | ||
jest.runAllTimers(); // Advancing the timer will make the cache throw. | ||
cache.set(Buffer.from('foo'), 'arg'); | ||
jest.runAllTimers(); | ||
} catch (err) { | ||
err1 = err; | ||
error = err; | ||
} | ||
expect(err1).toBeInstanceOf(RangeError); | ||
expect(error).toBeInstanceOf(RangeError); | ||
}); | ||
// Try gets. | ||
store1.get.mockImplementation(() => Promise.reject(new TypeError('bar'))); | ||
store2.get.mockImplementation(() => null); | ||
it('throws on a buggy store get', async () => { | ||
const store1 = createStore(); | ||
const store2 = createStore(); | ||
const cache = new Cache([store1, store2]); | ||
let error = null; | ||
store1.get.mockImplementation(() => null); | ||
store2.get.mockImplementation(() => Promise.reject(new TypeError('bar'))); | ||
try { | ||
await cache.get('arg'); | ||
await cache.get(Buffer.from('foo')); | ||
} catch (err) { | ||
err2 = err; | ||
error = err; | ||
} | ||
expect(err2).toBeInstanceOf(TypeError); | ||
expect(error).toBeInstanceOf(TypeError); | ||
}); | ||
}); |
@@ -18,3 +18,6 @@ /** | ||
_hits: WeakMap<Buffer, CacheStore<T>>; | ||
constructor(stores: $ReadOnlyArray<CacheStore<T>>) { | ||
this._hits = new WeakMap(); | ||
this._stores = stores; | ||
@@ -35,2 +38,4 @@ } | ||
if (value != null) { | ||
this._hits.set(key, stores[i]); | ||
return value; | ||
@@ -44,3 +49,12 @@ } | ||
set(key: Buffer, value: T): void { | ||
Promise.all(this._stores.map(store => store.set(key, value))).catch(err => { | ||
const stores = this._stores; | ||
const stop = this._hits.get(key); | ||
const length = stores.length; | ||
const promises = []; | ||
for (let i = 0; i < length && stores[i] !== stop; i++) { | ||
promises.push(stores[i].set(key, value)); | ||
} | ||
Promise.all(promises).catch(err => { | ||
process.nextTick(() => { | ||
@@ -47,0 +61,0 @@ throw err; |
@@ -16,4 +16,5 @@ /** | ||
const path = require('path'); | ||
const serializer = require('jest-serializer'); | ||
import type {TransformedCode} from 'metro/src/JSTransformer/worker'; | ||
export type Options = {| | ||
@@ -36,18 +37,16 @@ root: string, | ||
get(key: Buffer): mixed { | ||
get(key: Buffer): ?TransformedCode { | ||
try { | ||
return serializer.readFileSync(this._getFilePath(key)); | ||
return JSON.parse(fs.readFileSync(this._getFilePath(key), 'utf-8')); | ||
} catch (err) { | ||
return null; | ||
if (err.code === 'ENOENT') { | ||
return null; | ||
} | ||
throw err; | ||
} | ||
} | ||
set(key: Buffer, value: mixed): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
const data = serializer.serialize(value); | ||
fs.writeFile(this._getFilePath(key), data, err => { | ||
err ? reject(err) : resolve(); | ||
}); | ||
}); | ||
set(key: Buffer, value: TransformedCode): void { | ||
fs.writeFileSync(this._getFilePath(key), JSON.stringify(value)); | ||
} | ||
@@ -54,0 +53,0 @@ |
@@ -18,3 +18,6 @@ /** | ||
constructor(stores) { | ||
this._hits = new WeakMap(); | ||
this._stores = stores; | ||
@@ -35,2 +38,4 @@ } | ||
if (value != null) { | ||
_this._hits.set(key, stores[i]); | ||
return value; | ||
@@ -44,3 +49,12 @@ } | ||
set(key, value) { | ||
Promise.all(this._stores.map(store => store.set(key, value))).catch(err => { | ||
const stores = this._stores; | ||
const stop = this._hits.get(key); | ||
const length = stores.length; | ||
const promises = []; | ||
for (let i = 0; i < length && stores[i] !== stop; i++) { | ||
promises.push(stores[i].set(key, value)); | ||
} | ||
Promise.all(promises).catch(err => { | ||
process.nextTick(() => { | ||
@@ -47,0 +61,0 @@ throw err; |
@@ -16,3 +16,2 @@ /** | ||
const path = require('path'); | ||
const serializer = require('jest-serializer'); | ||
@@ -23,2 +22,4 @@ | ||
class FileStore { | ||
@@ -39,5 +40,9 @@ | ||
try { | ||
return serializer.readFileSync(this._getFilePath(key)); | ||
return JSON.parse(fs.readFileSync(this._getFilePath(key), 'utf-8')); | ||
} catch (err) { | ||
return null; | ||
if (err.code === 'ENOENT') { | ||
return null; | ||
} | ||
throw err; | ||
} | ||
@@ -47,9 +52,3 @@ } | ||
set(key, value) { | ||
return new Promise((resolve, reject) => { | ||
const data = serializer.serialize(value); | ||
fs.writeFile(this._getFilePath(key), data, err => { | ||
err ? reject(err) : resolve(); | ||
}); | ||
}); | ||
fs.writeFileSync(this._getFilePath(key), JSON.stringify(value)); | ||
} | ||
@@ -56,0 +55,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
24559
639