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:
parent
f46eaa30cf
commit
0b4e772e3b
|
@ -34,6 +34,7 @@ var defaults = require('../../../defaults');
|
||||||
var sizeOf = require('image-size');
|
var sizeOf = require('image-size');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
const {any, objectContaining} = expect;
|
const {any, objectContaining} = expect;
|
||||||
|
|
||||||
|
@ -85,6 +86,9 @@ describe('Bundler', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
os.cpus.mockReturnValue({length: 1});
|
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();
|
getDependencies = jest.fn();
|
||||||
getModuleSystemDependencies = jest.fn();
|
getModuleSystemDependencies = jest.fn();
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const debugRead = require('debug')('RNP:TransformCache:Read');
|
const debugRead = require('debug')('RNP:TransformCache:Read');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const invariant = require('fbjs/lib/invariant');
|
||||||
const mkdirp = require('mkdirp');
|
const mkdirp = require('mkdirp');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const rimraf = require('rimraf');
|
const rimraf = require('rimraf');
|
||||||
|
@ -29,7 +30,6 @@ import type {LocalPath} from '../node-haste/lib/toLocalPath';
|
||||||
type CacheFilePaths = {transformedCode: string, metadata: string};
|
type CacheFilePaths = {transformedCode: string, metadata: string};
|
||||||
export type GetTransformCacheKey = (options: {}) => string;
|
export type GetTransformCacheKey = (options: {}) => string;
|
||||||
|
|
||||||
const CACHE_NAME = 'react-native-packager-cache';
|
|
||||||
const CACHE_SUB_DIR = 'cache';
|
const CACHE_SUB_DIR = 'cache';
|
||||||
|
|
||||||
export type CachedResult = {
|
export type CachedResult = {
|
||||||
|
@ -68,11 +68,25 @@ const CACHE_FILE_MAX_LAST_ACCESS_TIME = GARBAGE_COLLECTION_PERIOD * 4;
|
||||||
|
|
||||||
class TransformCache {
|
class TransformCache {
|
||||||
_cacheWasReset: boolean;
|
_cacheWasReset: boolean;
|
||||||
_dirPath: string;
|
|
||||||
_lastCollected: ?number;
|
_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;
|
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
|
* close to each others, one of the workers is going to loose its results no
|
||||||
* matter what.
|
* matter what.
|
||||||
*/
|
*/
|
||||||
writeSync(
|
writeSync(props: {
|
||||||
props: {
|
filePath: string,
|
||||||
filePath: string,
|
sourceCode: string,
|
||||||
sourceCode: string,
|
getTransformCacheKey: GetTransformCacheKey,
|
||||||
getTransformCacheKey: GetTransformCacheKey,
|
transformOptions: WorkerOptions,
|
||||||
transformOptions: WorkerOptions,
|
transformOptionsKey: string,
|
||||||
transformOptionsKey: string,
|
result: CachedResult,
|
||||||
result: CachedResult,
|
}): void {
|
||||||
},
|
|
||||||
): void {
|
|
||||||
const cacheFilePath = this._getCacheFilePaths(props);
|
const cacheFilePath = this._getCacheFilePaths(props);
|
||||||
mkdirp.sync(path.dirname(cacheFilePath.transformedCode));
|
mkdirp.sync(path.dirname(cacheFilePath.transformedCode));
|
||||||
const {result} = props;
|
const {result} = props;
|
||||||
|
@ -202,7 +214,7 @@ class TransformCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
_resetCache(reporter: Reporter) {
|
_resetCache(reporter: Reporter) {
|
||||||
rimraf.sync(this._getCacheDirPath());
|
rimraf.sync(this._rootPath);
|
||||||
reporter.update({type: 'transform_cache_reset'});
|
reporter.update({type: 'transform_cache_reset'});
|
||||||
this._cacheWasReset = true;
|
this._cacheWasReset = true;
|
||||||
this._lastCollected = Date.now();
|
this._lastCollected = Date.now();
|
||||||
|
@ -220,7 +232,7 @@ class TransformCache {
|
||||||
terminal.log(
|
terminal.log(
|
||||||
'Error: Cleaning up the cache folder failed. Continuing anyway.',
|
'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();
|
this._lastCollected = Date.now();
|
||||||
}
|
}
|
||||||
|
@ -231,7 +243,7 @@ class TransformCache {
|
||||||
* first.
|
* first.
|
||||||
*/
|
*/
|
||||||
_collectCacheIfOldSync() {
|
_collectCacheIfOldSync() {
|
||||||
const cacheDirPath = this._getCacheDirPath();
|
const cacheDirPath = this._rootPath;
|
||||||
mkdirp.sync(cacheDirPath);
|
mkdirp.sync(cacheDirPath);
|
||||||
const cacheCollectionFilePath = path.join(cacheDirPath, 'last_collected');
|
const cacheCollectionFilePath = path.join(cacheDirPath, 'last_collected');
|
||||||
const lastCollected = Number.parseInt(
|
const lastCollected = Number.parseInt(
|
||||||
|
@ -255,12 +267,10 @@ class TransformCache {
|
||||||
* account because it would generate lots of file during development. (The
|
* account because it would generate lots of file during development. (The
|
||||||
* source hash is stored in the metadata instead).
|
* source hash is stored in the metadata instead).
|
||||||
*/
|
*/
|
||||||
_getCacheFilePaths(
|
_getCacheFilePaths(props: {
|
||||||
props: {
|
filePath: string,
|
||||||
filePath: string,
|
transformOptionsKey: string,
|
||||||
transformOptionsKey: string,
|
}): CacheFilePaths {
|
||||||
},
|
|
||||||
): CacheFilePaths {
|
|
||||||
const hasher = crypto
|
const hasher = crypto
|
||||||
.createHash('sha1')
|
.createHash('sha1')
|
||||||
.update(props.filePath)
|
.update(props.filePath)
|
||||||
|
@ -268,38 +278,9 @@ class TransformCache {
|
||||||
const hash = hasher.digest('hex');
|
const hash = hasher.digest('hex');
|
||||||
const prefix = hash.substr(0, 2);
|
const prefix = hash.substr(0, 2);
|
||||||
const fileName = `${hash.substr(2)}`;
|
const fileName = `${hash.substr(2)}`;
|
||||||
const base = path.join(
|
const base = path.join(this._rootPath, CACHE_SUB_DIR, prefix, fileName);
|
||||||
this._getCacheDirPath(),
|
|
||||||
CACHE_SUB_DIR,
|
|
||||||
prefix,
|
|
||||||
fileName,
|
|
||||||
);
|
|
||||||
return {transformedCode: base, metadata: base + '.meta'};
|
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(
|
function hashSourceCode(props: {
|
||||||
props: {
|
filePath: string,
|
||||||
filePath: string,
|
sourceCode: string,
|
||||||
sourceCode: string,
|
getTransformCacheKey: GetTransformCacheKey,
|
||||||
getTransformCacheKey: GetTransformCacheKey,
|
transformOptions: WorkerOptions,
|
||||||
transformOptions: WorkerOptions,
|
transformOptionsKey: string,
|
||||||
transformOptionsKey: string,
|
}): string {
|
||||||
},
|
|
||||||
): string {
|
|
||||||
return crypto
|
return crypto
|
||||||
.createHash('sha1')
|
.createHash('sha1')
|
||||||
.update(props.getTransformCacheKey(props.transformOptions))
|
.update(props.getTransformCacheKey(props.transformOptions))
|
||||||
|
|
|
@ -55,7 +55,7 @@ describe('TransformCache', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.resetModules();
|
jest.resetModules();
|
||||||
mockFS.clear();
|
mockFS.clear();
|
||||||
transformCache = new (require('../TransformCache'))();
|
transformCache = new (require('../TransformCache'))('/cache');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('is caching different files and options separately', () => {
|
it('is caching different files and options separately', () => {
|
||||||
|
|
|
@ -20,6 +20,7 @@ const fs = require('fs');
|
||||||
const invariant = require('fbjs/lib/invariant');
|
const invariant = require('fbjs/lib/invariant');
|
||||||
const isAbsolutePath = require('absolute-path');
|
const isAbsolutePath = require('absolute-path');
|
||||||
const jsonStableStringify = require('json-stable-stringify');
|
const jsonStableStringify = require('json-stable-stringify');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
const {join: joinPath, relative: relativePath, extname} = require('path');
|
const {join: joinPath, relative: relativePath, extname} = require('path');
|
||||||
|
|
||||||
|
@ -85,8 +86,6 @@ export type ConstructorArgs = {
|
||||||
|
|
||||||
type DocBlock = {+[key: string]: string};
|
type DocBlock = {+[key: string]: string};
|
||||||
|
|
||||||
const TRANSFORM_CACHE = new TransformCache();
|
|
||||||
|
|
||||||
class Module {
|
class Module {
|
||||||
localPath: LocalPath;
|
localPath: LocalPath;
|
||||||
path: string;
|
path: string;
|
||||||
|
@ -107,6 +106,8 @@ class Module {
|
||||||
|
|
||||||
_readResultsByOptionsKey: Map<string, CachedReadResult>;
|
_readResultsByOptionsKey: Map<string, CachedReadResult>;
|
||||||
|
|
||||||
|
static _transformCache: TransformCache;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
depGraphHelpers,
|
depGraphHelpers,
|
||||||
localPath,
|
localPath,
|
||||||
|
@ -333,7 +334,7 @@ class Module {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
invariant(result != null, 'missing result');
|
invariant(result != null, 'missing result');
|
||||||
TRANSFORM_CACHE.writeSync({...cacheProps, result});
|
Module._getTransformCache().writeSync({...cacheProps, result});
|
||||||
callback(undefined, result);
|
callback(undefined, result);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -380,7 +381,7 @@ class Module {
|
||||||
transformOptions,
|
transformOptions,
|
||||||
transformOptionsKey,
|
transformOptionsKey,
|
||||||
);
|
);
|
||||||
const cachedResult = TRANSFORM_CACHE.readSync(cacheProps);
|
const cachedResult = Module._getTransformCache().readSync(cacheProps);
|
||||||
if (cachedResult.result == null) {
|
if (cachedResult.result == null) {
|
||||||
return {
|
return {
|
||||||
result: null,
|
result: null,
|
||||||
|
@ -468,6 +469,27 @@ class Module {
|
||||||
isPolyfill() {
|
isPolyfill() {
|
||||||
return false;
|
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
|
// use weak map to speed up hash creation of known objects
|
||||||
|
|
Loading…
Reference in New Issue