packager: TransformCache: extract choice of root path outside of class

Summary: This is a first step towards doing an experiment I was talking about, that is, using the local directory (the directory where the config file lives) instead of the tmp dir, that is fragile.

Reviewed By: davidaurelio

Differential Revision: D5112326

fbshipit-source-id: 819636209972115e41213867f3eb78fbdf6ed9df
This commit is contained in:
Jean Lauliac 2017-05-24 07:42:28 -07:00 committed by Facebook Github Bot
parent f46eaa30cf
commit 0b4e772e3b
4 changed files with 71 additions and 66 deletions

View File

@ -34,6 +34,7 @@ var defaults = require('../../../defaults');
var sizeOf = require('image-size');
var fs = require('fs');
const os = require('os');
const path = require('path');
const {any, objectContaining} = expect;
@ -85,6 +86,9 @@ describe('Bundler', function() {
beforeEach(function() {
os.cpus.mockReturnValue({length: 1});
// local directory on purpose, because it should not actually write
// anything to the disk during a unit test!
os.tmpDir.mockReturnValue(path.join(__dirname));
getDependencies = jest.fn();
getModuleSystemDependencies = jest.fn();

View File

@ -15,6 +15,7 @@
const crypto = require('crypto');
const debugRead = require('debug')('RNP:TransformCache:Read');
const fs = require('fs');
const invariant = require('fbjs/lib/invariant');
const mkdirp = require('mkdirp');
const path = require('path');
const rimraf = require('rimraf');
@ -29,7 +30,6 @@ import type {LocalPath} from '../node-haste/lib/toLocalPath';
type CacheFilePaths = {transformedCode: string, metadata: string};
export type GetTransformCacheKey = (options: {}) => string;
const CACHE_NAME = 'react-native-packager-cache';
const CACHE_SUB_DIR = 'cache';
export type CachedResult = {
@ -68,11 +68,25 @@ const CACHE_FILE_MAX_LAST_ACCESS_TIME = GARBAGE_COLLECTION_PERIOD * 4;
class TransformCache {
_cacheWasReset: boolean;
_dirPath: string;
_lastCollected: ?number;
_rootPath: string;
constructor() {
/**
* The root path is where the data will be stored. It shouldn't contain
* other files other than the cache's own files, so it should start empty
* when the packager is first run. When doing a cache reset, it may be
* completely deleted.
*/
constructor(rootPath: string) {
this._cacheWasReset = false;
invariant(
path.isAbsolute(rootPath),
'root path of the transform cache must be absolute',
);
require('debug')('RNP:TransformCache:Dir')(
`transform cache directory: ${rootPath}`,
);
this._rootPath = rootPath;
}
/**
@ -90,16 +104,14 @@ class TransformCache {
* close to each others, one of the workers is going to loose its results no
* matter what.
*/
writeSync(
props: {
filePath: string,
sourceCode: string,
getTransformCacheKey: GetTransformCacheKey,
transformOptions: WorkerOptions,
transformOptionsKey: string,
result: CachedResult,
},
): void {
writeSync(props: {
filePath: string,
sourceCode: string,
getTransformCacheKey: GetTransformCacheKey,
transformOptions: WorkerOptions,
transformOptionsKey: string,
result: CachedResult,
}): void {
const cacheFilePath = this._getCacheFilePaths(props);
mkdirp.sync(path.dirname(cacheFilePath.transformedCode));
const {result} = props;
@ -202,7 +214,7 @@ class TransformCache {
}
_resetCache(reporter: Reporter) {
rimraf.sync(this._getCacheDirPath());
rimraf.sync(this._rootPath);
reporter.update({type: 'transform_cache_reset'});
this._cacheWasReset = true;
this._lastCollected = Date.now();
@ -220,7 +232,7 @@ class TransformCache {
terminal.log(
'Error: Cleaning up the cache folder failed. Continuing anyway.',
);
terminal.log('The cache folder is: %s', this._getCacheDirPath());
terminal.log('The cache folder is: %s', this._rootPath);
}
this._lastCollected = Date.now();
}
@ -231,7 +243,7 @@ class TransformCache {
* first.
*/
_collectCacheIfOldSync() {
const cacheDirPath = this._getCacheDirPath();
const cacheDirPath = this._rootPath;
mkdirp.sync(cacheDirPath);
const cacheCollectionFilePath = path.join(cacheDirPath, 'last_collected');
const lastCollected = Number.parseInt(
@ -255,12 +267,10 @@ class TransformCache {
* account because it would generate lots of file during development. (The
* source hash is stored in the metadata instead).
*/
_getCacheFilePaths(
props: {
filePath: string,
transformOptionsKey: string,
},
): CacheFilePaths {
_getCacheFilePaths(props: {
filePath: string,
transformOptionsKey: string,
}): CacheFilePaths {
const hasher = crypto
.createHash('sha1')
.update(props.filePath)
@ -268,38 +278,9 @@ class TransformCache {
const hash = hasher.digest('hex');
const prefix = hash.substr(0, 2);
const fileName = `${hash.substr(2)}`;
const base = path.join(
this._getCacheDirPath(),
CACHE_SUB_DIR,
prefix,
fileName,
);
const base = path.join(this._rootPath, CACHE_SUB_DIR, prefix, fileName);
return {transformedCode: base, metadata: base + '.meta'};
}
/**
* If packager is running for two different directories, we don't want the
* caches to conflict with each other. `__dirname` carries that because
* packager will be, for example, installed in a different `node_modules/`
* folder for different projects.
*/
_getCacheDirPath() {
if (this._dirPath != null) {
return this._dirPath;
}
const hash = crypto.createHash('sha1').update(__dirname);
if (process.getuid != null) {
hash.update(process.getuid().toString());
}
this._dirPath = path.join(
require('os').tmpdir(),
CACHE_NAME + '-' + hash.digest('hex'),
);
require('debug')('RNP:TransformCache:Dir')(
`transform cache directory: ${this._dirPath}`,
);
return this._dirPath;
}
}
/**
@ -388,15 +369,13 @@ function tryParseJSON(str: string): any {
}
}
function hashSourceCode(
props: {
filePath: string,
sourceCode: string,
getTransformCacheKey: GetTransformCacheKey,
transformOptions: WorkerOptions,
transformOptionsKey: string,
},
): string {
function hashSourceCode(props: {
filePath: string,
sourceCode: string,
getTransformCacheKey: GetTransformCacheKey,
transformOptions: WorkerOptions,
transformOptionsKey: string,
}): string {
return crypto
.createHash('sha1')
.update(props.getTransformCacheKey(props.transformOptions))

View File

@ -55,7 +55,7 @@ describe('TransformCache', () => {
beforeEach(() => {
jest.resetModules();
mockFS.clear();
transformCache = new (require('../TransformCache'))();
transformCache = new (require('../TransformCache'))('/cache');
});
it('is caching different files and options separately', () => {

View File

@ -20,6 +20,7 @@ const fs = require('fs');
const invariant = require('fbjs/lib/invariant');
const isAbsolutePath = require('absolute-path');
const jsonStableStringify = require('json-stable-stringify');
const path = require('path');
const {join: joinPath, relative: relativePath, extname} = require('path');
@ -85,8 +86,6 @@ export type ConstructorArgs = {
type DocBlock = {+[key: string]: string};
const TRANSFORM_CACHE = new TransformCache();
class Module {
localPath: LocalPath;
path: string;
@ -107,6 +106,8 @@ class Module {
_readResultsByOptionsKey: Map<string, CachedReadResult>;
static _transformCache: TransformCache;
constructor({
depGraphHelpers,
localPath,
@ -333,7 +334,7 @@ class Module {
return;
}
invariant(result != null, 'missing result');
TRANSFORM_CACHE.writeSync({...cacheProps, result});
Module._getTransformCache().writeSync({...cacheProps, result});
callback(undefined, result);
});
}
@ -380,7 +381,7 @@ class Module {
transformOptions,
transformOptionsKey,
);
const cachedResult = TRANSFORM_CACHE.readSync(cacheProps);
const cachedResult = Module._getTransformCache().readSync(cacheProps);
if (cachedResult.result == null) {
return {
result: null,
@ -468,6 +469,27 @@ class Module {
isPolyfill() {
return false;
}
/**
* If packager is running for two different directories, we don't want the
* caches to conflict with each other. `__dirname` carries that because
* packager will be, for example, installed in a different `node_modules/`
* folder for different projects.
*/
static _getTransformCache(): TransformCache {
if (this._transformCache != null) {
return this._transformCache;
}
const hash = crypto.createHash('sha1').update(__dirname);
if (process.getuid != null) {
hash.update(process.getuid().toString());
}
const tmpDir = require('os').tmpdir();
const cacheName = 'react-native-packager-cache';
const rootPath = path.join(tmpDir, cacheName + '-' + hash.digest('hex'));
this._transformCache = new TransformCache(rootPath);
return this._transformCache;
}
}
// use weak map to speed up hash creation of known objects