Use local paths for global cache

Summary:
Changes the global cache to use *local paths* rather than base names of files for the cache key. This is to enable correct usage of babel transforms like `transform-react-jsx-source` that use file paths in their output.

It remains a responsibility of the transform implementer to pass relative paths to babel if the global cache is being used.

Reviewed By: jeanlauliac

Differential Revision: D5044028

fbshipit-source-id: 2ef1e1545e510a18ab49a307053d91b4f40269b2
This commit is contained in:
David Aurelio 2017-05-11 16:37:06 -07:00 committed by Facebook Github Bot
parent 497d3ff228
commit 6ecfa15780
10 changed files with 84 additions and 35 deletions

View File

@ -26,13 +26,13 @@ const denodeify = require('denodeify');
const defaults = require('../../defaults'); const defaults = require('../../defaults');
const os = require('os'); const os = require('os');
const invariant = require('fbjs/lib/invariant'); const invariant = require('fbjs/lib/invariant');
const toLocalPath = require('../node-haste/lib/toLocalPath');
const {generateAssetTransformResult, isAssetTypeAnImage} = require('./util'); const {generateAssetTransformResult, isAssetTypeAnImage} = require('./util');
const { const {
sep: pathSeparator, sep: pathSeparator,
join: joinPath, join: joinPath,
relative: relativePath,
dirname: pathDirname, dirname: pathDirname,
extname, extname,
} = require('path'); } = require('path');
@ -842,19 +842,6 @@ class Bundler {
} }
function toLocalPath(roots, absPath) {
for (let i = 0; i < roots.length; i++) {
const localPath = relativePath(roots[i], absPath);
if (localPath[0] !== '.') {
return localPath;
}
}
throw new Error(
'Expected root module to be relative to one of the project roots'
);
}
function verifyRootExists(root) { function verifyRootExists(root) {
// Verify that the root exists. // Verify that the root exists.
assert(fs.statSync(root).isDirectory(), 'Root has to be a valid directory'); assert(fs.statSync(root).isDirectory(), 'Root has to be a valid directory');

View File

@ -19,6 +19,7 @@ const invariant = require('fbjs/lib/invariant');
const minify = require('./minify'); const minify = require('./minify');
import type {LogEntry} from '../../Logger/Types'; import type {LogEntry} from '../../Logger/Types';
import type {LocalPath} from '../../node-haste/lib/toLocalPath';
import type {Ast, Plugins as BabelPlugins, SourceMap as MappingsMap} from 'babel-core'; import type {Ast, Plugins as BabelPlugins, SourceMap as MappingsMap} from 'babel-core';
export type TransformedCode = { export type TransformedCode = {

View File

@ -18,6 +18,7 @@ const FetchError = require('node-fetch/lib/fetch-error');
const crypto = require('crypto'); const crypto = require('crypto');
const fetch = require('node-fetch'); const fetch = require('node-fetch');
const invariant = require('fbjs/lib/invariant');
const jsonStableStringify = require('json-stable-stringify'); const jsonStableStringify = require('json-stable-stringify');
const path = require('path'); const path = require('path');
const throat = require('throat'); const throat = require('throat');
@ -26,6 +27,7 @@ import type {
Options as TransformWorkerOptions, Options as TransformWorkerOptions,
TransformOptionsStrict, TransformOptionsStrict,
} from '../JSTransformer/worker/worker'; } from '../JSTransformer/worker/worker';
import type {LocalPath} from '../node-haste/lib/toLocalPath';
import type {CachedResult, GetTransformCacheKey} from './TransformCache'; import type {CachedResult, GetTransformCacheKey} from './TransformCache';
/** /**
@ -62,10 +64,10 @@ type FetchResultFromURI = (uri: string) => Promise<?CachedResult>;
type StoreResults = (resultsByKey: Map<string, CachedResult>) => Promise<void>; type StoreResults = (resultsByKey: Map<string, CachedResult>) => Promise<void>;
export type FetchProps = { export type FetchProps = {
filePath: string, +localPath: LocalPath,
sourceCode: string, +sourceCode: string,
getTransformCacheKey: GetTransformCacheKey, +getTransformCacheKey: GetTransformCacheKey,
transformOptions: TransformWorkerOptions, +transformOptions: TransformWorkerOptions,
}; };
type URI = string; type URI = string;
@ -227,13 +229,13 @@ class URIBasedGlobalTransformCache {
*/ */
keyOf(props: FetchProps) { keyOf(props: FetchProps) {
const hash = crypto.createHash('sha1'); const hash = crypto.createHash('sha1');
const {sourceCode, filePath, transformOptions} = props; const {sourceCode, localPath, transformOptions} = props;
hash.update(this._optionsHasher.getTransformWorkerOptionsDigest(transformOptions)); hash.update(this._optionsHasher.getTransformWorkerOptionsDigest(transformOptions));
const cacheKey = props.getTransformCacheKey(transformOptions); const cacheKey = props.getTransformCacheKey(transformOptions);
hash.update(JSON.stringify(cacheKey)); hash.update(JSON.stringify(cacheKey));
hash.update(crypto.createHash('sha1').update(sourceCode).digest('hex')); hash.update(crypto.createHash('sha1').update(sourceCode).digest('hex'));
const digest = hash.digest('hex'); const digest = hash.digest('hex');
return `${digest}-${path.basename(filePath)}`; return `${digest}-${localPath}`;
} }
/** /**

View File

@ -24,6 +24,7 @@ const writeFileAtomicSync = require('write-file-atomic').sync;
import type {Options as WorkerOptions} from '../JSTransformer/worker/worker'; import type {Options as WorkerOptions} from '../JSTransformer/worker/worker';
import type {MappingsMap} from './SourceMap'; import type {MappingsMap} from './SourceMap';
import type {Reporter} from './reporting'; import type {Reporter} from './reporting';
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;
@ -50,6 +51,7 @@ export type CacheOptions = {
export type ReadTransformProps = { export type ReadTransformProps = {
filePath: string, filePath: string,
localPath: LocalPath,
sourceCode: string, sourceCode: string,
transformOptions: WorkerOptions, transformOptions: WorkerOptions,
transformOptionsKey: string, transformOptionsKey: string,

View File

@ -55,12 +55,12 @@ describe('GlobalTransformCache', () => {
}, },
}; };
const result = await Promise.all([cache.fetch({ const result = await Promise.all([cache.fetch({
filePath: 'foo.js', localPath: 'some/where/foo.js',
sourceCode: '/* beep */', sourceCode: '/* beep */',
getTransformCacheKey: () => 'abcd', getTransformCacheKey: () => 'abcd',
transformOptions, transformOptions,
}), cache.fetch({ }), cache.fetch({
filePath: 'bar.js', localPath: 'some/where/else/bar.js',
sourceCode: '/* boop */', sourceCode: '/* boop */',
getTransformCacheKey: () => 'abcd', getTransformCacheKey: () => 'abcd',
transformOptions, transformOptions,

View File

@ -19,12 +19,12 @@ Object {
exports[`GlobalTransformCache fetches results 1`] = ` exports[`GlobalTransformCache fetches results 1`] = `
Array [ Array [
Object { Object {
"code": "/* code from http://globalcache.com/cd6df9b7e86839dafc5e9b5b493a28cbc55074e7-foo.js */", "code": "/* code from http://globalcache.com/cd6df9b7e86839dafc5e9b5b493a28cbc55074e7-some/where/foo.js */",
"dependencies": Array [], "dependencies": Array [],
"dependencyOffsets": Array [], "dependencyOffsets": Array [],
}, },
Object { Object {
"code": "/* code from http://globalcache.com/6329a317fcf94d74cc9a1de6442ee7c25e27a507-bar.js */", "code": "/* code from http://globalcache.com/6329a317fcf94d74cc9a1de6442ee7c25e27a507-some/where/else/bar.js */",
"dependencies": Array [], "dependencies": Array [],
"dependencyOffsets": Array [], "dependencyOffsets": Array [],
}, },

View File

@ -162,14 +162,15 @@ class DependencyGraph extends EventEmitter {
_createModuleCache() { _createModuleCache() {
const {_opts} = this; const {_opts} = this;
return new ModuleCache({ return new ModuleCache({
assetDependencies: _opts.assetDependencies,
depGraphHelpers: this._helpers,
getClosestPackage: this._getClosestPackage.bind(this),
getTransformCacheKey: _opts.getTransformCacheKey, getTransformCacheKey: _opts.getTransformCacheKey,
globalTransformCache: _opts.globalTransformCache, globalTransformCache: _opts.globalTransformCache,
transformCode: _opts.transformCode,
depGraphHelpers: this._helpers,
assetDependencies: _opts.assetDependencies,
moduleOptions: _opts.moduleOptions, moduleOptions: _opts.moduleOptions,
reporter: _opts.reporter, reporter: _opts.reporter,
getClosestPackage: this._getClosestPackage.bind(this), roots: _opts.roots,
transformCode: _opts.transformCode,
}, _opts.platforms); }, _opts.platforms);
} }

View File

@ -35,6 +35,7 @@ import type {Reporter} from '../lib/reporting';
import type DependencyGraphHelpers import type DependencyGraphHelpers
from './DependencyGraph/DependencyGraphHelpers'; from './DependencyGraph/DependencyGraphHelpers';
import type ModuleCache from './ModuleCache'; import type ModuleCache from './ModuleCache';
import type {LocalPath} from './lib/toLocalPath';
export type ReadResult = { export type ReadResult = {
+code: string, +code: string,
@ -74,6 +75,7 @@ export type ConstructorArgs = {
depGraphHelpers: DependencyGraphHelpers, depGraphHelpers: DependencyGraphHelpers,
globalTransformCache: ?GlobalTransformCache, globalTransformCache: ?GlobalTransformCache,
file: string, file: string,
localPath: LocalPath,
moduleCache: ModuleCache, moduleCache: ModuleCache,
options: Options, options: Options,
reporter: Reporter, reporter: Reporter,
@ -86,6 +88,7 @@ type DocBlock = {+[key: string]: string};
const TRANSFORM_CACHE = new TransformCache(); const TRANSFORM_CACHE = new TransformCache();
class Module { class Module {
localPath: LocalPath;
path: string; path: string;
type: string; type: string;
@ -106,6 +109,7 @@ class Module {
constructor({ constructor({
depGraphHelpers, depGraphHelpers,
localPath,
file, file,
getTransformCacheKey, getTransformCacheKey,
globalTransformCache, globalTransformCache,
@ -118,6 +122,7 @@ class Module {
throw new Error('Expected file to be absolute path but got ' + file); throw new Error('Expected file to be absolute path but got ' + file);
} }
this.localPath = localPath;
this.path = file; this.path = file;
this.type = 'Module'; this.type = 'Module';
@ -436,6 +441,7 @@ class Module {
const getTransformCacheKey = this._getTransformCacheKey; const getTransformCacheKey = this._getTransformCacheKey;
return { return {
filePath: this.path, filePath: this.path,
localPath: this.localPath,
sourceCode, sourceCode,
getTransformCacheKey, getTransformCacheKey,
transformOptions, transformOptions,

View File

@ -17,6 +17,8 @@ const Module = require('./Module');
const Package = require('./Package'); const Package = require('./Package');
const Polyfill = require('./Polyfill'); const Polyfill = require('./Polyfill');
const toLocalPath = require('./lib/toLocalPath');
import type {GlobalTransformCache} from '../lib/GlobalTransformCache'; import type {GlobalTransformCache} from '../lib/GlobalTransformCache';
import type {GetTransformCacheKey} from '../lib/TransformCache'; import type {GetTransformCacheKey} from '../lib/TransformCache';
import type {Reporter} from '../lib/reporting'; import type {Reporter} from '../lib/reporting';
@ -39,6 +41,7 @@ class ModuleCache {
_platforms: Set<string>; _platforms: Set<string>;
_transformCode: TransformCode; _transformCode: TransformCode;
_reporter: Reporter; _reporter: Reporter;
_roots: Array<string>;
constructor( constructor(
{ {
@ -49,18 +52,20 @@ class ModuleCache {
getTransformCacheKey, getTransformCacheKey,
globalTransformCache, globalTransformCache,
moduleOptions, moduleOptions,
roots,
reporter, reporter,
transformCode, transformCode,
}: { }: {|
assetDependencies: Array<string>, assetDependencies: Array<string>,
depGraphHelpers: DependencyGraphHelpers, depGraphHelpers: DependencyGraphHelpers,
getClosestPackage: GetClosestPackageFn, getClosestPackage: GetClosestPackageFn,
getTransformCacheKey: GetTransformCacheKey, getTransformCacheKey: GetTransformCacheKey,
globalTransformCache: ?GlobalTransformCache, globalTransformCache: ?GlobalTransformCache,
moduleOptions: ModuleOptions, moduleOptions: ModuleOptions,
roots: Array<string>,
reporter: Reporter, reporter: Reporter,
transformCode: TransformCode, transformCode: TransformCode,
}, |},
platforms: Set<string>, platforms: Set<string>,
) { ) {
this._assetDependencies = assetDependencies; this._assetDependencies = assetDependencies;
@ -75,6 +80,7 @@ class ModuleCache {
this._platforms = platforms; this._platforms = platforms;
this._transformCode = transformCode; this._transformCode = transformCode;
this._reporter = reporter; this._reporter = reporter;
this._roots = roots;
} }
getModule(filePath: string): Module { getModule(filePath: string): Module {
@ -84,6 +90,7 @@ class ModuleCache {
file: filePath, file: filePath,
getTransformCacheKey: this._getTransformCacheKey, getTransformCacheKey: this._getTransformCacheKey,
globalTransformCache: this._globalTransformCache, globalTransformCache: this._globalTransformCache,
localPath: toLocalPath(this._roots, filePath),
moduleCache: this, moduleCache: this,
options: this._moduleOptions, options: this._moduleOptions,
reporter: this._reporter, reporter: this._reporter,
@ -99,14 +106,22 @@ class ModuleCache {
getAssetModule(filePath: string) { getAssetModule(filePath: string) {
if (!this._moduleCache[filePath]) { if (!this._moduleCache[filePath]) {
/* $FlowFixMe: missing options. This is because this is an incorrect OOP /* FixMe: AssetModule does not need all these options. This is because
* design in the first place: AssetModule, being simpler than a normal * this is an incorrect OOP design in the first place: AssetModule, being
* Module, should not inherit the Module class. */ * simpler than a normal Module, should not inherit the Module class.
*/
this._moduleCache[filePath] = new AssetModule( this._moduleCache[filePath] = new AssetModule(
{ {
file: filePath,
moduleCache: this,
dependencies: this._assetDependencies, dependencies: this._assetDependencies,
depGraphHelpers: this._depGraphHelpers,
file: filePath,
getTransformCacheKey: this._getTransformCacheKey,
globalTransformCache: null,
localPath: toLocalPath(this._roots, filePath),
moduleCache: this,
options: this._moduleOptions,
reporter: this._reporter,
transformCode: this._transformCode,
}, },
this._platforms, this._platforms,
); );
@ -149,6 +164,7 @@ class ModuleCache {
file, file,
depGraphHelpers: this._depGraphHelpers, depGraphHelpers: this._depGraphHelpers,
getTransformCacheKey: this._getTransformCacheKey, getTransformCacheKey: this._getTransformCacheKey,
localPath: toLocalPath(this._roots, file),
moduleCache: this, moduleCache: this,
transformCode: this._transformCode, transformCode: this._transformCode,
}); });

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/
'use strict';
const {relative} = require('path');
declare class OpaqueLocalPath {}
export type LocalPath = OpaqueLocalPath & string;
// FIXME: This function has the shortcoming of potentially returning identical
// paths for two files in different roots.
function toLocalPath(roots: Array<string>, absolutePath: string): LocalPath {
for (let i = 0; i < roots.length; i++) {
const localPath = relative(roots[i], absolutePath);
if (localPath[0] !== '.') {
return (localPath: any);
}
}
throw new Error(
'Expected root module to be relative to one of the project roots'
);
}
module.exports = toLocalPath;