metro-bundler: @format lib/

Reviewed By: cpojer

Differential Revision: D5493458

fbshipit-source-id: 05ac2895125b1419996a66f3ef155bc2cec11c02
This commit is contained in:
Jean Lauliac 2017-07-26 12:18:07 -07:00 committed by Facebook Github Bot
parent 521e25bded
commit 14b88b6ad4
17 changed files with 361 additions and 222 deletions

View File

@ -7,13 +7,16 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
* *
* @flow * @flow
* @format
*/ */
'use strict'; 'use strict';
const invariant = require('fbjs/lib/invariant'); const invariant = require('fbjs/lib/invariant');
type ProcessBatch<TItem, TResult> = (batch: Array<TItem>) => Promise<Array<TResult>>; type ProcessBatch<TItem, TResult> = (
batch: Array<TItem>,
) => Promise<Array<TResult>>;
type BatchProcessorOptions = { type BatchProcessorOptions = {
maximumDelayMs: number, maximumDelayMs: number,
@ -35,14 +38,16 @@ type QueueItem<TItem, TResult> = {
* processing right away. * processing right away.
*/ */
class BatchProcessor<TItem, TResult> { class BatchProcessor<TItem, TResult> {
_currentProcessCount: number; _currentProcessCount: number;
_options: BatchProcessorOptions; _options: BatchProcessorOptions;
_processBatch: ProcessBatch<TItem, TResult>; _processBatch: ProcessBatch<TItem, TResult>;
_queue: Array<QueueItem<TItem, TResult>>; _queue: Array<QueueItem<TItem, TResult>>;
_timeoutHandle: ?number; _timeoutHandle: ?number;
constructor(options: BatchProcessorOptions, processBatch: ProcessBatch<TItem, TResult>) { constructor(
options: BatchProcessorOptions,
processBatch: ProcessBatch<TItem, TResult>,
) {
this._options = options; this._options = options;
this._processBatch = processBatch; this._processBatch = processBatch;
this._queue = []; this._queue = [];
@ -56,7 +61,10 @@ class BatchProcessor<TItem, TResult> {
this._processQueueOnceReady(); this._processQueueOnceReady();
} }
_onBatchResults(jobs: Array<QueueItem<TItem, TResult>>, results: Array<TResult>) { _onBatchResults(
jobs: Array<QueueItem<TItem, TResult>>,
results: Array<TResult>,
) {
invariant(results.length === jobs.length, 'Not enough results returned.'); invariant(results.length === jobs.length, 'Not enough results returned.');
for (let i = 0; i < jobs.length; ++i) { for (let i = 0; i < jobs.length; ++i) {
jobs[i].resolve(results[i]); jobs[i].resolve(results[i]);
@ -104,7 +112,6 @@ class BatchProcessor<TItem, TResult> {
this._processQueueOnceReady(); this._processQueueOnceReady();
}); });
} }
} }
module.exports = BatchProcessor; module.exports = BatchProcessor;

View File

@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
* *
* @flow * @flow
* @format
*/ */
'use strict'; 'use strict';
@ -76,7 +77,6 @@ type URI = string;
* ensures we do a single request at a time to avoid pressuring the I/O. * ensures we do a single request at a time to avoid pressuring the I/O.
*/ */
class KeyURIFetcher { class KeyURIFetcher {
_batchProcessor: BatchProcessor<string, ?URI>; _batchProcessor: BatchProcessor<string, ?URI>;
_fetchResultURIs: FetchResultURIs; _fetchResultURIs: FetchResultURIs;
@ -96,24 +96,27 @@ class KeyURIFetcher {
constructor(fetchResultURIs: FetchResultURIs) { constructor(fetchResultURIs: FetchResultURIs) {
this._fetchResultURIs = fetchResultURIs; this._fetchResultURIs = fetchResultURIs;
this._batchProcessor = new BatchProcessor({ this._batchProcessor = new BatchProcessor(
{
maximumDelayMs: 10, maximumDelayMs: 10,
maximumItems: 500, maximumItems: 500,
concurrency: 2, concurrency: 2,
}, this._processKeys.bind(this)); },
this._processKeys.bind(this),
);
} }
} }
type KeyedResult = {key: string, result: CachedResult}; type KeyedResult = {key: string, result: CachedResult};
class KeyResultStore { class KeyResultStore {
_storeResults: StoreResults; _storeResults: StoreResults;
_batchProcessor: BatchProcessor<KeyedResult, void>; _batchProcessor: BatchProcessor<KeyedResult, void>;
async _processResults(keyResults: Array<KeyedResult>): Promise<Array<void>> { async _processResults(keyResults: Array<KeyedResult>): Promise<Array<void>> {
const resultsByKey = new Map(keyResults.map(pair => [pair.key, pair.result])); const resultsByKey = new Map(
keyResults.map(pair => [pair.key, pair.result]),
);
await this._storeResults(resultsByKey); await this._storeResults(resultsByKey);
return new Array(keyResults.length); return new Array(keyResults.length);
} }
@ -124,16 +127,22 @@ class KeyResultStore {
constructor(storeResults: StoreResults) { constructor(storeResults: StoreResults) {
this._storeResults = storeResults; this._storeResults = storeResults;
this._batchProcessor = new BatchProcessor({ this._batchProcessor = new BatchProcessor(
{
maximumDelayMs: 1000, maximumDelayMs: 1000,
maximumItems: 100, maximumItems: 100,
concurrency: 10, concurrency: 10,
}, this._processResults.bind(this)); },
this._processResults.bind(this),
);
} }
} }
export type TransformProfile = {+dev: boolean, +minify: boolean, +platform: ?string}; export type TransformProfile = {
+dev: boolean,
+minify: boolean,
+platform: ?string,
};
function profileKey({dev, minify, platform}: TransformProfile): string { function profileKey({dev, minify, platform}: TransformProfile): string {
return jsonStableStringify({dev, minify, platform}); return jsonStableStringify({dev, minify, platform});
@ -157,14 +166,14 @@ class TransformProfileSet {
} }
type FetchFailedDetails = type FetchFailedDetails =
{ | {
+statusCode: number, +statusCode: number,
+statusText: string, +statusText: string,
+type: 'unhandled_http_status', +type: 'unhandled_http_status',
+uri: string, +uri: string,
} | }
{+type: 'invalid_data'} | | {+type: 'invalid_data'}
{+type: 'invalid_key_data', key: string}; | {+type: 'invalid_key_data', key: string};
class FetchFailedError extends Error { class FetchFailedError extends Error {
/** Separate object for details allows us to have a type union. */ /** Separate object for details allows us to have a type union. */
@ -210,7 +219,6 @@ function validateCachedResult(cachedResult: mixed): ?CachedResult {
} }
class URIBasedGlobalTransformCache { class URIBasedGlobalTransformCache {
_fetcher: KeyURIFetcher; _fetcher: KeyURIFetcher;
_fetchResultFromURI: FetchResultFromURI; _fetchResultFromURI: FetchResultFromURI;
_profileSet: TransformProfileSet; _profileSet: TransformProfileSet;
@ -249,7 +257,9 @@ class URIBasedGlobalTransformCache {
keyOf(props: FetchProps) { keyOf(props: FetchProps) {
const hash = crypto.createHash('sha1'); const hash = crypto.createHash('sha1');
const {sourceCode, localPath, 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(JSON.stringify(localPath)); hash.update(JSON.stringify(localPath));
@ -287,7 +297,9 @@ class URIBasedGlobalTransformCache {
* waiting a little time before retring if experience shows it's useful. * waiting a little time before retring if experience shows it's useful.
*/ */
static _fetchResultFromURIWithRetry(uri: string): Promise<CachedResult> { static _fetchResultFromURIWithRetry(uri: string): Promise<CachedResult> {
return URIBasedGlobalTransformCache._fetchResultFromURI(uri).catch(error => { return URIBasedGlobalTransformCache._fetchResultFromURI(
uri,
).catch(error => {
if (!URIBasedGlobalTransformCache.shouldRetryAfterThatError(error)) { if (!URIBasedGlobalTransformCache.shouldRetryAfterThatError(error)) {
throw error; throw error;
} }
@ -311,11 +323,10 @@ class URIBasedGlobalTransformCache {
*/ */
static shouldRetryAfterThatError(error: mixed): boolean { static shouldRetryAfterThatError(error: mixed): boolean {
return ( return (
error instanceof FetchError && error.type === 'request-timeout' || ( (error instanceof FetchError && error.type === 'request-timeout') ||
error instanceof FetchFailedError && (error instanceof FetchFailedError &&
error.details.type === 'unhandled_http_status' && error.details.type === 'unhandled_http_status' &&
(error.details.statusCode === 503 || error.details.statusCode === 502) (error.details.statusCode === 503 || error.details.statusCode === 502))
)
); );
} }
@ -340,11 +351,12 @@ class URIBasedGlobalTransformCache {
this._store.store(this.keyOf(props), result); this._store.store(this.keyOf(props), result);
} }
} }
} }
URIBasedGlobalTransformCache.fetchResultFromURI = URIBasedGlobalTransformCache.fetchResultFromURI = throat(
throat(500, URIBasedGlobalTransformCache._fetchResultFromURIWithRetry); 500,
URIBasedGlobalTransformCache._fetchResultFromURIWithRetry,
);
class OptionsHasher { class OptionsHasher {
_rootPath: string; _rootPath: string;
@ -383,15 +395,20 @@ class OptionsHasher {
* many different fields including the optional Babel fields, and some serious * many different fields including the optional Babel fields, and some serious
* cleanup will be necessary to enable rock-solid typing. * cleanup will be necessary to enable rock-solid typing.
*/ */
hashTransformWorkerOptions(hash: crypto$Hash, options: TransformWorkerOptions): crypto$Hash { hashTransformWorkerOptions(
hash: crypto$Hash,
options: TransformWorkerOptions,
): crypto$Hash {
const {dev, minify, platform, transform, ...unknowns} = options; const {dev, minify, platform, transform, ...unknowns} = options;
const unknownKeys = Object.keys(unknowns); const unknownKeys = Object.keys(unknowns);
if (unknownKeys.length > 0) { if (unknownKeys.length > 0) {
const message = `these worker option fields are unknown: ${JSON.stringify(unknownKeys)}`; const message = `these worker option fields are unknown: ${JSON.stringify(
unknownKeys,
)}`;
throw new CannotHashOptionsError(message); throw new CannotHashOptionsError(message);
} }
// eslint-disable-next-line no-undef, no-bitwise // eslint-disable-next-line no-undef, no-bitwise
hash.update(new Buffer([+dev | +minify << 1])); hash.update(new Buffer([+dev | (+minify << 1)]));
hash.update(JSON.stringify(platform)); hash.update(JSON.stringify(platform));
return this.hashTransformOptions(hash, transform); return this.hashTransformOptions(hash, transform);
} }
@ -404,25 +421,45 @@ class OptionsHasher {
* of the cache key as they should not affect the transformation of a single * of the cache key as they should not affect the transformation of a single
* particular file. * particular file.
*/ */
hashTransformOptions(hash: crypto$Hash, options: TransformOptionsStrict): crypto$Hash { hashTransformOptions(
hash: crypto$Hash,
options: TransformOptionsStrict,
): crypto$Hash {
const { const {
generateSourceMaps, dev, hot, inlineRequires, platform, projectRoot, generateSourceMaps,
dev,
hot,
inlineRequires,
platform,
projectRoot,
...unknowns ...unknowns
} = options; } = options;
const unknownKeys = Object.keys(unknowns); const unknownKeys = Object.keys(unknowns);
if (unknownKeys.length > 0) { if (unknownKeys.length > 0) {
const message = `these transform option fields are unknown: ${JSON.stringify(unknownKeys)}`; const message = `these transform option fields are unknown: ${JSON.stringify(
unknownKeys,
)}`;
throw new CannotHashOptionsError(message); throw new CannotHashOptionsError(message);
} }
hash.update(new Buffer([ hash.update(
new Buffer([
// eslint-disable-next-line no-bitwise // eslint-disable-next-line no-bitwise
+dev | +generateSourceMaps << 1 | +hot << 2 | +!!inlineRequires << 3, +dev |
])); // eslint-disable-next-line no-bitwise
(+generateSourceMaps << 1) |
// eslint-disable-next-line no-bitwise
(+hot << 2) |
// eslint-disable-next-line no-bitwise
(+!!inlineRequires << 3),
]),
);
hash.update(JSON.stringify(platform)); hash.update(JSON.stringify(platform));
let blacklistWithLocalPaths = []; let blacklistWithLocalPaths = [];
if (typeof inlineRequires === 'object') { if (typeof inlineRequires === 'object') {
blacklistWithLocalPaths = this.pathsToLocal(Object.keys(inlineRequires.blacklist)); blacklistWithLocalPaths = this.pathsToLocal(
Object.keys(inlineRequires.blacklist),
);
} }
const localProjectRoot = this.toLocalPath(projectRoot); const localProjectRoot = this.toLocalPath(projectRoot);
const optionTuple = [blacklistWithLocalPaths, localProjectRoot]; const optionTuple = [blacklistWithLocalPaths, localProjectRoot];

View File

@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
* *
* @flow * @flow
* @format
*/ */
'use strict'; 'use strict';
@ -14,7 +15,6 @@
import {Writable} from 'stream'; import {Writable} from 'stream';
class JsonReporter<TEvent: {}> { class JsonReporter<TEvent: {}> {
_stream: Writable; _stream: Writable;
constructor(stream: Writable) { constructor(stream: Writable) {
@ -37,7 +37,6 @@ class JsonReporter<TEvent: {}> {
} }
this._stream.write(JSON.stringify(event) + '\n'); this._stream.write(JSON.stringify(event) + '\n');
} }
} }
module.exports = JsonReporter; module.exports = JsonReporter;

View File

@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
* *
* @flow * @flow
* @format
*/ */
'use strict'; 'use strict';
@ -24,7 +25,6 @@ type Metadata = {
}; };
class ModuleTransport { class ModuleTransport {
name: string; name: string;
id: number; id: number;
code: string; code: string;
@ -67,7 +67,6 @@ class ModuleTransport {
Object.freeze(this); Object.freeze(this);
} }
} }
module.exports = ModuleTransport; module.exports = ModuleTransport;

View File

@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
* *
* @flow * @flow
* @format
*/ */
'use strict'; 'use strict';
@ -32,7 +33,7 @@ export type FBIndexMap = IndexMap & FBExtensions;
export type SourceMap = IndexMap | MappingsMap; export type SourceMap = IndexMap | MappingsMap;
export type FBSourceMap = FBIndexMap | (MappingsMap & FBExtensions); export type FBSourceMap = FBIndexMap | (MappingsMap & FBExtensions);
function isMappingsMap(map: SourceMap)/*: %checks*/ { function isMappingsMap(map: SourceMap): %checks {
return map.mappings !== undefined; return map.mappings !== undefined;
} }

View File

@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
* *
* @flow * @flow
* @format
*/ */
'use strict'; 'use strict';
@ -49,12 +50,14 @@ function getProgressBar(ratio: number, length: number) {
); );
} }
export type TerminalReportableEvent = ReportableEvent | { export type TerminalReportableEvent =
| ReportableEvent
| {
buildID: string, buildID: string,
type: 'bundle_transform_progressed_throttled', type: 'bundle_transform_progressed_throttled',
transformedFileCount: number, transformedFileCount: number,
totalFileCount: number, totalFileCount: number,
}; };
type BuildPhase = 'in_progress' | 'done' | 'failed'; type BuildPhase = 'in_progress' | 'done' | 'failed';
@ -63,7 +66,6 @@ type BuildPhase = 'in_progress' | 'done' | 'failed';
* This implements the `Reporter` interface from the './reporting' module. * This implements the `Reporter` interface from the './reporting' module.
*/ */
class TerminalReporter { class TerminalReporter {
/** /**
* The bundle builds for which we are actively maintaining the status on the * The bundle builds for which we are actively maintaining the status on the
* terminal, ie. showing a progress bar. There can be several bundles being * terminal, ie. showing a progress bar. There can be several bundles being
@ -115,7 +117,7 @@ class TerminalReporter {
(100 * ratio).toFixed(1), (100 * ratio).toFixed(1),
transformedFileCount, transformedFileCount,
totalFileCount, totalFileCount,
phase === 'done' ? ', done.' : (phase === 'failed' ? ', failed.' : ''), phase === 'done' ? ', done.' : phase === 'failed' ? ', failed.' : '',
); );
} }
@ -142,11 +144,14 @@ class TerminalReporter {
_logBundleBuildDone(buildID: string) { _logBundleBuildDone(buildID: string) {
const progress = this._activeBundles.get(buildID); const progress = this._activeBundles.get(buildID);
if (progress != null) { if (progress != null) {
const msg = this._getBundleStatusMessage({ const msg = this._getBundleStatusMessage(
{
...progress, ...progress,
ratio: 1, ratio: 1,
transformedFileCount: progress.totalFileCount, transformedFileCount: progress.totalFileCount,
}, 'done'); },
'done',
);
this.terminal.log(msg); this.terminal.log(msg);
} }
} }
@ -166,21 +171,21 @@ class TerminalReporter {
port + port +
'.\n\n' + '.\n\n' +
'Keep this packager running while developing on any JS projects. ' + 'Keep this packager running while developing on any JS projects. ' +
'Feel free to close this tab and run your own packager instance if you ' + 'Feel free to close this tab and run your own packager instance ' +
'prefer.\n\n' + 'if you prefer.\n\n' +
'https://github.com/facebook/react-native', 'https://github.com/facebook/react-native',
{ {
marginLeft: 1, marginLeft: 1,
marginRight: 1, marginRight: 1,
paddingBottom: 1, paddingBottom: 1,
} },
) ),
); );
this.terminal.log( this.terminal.log(
'Looking for JS files in\n ', 'Looking for JS files in\n ',
chalk.dim(projectRoots.join('\n ')), chalk.dim(projectRoots.join('\n ')),
'\n' '\n',
); );
} }
@ -188,9 +193,11 @@ class TerminalReporter {
if (error.code === 'EADDRINUSE') { if (error.code === 'EADDRINUSE') {
this.terminal.log( this.terminal.log(
chalk.bgRed.bold(' ERROR '), chalk.bgRed.bold(' ERROR '),
chalk.red("Packager can't listen on port", chalk.bold(port)) chalk.red("Packager can't listen on port", chalk.bold(port)),
);
this.terminal.log(
'Most likely another process is already using this port',
); );
this.terminal.log('Most likely another process is already using this port');
this.terminal.log('Run the following command to find out which process:'); this.terminal.log('Run the following command to find out which process:');
this.terminal.log('\n ', chalk.bold('lsof -i :' + port), '\n'); this.terminal.log('\n ', chalk.bold('lsof -i :' + port), '\n');
this.terminal.log('Then, you can either shut down the other process:'); this.terminal.log('Then, you can either shut down the other process:');
@ -258,9 +265,9 @@ class TerminalReporter {
const he = error.hasteError; const he = error.hasteError;
const message = const message =
'ambiguous resolution: module `' + 'ambiguous resolution: module `' +
`${error.fromModulePath}\` tries to require \`${he.hasteName}\`, but ` + `${error.fromModulePath}\` tries to require \`${he.hasteName}\`, ` +
`there are several files providing this module. You can delete or ` + `but there are several files providing this module. You can delete ` +
'fix them: \n\n' + 'or fix them: \n\n' +
Object.keys(he.duplicatesSet) Object.keys(he.duplicatesSet)
.sort() .sort()
.map(dupFilePath => ` * \`${dupFilePath}\`\n`) .map(dupFilePath => ` * \`${dupFilePath}\`\n`)
@ -269,10 +276,11 @@ class TerminalReporter {
return; return;
} }
let message = (error.snippet == null && error.stack != null) let message =
error.snippet == null && error.stack != null
? error.stack ? error.stack
//$FlowFixMe T19379628 : //$FlowFixMe T19379628
: error.message; error.message;
//$FlowFixMe T19379628 //$FlowFixMe T19379628
if (error.filename && !message.includes(error.filename)) { if (error.filename && !message.includes(error.filename)) {
//$FlowFixMe T19379628 //$FlowFixMe T19379628
@ -305,13 +313,15 @@ class TerminalReporter {
* we know the `totalCount` is going to progressively increase as well. We * we know the `totalCount` is going to progressively increase as well. We
* also prevent the ratio from going backwards. * also prevent the ratio from going backwards.
*/ */
_updateBundleProgress( _updateBundleProgress({
{buildID, transformedFileCount, totalFileCount}: { buildID,
transformedFileCount,
totalFileCount,
}: {
buildID: string, buildID: string,
transformedFileCount: number, transformedFileCount: number,
totalFileCount: number, totalFileCount: number,
}, }) {
) {
const currentProgress = this._activeBundles.get(buildID); const currentProgress = this._activeBundles.get(buildID);
if (currentProgress == null) { if (currentProgress == null) {
return; return;
@ -377,12 +387,14 @@ class TerminalReporter {
* different callsites overriding each other status messages. * different callsites overriding each other status messages.
*/ */
_getStatusMessage(): string { _getStatusMessage(): string {
return [ return [this._getDepGraphStatusMessage()]
this._getDepGraphStatusMessage(), .concat(
].concat(Array.from(this._activeBundles.entries()).map( Array.from(this._activeBundles.entries()).map(([_, progress]) =>
([_, progress]) =>
this._getBundleStatusMessage(progress, 'in_progress'), this._getBundleStatusMessage(progress, 'in_progress'),
)).filter(str => str != null).join('\n'); ),
)
.filter(str => str != null)
.join('\n');
} }
/** /**
@ -394,7 +406,6 @@ class TerminalReporter {
this._updateState(event); this._updateState(event);
this.terminal.status(this._getStatusMessage()); this.terminal.status(this._getStatusMessage());
} }
} }
module.exports = TerminalReporter; module.exports = TerminalReporter;

View File

@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
* *
* @flow * @flow
* @format
*/ */
'use strict'; 'use strict';

View File

@ -5,6 +5,8 @@
* This source code is licensed under the BSD-style license found in the * 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 * 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. * of patent rights can be found in the PATENTS file in the same directory.
*
* @format
*/ */
'use strict'; 'use strict';
@ -15,14 +17,22 @@ const jsonStableStringify = require('json-stable-stringify');
const transformCache = new Map(); const transformCache = new Map();
const transformCacheKeyOf = props => const transformCacheKeyOf = props =>
props.filePath + '-' + crypto.createHash('md5') props.filePath +
'-' +
crypto
.createHash('md5')
.update(props.sourceCode) .update(props.sourceCode)
.update(props.getTransformCacheKey(props.sourceCode, props.filePath, props.transformOptions)) .update(
props.getTransformCacheKey(
props.sourceCode,
props.filePath,
props.transformOptions,
),
)
.update(jsonStableStringify(props.transformOptions || {})) .update(jsonStableStringify(props.transformOptions || {}))
.digest('hex'); .digest('hex');
class TransformCacheMock { class TransformCacheMock {
constructor() { constructor() {
this.mock = { this.mock = {
lastWrite: null, lastWrite: null,
@ -39,9 +49,11 @@ class TransformCacheMock {
} }
readSync(props) { readSync(props) {
return {result: transformCache.get(transformCacheKeyOf(props)), outdatedDependencies: []}; return {
result: transformCache.get(transformCacheKeyOf(props)),
outdatedDependencies: [],
};
} }
} }
module.exports = {mocked: () => new TransformCacheMock()}; module.exports = {mocked: () => new TransformCacheMock()};

View File

@ -5,7 +5,10 @@
* This source code is licensed under the BSD-style license found in the * 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 * 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. * of patent rights can be found in the PATENTS file in the same directory.
*
* @format
*/ */
'use strict'; 'use strict';
module.exports = function(declared) { module.exports = function(declared) {

View File

@ -5,6 +5,8 @@
* This source code is licensed under the BSD-style license found in the * 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 * 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. * of patent rights can be found in the PATENTS file in the same directory.
*
* @format
*/ */
'use strict'; 'use strict';
@ -14,7 +16,6 @@ jest.useRealTimers();
const BatchProcessor = require('../BatchProcessor'); const BatchProcessor = require('../BatchProcessor');
describe('BatchProcessor', () => { describe('BatchProcessor', () => {
const options = { const options = {
maximumDelayMs: 500, maximumDelayMs: 500,
maximumItems: 3, maximumItems: 3,
@ -27,7 +28,10 @@ describe('BatchProcessor', () => {
const batches = []; const batches = [];
let concurrency = 0; let concurrency = 0;
let maxConcurrency = 0; let maxConcurrency = 0;
const bp = new BatchProcessor(options, items => new Promise(resolve => { const bp = new BatchProcessor(
options,
items =>
new Promise(resolve => {
++concurrency; ++concurrency;
expect(concurrency).toBeLessThanOrEqual(options.concurrency); expect(concurrency).toBeLessThanOrEqual(options.concurrency);
maxConcurrency = Math.max(maxConcurrency, concurrency); maxConcurrency = Math.max(maxConcurrency, concurrency);
@ -36,31 +40,38 @@ describe('BatchProcessor', () => {
resolve(items.map(transform)); resolve(items.map(transform));
--concurrency; --concurrency;
}, 0); }, 0);
})); }),
);
const results = []; const results = [];
await Promise.all(input.map(e => bp.queue(e).then( await Promise.all(
input.map(e =>
bp.queue(e).then(
res => results.push(res), res => results.push(res),
error => process.nextTick(() => { throw error; }), error =>
))); process.nextTick(() => {
expect(batches).toEqual([ throw error;
[1, 2, 3], }),
[4, 5, 6], ),
[7, 8], ),
]); );
expect(batches).toEqual([[1, 2, 3], [4, 5, 6], [7, 8]]);
expect(maxConcurrency).toEqual(options.concurrency); expect(maxConcurrency).toEqual(options.concurrency);
expect(results).toEqual(input.map(transform)); expect(results).toEqual(input.map(transform));
}); });
it('report errors', async () => { it('report errors', async () => {
const error = new Error('oh noes'); const error = new Error('oh noes');
const bp = new BatchProcessor(options, items => new Promise((_, reject) => { const bp = new BatchProcessor(
options,
items =>
new Promise((_, reject) => {
setTimeout(reject.bind(null, error), 0); setTimeout(reject.bind(null, error), 0);
})); }),
let receivedError;
await bp.queue('foo').catch(
err => { receivedError = err; },
); );
let receivedError;
await bp.queue('foo').catch(err => {
receivedError = err;
});
expect(receivedError).toBe(error); expect(receivedError).toBe(error);
}); });
}); });

View File

@ -5,6 +5,8 @@
* This source code is licensed under the BSD-style license found in the * 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 * 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. * of patent rights can be found in the PATENTS file in the same directory.
*
* @format
*/ */
'use strict'; 'use strict';
@ -18,7 +20,9 @@ const {URIBasedGlobalTransformCache} = require('../GlobalTransformCache');
const FetchError = require('node-fetch/lib/fetch-error'); const FetchError = require('node-fetch/lib/fetch-error');
const path = require('path'); const path = require('path');
async function fetchResultURIs(keys: Array<string>): Promise<Map<string, string>> { async function fetchResultURIs(
keys: Array<string>,
): Promise<Map<string, string>> {
return new Map(keys.map(key => [key, `http://globalcache.com/${key}`])); return new Map(keys.map(key => [key, `http://globalcache.com/${key}`]));
} }
@ -31,7 +35,6 @@ async function fetchResultFromURI(uri: string): Promise<?CachedResult> {
} }
describe('GlobalTransformCache', () => { describe('GlobalTransformCache', () => {
it('fetches results', async () => { it('fetches results', async () => {
const cache = new URIBasedGlobalTransformCache({ const cache = new URIBasedGlobalTransformCache({
fetchResultFromURI, fetchResultFromURI,
@ -53,22 +56,24 @@ describe('GlobalTransformCache', () => {
projectRoot: path.join(__dirname, 'root'), projectRoot: path.join(__dirname, 'root'),
}, },
}; };
const result = await Promise.all([cache.fetch({ const result = await Promise.all([
cache.fetch({
localPath: 'some/where/foo.js', localPath: 'some/where/foo.js',
sourceCode: '/* beep */', sourceCode: '/* beep */',
getTransformCacheKey: () => 'abcd', getTransformCacheKey: () => 'abcd',
transformOptions, transformOptions,
}), cache.fetch({ }),
cache.fetch({
localPath: 'some/where/else/bar.js', localPath: 'some/where/else/bar.js',
sourceCode: '/* boop */', sourceCode: '/* boop */',
getTransformCacheKey: () => 'abcd', getTransformCacheKey: () => 'abcd',
transformOptions, transformOptions,
})]); }),
]);
expect(result).toMatchSnapshot(); expect(result).toMatchSnapshot();
}); });
describe('fetchResultFromURI', () => { describe('fetchResultFromURI', () => {
const defaultFetchMockImpl = async uri => ({ const defaultFetchMockImpl = async uri => ({
status: 200, status: 200,
json: async () => ({ json: async () => ({
@ -84,8 +89,9 @@ describe('GlobalTransformCache', () => {
it('fetches result', async () => { it('fetches result', async () => {
mockFetch.mockImplementation(defaultFetchMockImpl); mockFetch.mockImplementation(defaultFetchMockImpl);
const result = await URIBasedGlobalTransformCache const result = await URIBasedGlobalTransformCache.fetchResultFromURI(
.fetchResultFromURI('http://globalcache.com/foo'); 'http://globalcache.com/foo',
);
expect(result).toMatchSnapshot(); expect(result).toMatchSnapshot();
}); });
@ -94,11 +100,10 @@ describe('GlobalTransformCache', () => {
mockFetch.mockImplementation(defaultFetchMockImpl); mockFetch.mockImplementation(defaultFetchMockImpl);
throw new FetchError('timeout!', 'request-timeout'); throw new FetchError('timeout!', 'request-timeout');
}); });
const result = await URIBasedGlobalTransformCache const result = await URIBasedGlobalTransformCache.fetchResultFromURI(
.fetchResultFromURI('http://globalcache.com/foo'); 'http://globalcache.com/foo',
);
expect(result).toMatchSnapshot(); expect(result).toMatchSnapshot();
}); });
}); });
}); });

View File

@ -5,6 +5,8 @@
* This source code is licensed under the BSD-style license found in the * 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 * 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. * of patent rights can be found in the PATENTS file in the same directory.
*
* @format
*/ */
'use strict'; 'use strict';
@ -12,10 +14,13 @@
jest.mock('readline', () => ({ jest.mock('readline', () => ({
moveCursor: (stream, dx, dy) => { moveCursor: (stream, dx, dy) => {
const {cursor, columns} = stream; const {cursor, columns} = stream;
stream.cursor = Math.max(cursor - cursor % columns, cursor + dx) + dy * columns; stream.cursor =
Math.max(cursor - cursor % columns, cursor + dx) + dy * columns;
}, },
clearLine: (stream, dir) => { clearLine: (stream, dir) => {
if (dir !== 0) {throw new Error('unsupported');} if (dir !== 0) {
throw new Error('unsupported');
}
const {cursor, columns} = stream; const {cursor, columns} = stream;
const curLine = cursor - cursor % columns; const curLine = cursor - cursor % columns;
const nextLine = curLine + columns; const nextLine = curLine + columns;
@ -26,7 +31,6 @@ jest.mock('readline', () => ({
})); }));
describe('Terminal', () => { describe('Terminal', () => {
beforeEach(() => { beforeEach(() => {
jest.resetModules(); jest.resetModules();
}); });
@ -46,7 +50,7 @@ describe('Terminal', () => {
write(str) { write(str) {
for (let i = 0; i < str.length; ++i) { for (let i = 0; i < str.length; ++i) {
if (str[i] === '\n') { if (str[i] === '\n') {
this.cursor = this.cursor - (this.cursor % columns) + columns; this.cursor = this.cursor - this.cursor % columns + columns;
} else { } else {
this.buffer[this.cursor] = str[i]; this.buffer[this.cursor] = str[i];
++this.cursor; ++this.cursor;
@ -81,10 +85,14 @@ describe('Terminal', () => {
terminal.status('status2'); terminal.status('status2');
terminal.log('bar'); terminal.log('bar');
jest.runAllTimers(); jest.runAllTimers();
expect(stream.buffer.join('').trim()).toEqual('foo bar status2'); expect(stream.buffer.join('').trim()).toEqual(
'foo bar status2',
);
terminal.log('beep'); terminal.log('beep');
jest.runAllTimers(); jest.runAllTimers();
expect(stream.buffer.join('').trim()).toEqual('foo bar beep status2'); expect(stream.buffer.join('').trim()).toEqual(
'foo bar beep status2',
);
}); });
it('updates status when logging, multi-line', () => { it('updates status when logging, multi-line', () => {
@ -93,8 +101,9 @@ describe('Terminal', () => {
terminal.status('status\nanother'); terminal.status('status\nanother');
terminal.log('bar'); terminal.log('bar');
jest.runAllTimers(); jest.runAllTimers();
expect(stream.buffer.join('').trim()) expect(stream.buffer.join('').trim()).toEqual(
.toEqual('foo bar status another'); 'foo bar status another',
);
}); });
it('persists status', () => { it('persists status', () => {
@ -106,5 +115,4 @@ describe('Terminal', () => {
jest.runAllTimers(); jest.runAllTimers();
expect(stream.buffer.join('').trim()).toEqual('foo status bar'); expect(stream.buffer.join('').trim()).toEqual('foo status bar');
}); });
}); });

View File

@ -5,6 +5,8 @@
* This source code is licensed under the BSD-style license found in the * 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 * 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. * of patent rights can be found in the PATENTS file in the same directory.
*
* @format
*/ */
'use strict'; 'use strict';
@ -43,13 +45,14 @@ function cartesianProductOf(a1, a2) {
} }
describe('TransformCaching.FileBasedCache', () => { describe('TransformCaching.FileBasedCache', () => {
let transformCache; let transformCache;
beforeEach(() => { beforeEach(() => {
jest.resetModules(); jest.resetModules();
mockFS.clear(); mockFS.clear();
transformCache = new (require('../TransformCaching').FileBasedCache)('/cache'); transformCache = new (require('../TransformCaching')).FileBasedCache(
'/cache',
);
}); });
it('is caching different files and options separately', () => { it('is caching different files and options separately', () => {
@ -60,8 +63,10 @@ describe('TransformCaching.FileBasedCache', () => {
getTransformCacheKey: () => 'abcdef', getTransformCacheKey: () => 'abcdef',
filePath, filePath,
transformOptions, transformOptions,
transformOptionsKey: crypto.createHash('md5') transformOptionsKey: crypto
.update(jsonStableStringify(transformOptions)).digest('hex'), .createHash('md5')
.update(jsonStableStringify(transformOptions))
.digest('hex'),
result: { result: {
code: `/* result for ${key} */`, code: `/* result for ${key} */`,
dependencies: ['foo', `dep of ${key}`], dependencies: ['foo', `dep of ${key}`],
@ -74,9 +79,7 @@ describe('TransformCaching.FileBasedCache', () => {
['/some/project/sub/dir/file.js', '/some/project/other.js'], ['/some/project/sub/dir/file.js', '/some/project/other.js'],
[{foo: 1}, {foo: 2}], [{foo: 1}, {foo: 2}],
); );
allCases.forEach( allCases.forEach(entry => transformCache.writeSync(argsFor(entry)));
entry => transformCache.writeSync(argsFor(entry)),
);
allCases.forEach(entry => { allCases.forEach(entry => {
const args = argsFor(entry); const args = argsFor(entry);
const {result} = args; const {result} = args;
@ -129,5 +132,4 @@ describe('TransformCaching.FileBasedCache', () => {
expect(cachedResult.outdatedDependencies).toEqual(['foo', 'bar']); expect(cachedResult.outdatedDependencies).toEqual(['foo', 'bar']);
}); });
}); });
}); });

View File

@ -5,7 +5,10 @@
* This source code is licensed under the BSD-style license found in the * 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 * 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. * of patent rights can be found in the PATENTS file in the same directory.
*
* @format
*/ */
'use strict'; 'use strict';
var _ = require('lodash'); var _ = require('lodash');
@ -82,12 +85,20 @@ function formatBanner(message, options) {
var horizontalBorderLine = repeatString( var horizontalBorderLine = repeatString(
HORIZONTAL_LINE, HORIZONTAL_LINE,
width - marginLeft - marginRight - 2 width - marginLeft - marginRight - 2,
); );
var top = spaces(marginLeft) + TOP_LEFT + horizontalBorderLine + TOP_RIGHT + var top =
spaces(marginLeft) +
TOP_LEFT +
horizontalBorderLine +
TOP_RIGHT +
spaces(marginRight);
var bottom =
spaces(marginLeft) +
BOTTOM_LEFT +
horizontalBorderLine +
BOTTOM_RIGHT +
spaces(marginRight); spaces(marginRight);
var bottom = spaces(marginLeft) + BOTTOM_LEFT + horizontalBorderLine +
BOTTOM_RIGHT + spaces(marginRight);
return _.flattenDeep([top, bodyLines, bottom]).join('\n'); return _.flattenDeep([top, bodyLines, bottom]).join('\n');
} }

View File

@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
* *
* @flow * @flow
* @format
*/ */
'use strict'; 'use strict';
@ -17,7 +18,10 @@ const {isMappingsMap} = require('./SourceMap');
import type {SourceMap} from './SourceMap'; import type {SourceMap} from './SourceMap';
function relativizeSourceMapInternal(sourceMap: SourceMap, sourcesRoot: string) { function relativizeSourceMapInternal(
sourceMap: SourceMap,
sourcesRoot: string,
) {
if (!isMappingsMap(sourceMap)) { if (!isMappingsMap(sourceMap)) {
for (let i = 0; i < sourceMap.sections.length; i++) { for (let i = 0; i < sourceMap.sections.length; i++) {
relativizeSourceMapInternal(sourceMap.sections[i].map, sourcesRoot); relativizeSourceMapInternal(sourceMap.sections[i].map, sourcesRoot);
@ -29,7 +33,10 @@ function relativizeSourceMapInternal(sourceMap: SourceMap, sourcesRoot: string)
} }
} }
function relativizeSourceMap(sourceMap: SourceMap, sourcesRoot?: string): SourceMap { function relativizeSourceMap(
sourceMap: SourceMap,
sourcesRoot?: string,
): SourceMap {
if (!sourcesRoot) { if (!sourcesRoot) {
return sourceMap; return sourceMap;
} }

View File

@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
* *
* @flow * @flow
* @format
*/ */
'use strict'; 'use strict';
@ -23,53 +24,68 @@ export type GlobalCacheDisabledReason = 'too_many_errors' | 'too_many_misses';
* A tagged union of all the actions that may happen and we may want to * A tagged union of all the actions that may happen and we may want to
* report to the tool user. * report to the tool user.
*/ */
export type ReportableEvent = { export type ReportableEvent =
| {
port: number, port: number,
projectRoots: $ReadOnlyArray<string>, projectRoots: $ReadOnlyArray<string>,
type: 'initialize_packager_started', type: 'initialize_packager_started',
} | { }
| {
type: 'initialize_packager_done', type: 'initialize_packager_done',
} | { }
| {
type: 'initialize_packager_failed', type: 'initialize_packager_failed',
port: number, port: number,
error: Error, error: Error,
} | { }
| {
buildID: string, buildID: string,
type: 'bundle_build_done', type: 'bundle_build_done',
} | { }
| {
buildID: string, buildID: string,
type: 'bundle_build_failed', type: 'bundle_build_failed',
} | { }
| {
buildID: string, buildID: string,
bundleOptions: BundleOptions, bundleOptions: BundleOptions,
type: 'bundle_build_started', type: 'bundle_build_started',
} | { }
| {
error: Error, error: Error,
type: 'bundling_error', type: 'bundling_error',
} | { }
| {
type: 'dep_graph_loading', type: 'dep_graph_loading',
} | { }
| {
type: 'dep_graph_loaded', type: 'dep_graph_loaded',
} | { }
| {
buildID: string, buildID: string,
type: 'bundle_transform_progressed', type: 'bundle_transform_progressed',
transformedFileCount: number, transformedFileCount: number,
totalFileCount: number, totalFileCount: number,
} | { }
| {
type: 'global_cache_error', type: 'global_cache_error',
error: Error, error: Error,
} | { }
| {
type: 'global_cache_disabled', type: 'global_cache_disabled',
reason: GlobalCacheDisabledReason, reason: GlobalCacheDisabledReason,
} | { }
| {
type: 'transform_cache_reset', type: 'transform_cache_reset',
} | { }
| {
type: 'worker_stdout_chunk', type: 'worker_stdout_chunk',
chunk: string, chunk: string,
} | { }
| {
type: 'worker_stderr_chunk', type: 'worker_stderr_chunk',
chunk: string, chunk: string,
}; };
/** /**
* Code across the application takes a reporter as an option and calls the * Code across the application takes a reporter as an option and calls the
@ -99,7 +115,11 @@ export type Reporter = {
* calling this, add a new type of ReportableEvent instead, and implement a * calling this, add a new type of ReportableEvent instead, and implement a
* proper handler in the reporter(s). * proper handler in the reporter(s).
*/ */
function logWarning(terminal: Terminal, format: string, ...args: Array<mixed>): void { function logWarning(
terminal: Terminal,
format: string,
...args: Array<mixed>
): void {
const str = util.format(format, ...args); const str = util.format(format, ...args);
terminal.log('%s: %s', chalk.yellow('warning'), str); terminal.log('%s: %s', chalk.yellow('warning'), str);
} }
@ -107,7 +127,11 @@ function logWarning(terminal: Terminal, format: string, ...args: Array<mixed>):
/** /**
* Similar to `logWarning`, but for messages that require the user to act. * Similar to `logWarning`, but for messages that require the user to act.
*/ */
function logError(terminal: Terminal, format: string, ...args: Array<mixed>): void { function logError(
terminal: Terminal,
format: string,
...args: Array<mixed>
): void {
const str = util.format(format, ...args); const str = util.format(format, ...args);
terminal.log('%s: %s', chalk.red('error'), str); terminal.log('%s: %s', chalk.red('error'), str);
} }

View File

@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
* *
* @flow * @flow
* @format
*/ */
'use strict'; 'use strict';