packager: make output simpler, more legible
Reviewed By: cpojer Differential Revision: D4339417 fbshipit-source-id: f174ee11bc220de5e8da1d8227e9a9ceb5319e8d
This commit is contained in:
parent
c92ad5f6ae
commit
ede04abf8f
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
const log = require('../util/log').out('bundle');
|
const log = require('../util/log').out('bundle');
|
||||||
const Server = require('../../packager/react-packager/src/Server');
|
const Server = require('../../packager/react-packager/src/Server');
|
||||||
|
const TerminalReporter = require('../../packager/react-packager/src/lib/TerminalReporter');
|
||||||
|
|
||||||
const outputBundle = require('./output/bundle');
|
const outputBundle = require('./output/bundle');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
@ -57,6 +58,7 @@ function buildBundle(args, config, output = outputBundle, packagerInstance) {
|
||||||
extraNodeModules: config.extraNodeModules,
|
extraNodeModules: config.extraNodeModules,
|
||||||
resetCache: args.resetCache,
|
resetCache: args.resetCache,
|
||||||
watch: false,
|
watch: false,
|
||||||
|
reporter: new TerminalReporter(),
|
||||||
};
|
};
|
||||||
|
|
||||||
packagerInstance = new Server(options);
|
packagerInstance = new Server(options);
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
const querystring = require('querystring');
|
const querystring = require('querystring');
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
|
|
||||||
const {createEntry, print} = require('../../../packager/react-packager/src/Logger');
|
|
||||||
const {getInverseDependencies} = require('../../../packager/react-packager/src/node-haste');
|
const {getInverseDependencies} = require('../../../packager/react-packager/src/node-haste');
|
||||||
|
|
||||||
const blacklist = [
|
const blacklist = [
|
||||||
|
@ -114,9 +113,7 @@ function attachHMRServer({httpServer, path, packagerServer}) {
|
||||||
path: path,
|
path: path,
|
||||||
});
|
});
|
||||||
|
|
||||||
print(createEntry(`HMR Server listening on ${path}`));
|
|
||||||
wss.on('connection', ws => {
|
wss.on('connection', ws => {
|
||||||
print(createEntry('HMR Client connected'));
|
|
||||||
const params = querystring.parse(url.parse(ws.upgradeReq.url).query);
|
const params = querystring.parse(url.parse(ws.upgradeReq.url).query);
|
||||||
|
|
||||||
getDependencies(params.platform, params.bundleEntry)
|
getDependencies(params.platform, params.bundleEntry)
|
||||||
|
@ -140,7 +137,6 @@ function attachHMRServer({httpServer, path, packagerServer}) {
|
||||||
if (!client) {
|
if (!client) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
print(createEntry('HMR Server detected file change'));
|
|
||||||
|
|
||||||
const blacklisted = blacklist.find(blacklistedPath =>
|
const blacklisted = blacklist.find(blacklistedPath =>
|
||||||
filename.indexOf(blacklistedPath) !== -1
|
filename.indexOf(blacklistedPath) !== -1
|
||||||
|
@ -297,7 +293,6 @@ function attachHMRServer({httpServer, path, packagerServer}) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
print(createEntry('HMR Server sending update to client'));
|
|
||||||
client.ws.send(update);
|
client.ws.send(update);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -57,12 +57,22 @@ function createServer(options) {
|
||||||
|
|
||||||
options = Object.assign({}, options);
|
options = Object.assign({}, options);
|
||||||
delete options.verbose;
|
delete options.verbose;
|
||||||
|
if (options.reporter == null) {
|
||||||
|
// It's unsound to set-up the reporter here, but this allows backward
|
||||||
|
// compatibility.
|
||||||
|
var TerminalReporter = require('./src/lib/TerminalReporter');
|
||||||
|
options.reporter = new TerminalReporter();
|
||||||
|
}
|
||||||
var Server = require('./src/Server');
|
var Server = require('./src/Server');
|
||||||
return new Server(options);
|
return new Server(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNonPersistentServer(options) {
|
function createNonPersistentServer(options) {
|
||||||
Logger.disablePrinting();
|
if (options.reporter == null) {
|
||||||
|
// It's unsound to set-up the reporter here, but this allows backward
|
||||||
|
// compatibility.
|
||||||
|
options.reporter = require('./src/lib/reporting').nullReporter;
|
||||||
|
}
|
||||||
options.watch = !options.nonPersistent;
|
options.watch = !options.nonPersistent;
|
||||||
return createServer(options);
|
return createServer(options);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import type AssetServer from '../AssetServer';
|
||||||
import type Module from '../node-haste/Module';
|
import type Module from '../node-haste/Module';
|
||||||
import type ResolutionResponse from '../node-haste/DependencyGraph/ResolutionResponse';
|
import type ResolutionResponse from '../node-haste/DependencyGraph/ResolutionResponse';
|
||||||
import type {Options as TransformOptions} from '../JSTransformer/worker/worker';
|
import type {Options as TransformOptions} from '../JSTransformer/worker/worker';
|
||||||
|
import type {Reporter} from '../lib/reporting';
|
||||||
|
|
||||||
export type GetTransformOptions<T> = (
|
export type GetTransformOptions<T> = (
|
||||||
string,
|
string,
|
||||||
|
@ -54,7 +55,6 @@ const {
|
||||||
createActionStartEntry,
|
createActionStartEntry,
|
||||||
createActionEndEntry,
|
createActionEndEntry,
|
||||||
log,
|
log,
|
||||||
print,
|
|
||||||
} = require('../Logger');
|
} = require('../Logger');
|
||||||
|
|
||||||
const validateOpts = declareOpts({
|
const validateOpts = declareOpts({
|
||||||
|
@ -113,6 +113,9 @@ const validateOpts = declareOpts({
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
reporter: {
|
||||||
|
type: 'object',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const assetPropertyBlacklist = new Set([
|
const assetPropertyBlacklist = new Set([
|
||||||
|
@ -122,21 +125,22 @@ const assetPropertyBlacklist = new Set([
|
||||||
]);
|
]);
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
projectRoots: Array<string>,
|
allowBundleUpdates: boolean,
|
||||||
|
assetExts: Array<string>,
|
||||||
|
assetServer: AssetServer,
|
||||||
blacklistRE: RegExp,
|
blacklistRE: RegExp,
|
||||||
moduleFormat: string,
|
|
||||||
polyfillModuleNames: Array<string>,
|
|
||||||
cacheVersion: string,
|
cacheVersion: string,
|
||||||
|
extraNodeModules: {},
|
||||||
|
getTransformOptions?: GetTransformOptions<*>,
|
||||||
|
moduleFormat: string,
|
||||||
|
platforms: Array<string>,
|
||||||
|
polyfillModuleNames: Array<string>,
|
||||||
|
projectRoots: Array<string>,
|
||||||
|
reporter: Reporter,
|
||||||
resetCache: boolean,
|
resetCache: boolean,
|
||||||
transformModulePath: string,
|
transformModulePath: string,
|
||||||
getTransformOptions?: GetTransformOptions<*>,
|
|
||||||
extraNodeModules: {},
|
|
||||||
assetExts: Array<string>,
|
|
||||||
platforms: Array<string>,
|
|
||||||
watch: boolean,
|
|
||||||
assetServer: AssetServer,
|
|
||||||
transformTimeoutInterval: ?number,
|
transformTimeoutInterval: ?number,
|
||||||
allowBundleUpdates: boolean,
|
watch: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Bundler {
|
class Bundler {
|
||||||
|
@ -204,20 +208,21 @@ class Bundler {
|
||||||
blacklistRE: opts.blacklistRE,
|
blacklistRE: opts.blacklistRE,
|
||||||
cache: this._cache,
|
cache: this._cache,
|
||||||
extraNodeModules: opts.extraNodeModules,
|
extraNodeModules: opts.extraNodeModules,
|
||||||
watch: opts.watch,
|
|
||||||
minifyCode: this._transformer.minify,
|
minifyCode: this._transformer.minify,
|
||||||
moduleFormat: opts.moduleFormat,
|
moduleFormat: opts.moduleFormat,
|
||||||
platforms: opts.platforms,
|
platforms: opts.platforms,
|
||||||
polyfillModuleNames: opts.polyfillModuleNames,
|
polyfillModuleNames: opts.polyfillModuleNames,
|
||||||
projectRoots: opts.projectRoots,
|
projectRoots: opts.projectRoots,
|
||||||
|
reporter: options.reporter,
|
||||||
resetCache: opts.resetCache,
|
resetCache: opts.resetCache,
|
||||||
|
transformCacheKey,
|
||||||
transformCode:
|
transformCode:
|
||||||
(module, code, transformCodeOptions) => this._transformer.transformFile(
|
(module, code, transformCodeOptions) => this._transformer.transformFile(
|
||||||
module.path,
|
module.path,
|
||||||
code,
|
code,
|
||||||
transformCodeOptions,
|
transformCodeOptions,
|
||||||
),
|
),
|
||||||
transformCacheKey,
|
watch: opts.watch,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._projectRoots = opts.projectRoots;
|
this._projectRoots = opts.projectRoots;
|
||||||
|
@ -401,11 +406,11 @@ class Bundler {
|
||||||
onProgress = noop,
|
onProgress = noop,
|
||||||
}: *) {
|
}: *) {
|
||||||
const transformingFilesLogEntry =
|
const transformingFilesLogEntry =
|
||||||
print(log(createActionStartEntry({
|
log(createActionStartEntry({
|
||||||
action_name: 'Transforming files',
|
action_name: 'Transforming files',
|
||||||
entry_point: entryFile,
|
entry_point: entryFile,
|
||||||
environment: dev ? 'dev' : 'prod',
|
environment: dev ? 'dev' : 'prod',
|
||||||
})));
|
}));
|
||||||
|
|
||||||
const modulesByName = Object.create(null);
|
const modulesByName = Object.create(null);
|
||||||
|
|
||||||
|
@ -425,7 +430,7 @@ class Bundler {
|
||||||
return Promise.resolve(resolutionResponse).then(response => {
|
return Promise.resolve(resolutionResponse).then(response => {
|
||||||
bundle.setRamGroups(response.transformOptions.transform.ramGroups);
|
bundle.setRamGroups(response.transformOptions.transform.ramGroups);
|
||||||
|
|
||||||
print(log(createActionEndEntry(transformingFilesLogEntry)));
|
log(createActionEndEntry(transformingFilesLogEntry));
|
||||||
onResolutionResponse(response);
|
onResolutionResponse(response);
|
||||||
|
|
||||||
// get entry file complete path (`entryFile` is relative to roots)
|
// get entry file complete path (`entryFile` is relative to roots)
|
||||||
|
|
|
@ -17,7 +17,6 @@ const {
|
||||||
createEntry,
|
createEntry,
|
||||||
createActionStartEntry,
|
createActionStartEntry,
|
||||||
createActionEndEntry,
|
createActionEndEntry,
|
||||||
enablePrinting,
|
|
||||||
} = require('../');
|
} = require('../');
|
||||||
|
|
||||||
describe('Logger', () => {
|
describe('Logger', () => {
|
||||||
|
@ -29,7 +28,6 @@ describe('Logger', () => {
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
console.log = originalConsoleLog;
|
console.log = originalConsoleLog;
|
||||||
enablePrinting();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates simple log entries', () => {
|
it('creates simple log entries', () => {
|
||||||
|
|
|
@ -7,14 +7,12 @@
|
||||||
* 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
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const chalk = require('chalk');
|
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const pkgjson = require('../../../package.json');
|
const pkgjson = require('../../../package.json');
|
||||||
const terminal = require('../lib/terminal');
|
|
||||||
|
|
||||||
const {EventEmitter} = require('events');
|
const {EventEmitter} = require('events');
|
||||||
|
|
||||||
|
@ -24,17 +22,6 @@ import type {
|
||||||
LogEntry,
|
LogEntry,
|
||||||
} from './Types';
|
} from './Types';
|
||||||
|
|
||||||
const DATE_LOCALE_OPTIONS = {
|
|
||||||
day: '2-digit',
|
|
||||||
hour12: false,
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit',
|
|
||||||
month: '2-digit',
|
|
||||||
second: '2-digit',
|
|
||||||
year: 'numeric',
|
|
||||||
};
|
|
||||||
|
|
||||||
let PRINT_LOG_ENTRIES = true;
|
|
||||||
const log_session = `${os.hostname()}-${Date.now()}`;
|
const log_session = `${os.hostname()}-${Date.now()}`;
|
||||||
const eventEmitter = new EventEmitter();
|
const eventEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
@ -93,68 +80,10 @@ function log(logEntry: LogEntry): LogEntry {
|
||||||
return logEntry;
|
return logEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
function print(
|
|
||||||
logEntry: LogEntry,
|
|
||||||
printFields?: Array<string> = [],
|
|
||||||
): LogEntry {
|
|
||||||
if (!PRINT_LOG_ENTRIES) {
|
|
||||||
return logEntry;
|
|
||||||
}
|
|
||||||
const {
|
|
||||||
log_entry_label: logEntryLabel,
|
|
||||||
action_phase: actionPhase,
|
|
||||||
duration_ms: duration,
|
|
||||||
} = logEntry;
|
|
||||||
|
|
||||||
const timeStamp = new Date().toLocaleString(undefined, DATE_LOCALE_OPTIONS);
|
|
||||||
let logEntryString;
|
|
||||||
|
|
||||||
switch (actionPhase) {
|
|
||||||
case 'start':
|
|
||||||
logEntryString = chalk.dim(`[${timeStamp}] <START> ${logEntryLabel}`);
|
|
||||||
break;
|
|
||||||
case 'end':
|
|
||||||
logEntryString = chalk.dim(`[${timeStamp}] <END> ${logEntryLabel}`) +
|
|
||||||
chalk.cyan(` (${+duration}ms)`);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
logEntryString = chalk.dim(`[${timeStamp}] ${logEntryLabel}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (printFields.length) {
|
|
||||||
const indent = ' '.repeat(timeStamp.length + 11);
|
|
||||||
|
|
||||||
for (const field of printFields) {
|
|
||||||
const value = logEntry[field];
|
|
||||||
if (value === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
logEntryString += chalk.dim(`\n${indent}${field}: ${value.toString()}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-console-disallow
|
|
||||||
terminal.log(logEntryString);
|
|
||||||
|
|
||||||
return logEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
function enablePrinting(): void {
|
|
||||||
PRINT_LOG_ENTRIES = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function disablePrinting(): void {
|
|
||||||
PRINT_LOG_ENTRIES = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
on,
|
on,
|
||||||
createEntry,
|
createEntry,
|
||||||
createActionStartEntry,
|
createActionStartEntry,
|
||||||
createActionEndEntry,
|
createActionEndEntry,
|
||||||
log,
|
log,
|
||||||
print,
|
|
||||||
enablePrinting,
|
|
||||||
disablePrinting,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,7 @@ import type ResolutionResponse from '../node-haste/DependencyGraph/ResolutionRes
|
||||||
import type Module from '../node-haste/Module';
|
import type Module from '../node-haste/Module';
|
||||||
import type {SourceMap} from '../lib/SourceMap';
|
import type {SourceMap} from '../lib/SourceMap';
|
||||||
import type {Options as TransformOptions} from '../JSTransformer/worker/worker';
|
import type {Options as TransformOptions} from '../JSTransformer/worker/worker';
|
||||||
|
import type {Reporter} from '../lib/reporting';
|
||||||
|
|
||||||
const validateOpts = declareOpts({
|
const validateOpts = declareOpts({
|
||||||
projectRoots: {
|
projectRoots: {
|
||||||
|
@ -71,6 +72,9 @@ const validateOpts = declareOpts({
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
reporter: {
|
||||||
|
type: 'object',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const getDependenciesValidateOpts = declareOpts({
|
const getDependenciesValidateOpts = declareOpts({
|
||||||
|
@ -99,30 +103,34 @@ class Resolver {
|
||||||
Promise<{code: string, map: SourceMap}>;
|
Promise<{code: string, map: SourceMap}>;
|
||||||
_polyfillModuleNames: Array<string>;
|
_polyfillModuleNames: Array<string>;
|
||||||
|
|
||||||
constructor(options: {resetCache: boolean}) {
|
constructor(options: {
|
||||||
|
reporter: Reporter,
|
||||||
|
resetCache: boolean,
|
||||||
|
}) {
|
||||||
const opts = validateOpts(options);
|
const opts = validateOpts(options);
|
||||||
|
|
||||||
this._depGraph = new DependencyGraph({
|
this._depGraph = new DependencyGraph({
|
||||||
roots: opts.projectRoots,
|
assetDependencies: ['react-native/Libraries/Image/AssetRegistry'],
|
||||||
assetExts: opts.assetExts,
|
assetExts: opts.assetExts,
|
||||||
|
cache: opts.cache,
|
||||||
|
extraNodeModules: opts.extraNodeModules,
|
||||||
ignoreFilePath: function(filepath) {
|
ignoreFilePath: function(filepath) {
|
||||||
return filepath.indexOf('__tests__') !== -1 ||
|
return filepath.indexOf('__tests__') !== -1 ||
|
||||||
(opts.blacklistRE && opts.blacklistRE.test(filepath));
|
(opts.blacklistRE && opts.blacklistRE.test(filepath));
|
||||||
},
|
},
|
||||||
providesModuleNodeModules: defaults.providesModuleNodeModules,
|
|
||||||
platforms: opts.platforms,
|
|
||||||
preferNativePlatform: true,
|
|
||||||
watch: opts.watch,
|
|
||||||
cache: opts.cache,
|
|
||||||
transformCode: opts.transformCode,
|
|
||||||
transformCacheKey: opts.transformCacheKey,
|
|
||||||
extraNodeModules: opts.extraNodeModules,
|
|
||||||
assetDependencies: ['react-native/Libraries/Image/AssetRegistry'],
|
|
||||||
resetCache: options.resetCache,
|
|
||||||
moduleOptions: {
|
moduleOptions: {
|
||||||
cacheTransformResults: true,
|
cacheTransformResults: true,
|
||||||
resetCache: options.resetCache,
|
resetCache: options.resetCache,
|
||||||
},
|
},
|
||||||
|
platforms: opts.platforms,
|
||||||
|
preferNativePlatform: true,
|
||||||
|
providesModuleNodeModules: defaults.providesModuleNodeModules,
|
||||||
|
reporter: options.reporter,
|
||||||
|
resetCache: options.resetCache,
|
||||||
|
roots: opts.projectRoots,
|
||||||
|
transformCacheKey: opts.transformCacheKey,
|
||||||
|
transformCode: opts.transformCode,
|
||||||
|
watch: opts.watch,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._minifyCode = opts.minifyCode;
|
this._minifyCode = opts.minifyCode;
|
||||||
|
|
|
@ -39,7 +39,8 @@ describe('processRequest', () => {
|
||||||
projectRoots: ['root'],
|
projectRoots: ['root'],
|
||||||
blacklistRE: null,
|
blacklistRE: null,
|
||||||
cacheVersion: null,
|
cacheVersion: null,
|
||||||
polyfillModuleNames: null
|
polyfillModuleNames: null,
|
||||||
|
reporter: require('../../lib/reporting').nullReporter,
|
||||||
};
|
};
|
||||||
|
|
||||||
const makeRequest = (reqHandler, requrl, reqOptions) => new Promise(resolve =>
|
const makeRequest = (reqHandler, requrl, reqOptions) => new Promise(resolve =>
|
||||||
|
|
|
@ -22,7 +22,6 @@ const defaults = require('../../../defaults');
|
||||||
const mime = require('mime-types');
|
const mime = require('mime-types');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const terminal = require('../lib/terminal');
|
const terminal = require('../lib/terminal');
|
||||||
const throttle = require('lodash/throttle');
|
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
|
|
||||||
const debug = require('debug')('ReactNativePackager:Server');
|
const debug = require('debug')('ReactNativePackager:Server');
|
||||||
|
@ -32,12 +31,12 @@ import type {Stats} from 'fs';
|
||||||
import type {IncomingMessage, ServerResponse} from 'http';
|
import type {IncomingMessage, ServerResponse} from 'http';
|
||||||
import type ResolutionResponse from '../node-haste/DependencyGraph/ResolutionResponse';
|
import type ResolutionResponse from '../node-haste/DependencyGraph/ResolutionResponse';
|
||||||
import type Bundle from '../Bundler/Bundle';
|
import type Bundle from '../Bundler/Bundle';
|
||||||
|
import type {Reporter} from '../lib/reporting';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
createActionStartEntry,
|
createActionStartEntry,
|
||||||
createActionEndEntry,
|
createActionEndEntry,
|
||||||
log,
|
log,
|
||||||
print,
|
|
||||||
} = require('../Logger');
|
} = require('../Logger');
|
||||||
|
|
||||||
function debounceAndBatch(fn, delay) {
|
function debounceAndBatch(fn, delay) {
|
||||||
|
@ -109,6 +108,9 @@ const validateOpts = declareOpts({
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
reporter: {
|
||||||
|
type: 'object',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const bundleOpts = declareOpts({
|
const bundleOpts = declareOpts({
|
||||||
|
@ -223,12 +225,17 @@ class Server {
|
||||||
_bundler: Bundler;
|
_bundler: Bundler;
|
||||||
_debouncedFileChangeHandler: (filePath: string) => mixed;
|
_debouncedFileChangeHandler: (filePath: string) => mixed;
|
||||||
_hmrFileChangeListener: (type: string, filePath: string) => mixed;
|
_hmrFileChangeListener: (type: string, filePath: string) => mixed;
|
||||||
|
_reporter: Reporter;
|
||||||
|
|
||||||
constructor(options: {watch?: boolean}) {
|
constructor(options: {
|
||||||
|
reporter: Reporter,
|
||||||
|
watch?: boolean,
|
||||||
|
}) {
|
||||||
const opts = this._opts = validateOpts(options);
|
const opts = this._opts = validateOpts(options);
|
||||||
const processFileChange =
|
const processFileChange =
|
||||||
({type, filePath, stat}) => this.onFileChange(type, filePath, stat);
|
({type, filePath, stat}) => this.onFileChange(type, filePath, stat);
|
||||||
|
|
||||||
|
this._reporter = options.reporter;
|
||||||
this._projectRoots = opts.projectRoots;
|
this._projectRoots = opts.projectRoots;
|
||||||
this._bundles = Object.create(null);
|
this._bundles = Object.create(null);
|
||||||
this._changeWatchers = [];
|
this._changeWatchers = [];
|
||||||
|
@ -243,6 +250,7 @@ class Server {
|
||||||
bundlerOpts.assetServer = this._assetServer;
|
bundlerOpts.assetServer = this._assetServer;
|
||||||
bundlerOpts.allowBundleUpdates = options.watch;
|
bundlerOpts.allowBundleUpdates = options.watch;
|
||||||
bundlerOpts.watch = options.watch;
|
bundlerOpts.watch = options.watch;
|
||||||
|
bundlerOpts.reporter = options.reporter;
|
||||||
this._bundler = new Bundler(bundlerOpts);
|
this._bundler = new Bundler(bundlerOpts);
|
||||||
|
|
||||||
// changes to the haste map can affect resolution of files in the bundle
|
// changes to the haste map can affect resolution of files in the bundle
|
||||||
|
@ -514,10 +522,10 @@ class Server {
|
||||||
const assetPath: string = urlObj.pathname.match(/^\/assets\/(.+)$/);
|
const assetPath: string = urlObj.pathname.match(/^\/assets\/(.+)$/);
|
||||||
|
|
||||||
const processingAssetRequestLogEntry =
|
const processingAssetRequestLogEntry =
|
||||||
print(log(createActionStartEntry({
|
log(createActionStartEntry({
|
||||||
action_name: 'Processing asset request',
|
action_name: 'Processing asset request',
|
||||||
asset: assetPath[1],
|
asset: assetPath[1],
|
||||||
})), ['asset']);
|
}));
|
||||||
|
|
||||||
/* $FlowFixMe: query may be empty for invalid URLs */
|
/* $FlowFixMe: query may be empty for invalid URLs */
|
||||||
this._assetServer.get(assetPath[1], urlObj.query.platform)
|
this._assetServer.get(assetPath[1], urlObj.query.platform)
|
||||||
|
@ -530,7 +538,7 @@ class Server {
|
||||||
}
|
}
|
||||||
res.end(this._rangeRequestMiddleware(req, res, data, assetPath));
|
res.end(this._rangeRequestMiddleware(req, res, data, assetPath));
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
print(log(createActionEndEntry(processingAssetRequestLogEntry)), ['asset']);
|
log(createActionEndEntry(processingAssetRequestLogEntry));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
|
@ -565,10 +573,10 @@ class Server {
|
||||||
if (outdated.size) {
|
if (outdated.size) {
|
||||||
|
|
||||||
const updatingExistingBundleLogEntry =
|
const updatingExistingBundleLogEntry =
|
||||||
print(log(createActionStartEntry({
|
log(createActionStartEntry({
|
||||||
action_name: 'Updating existing bundle',
|
action_name: 'Updating existing bundle',
|
||||||
outdated_modules: outdated.size,
|
outdated_modules: outdated.size,
|
||||||
})), ['outdated_modules']);
|
}));
|
||||||
|
|
||||||
debug('Attempt to update existing bundle');
|
debug('Attempt to update existing bundle');
|
||||||
|
|
||||||
|
@ -632,10 +640,7 @@ class Server {
|
||||||
|
|
||||||
bundle.invalidateSource();
|
bundle.invalidateSource();
|
||||||
|
|
||||||
print(
|
log(createActionEndEntry(updatingExistingBundleLogEntry));
|
||||||
log(createActionEndEntry(updatingExistingBundleLogEntry)),
|
|
||||||
['outdated_modules'],
|
|
||||||
);
|
|
||||||
|
|
||||||
debug('Successfully updated existing bundle');
|
debug('Successfully updated existing bundle');
|
||||||
return bundle;
|
return bundle;
|
||||||
|
@ -689,21 +694,32 @@ class Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = this._getOptionsFromUrl(req.url);
|
const options = this._getOptionsFromUrl(req.url);
|
||||||
|
this._reporter.update({
|
||||||
|
type: 'bundle_requested',
|
||||||
|
entryFilePath: options.entryFile,
|
||||||
|
});
|
||||||
const requestingBundleLogEntry =
|
const requestingBundleLogEntry =
|
||||||
print(log(createActionStartEntry({
|
log(createActionStartEntry({
|
||||||
action_name: 'Requesting bundle',
|
action_name: 'Requesting bundle',
|
||||||
bundle_url: req.url,
|
bundle_url: req.url,
|
||||||
entry_point: options.entryFile,
|
entry_point: options.entryFile,
|
||||||
})), ['bundle_url']);
|
}));
|
||||||
|
|
||||||
let updateTTYProgressMessage = () => {};
|
let reportProgress = () => {};
|
||||||
if (process.stdout.isTTY && !this._opts.silent) {
|
if (!this._opts.silent) {
|
||||||
updateTTYProgressMessage = startTTYProgressMessage();
|
reportProgress = (transformedFileCount, totalFileCount) => {
|
||||||
|
this._reporter.update({
|
||||||
|
type: 'bundle_transform_progressed',
|
||||||
|
entryFilePath: options.entryFile,
|
||||||
|
transformedFileCount,
|
||||||
|
totalFileCount,
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const mres = MultipartResponse.wrap(req, res);
|
const mres = MultipartResponse.wrap(req, res);
|
||||||
options.onProgress = (done, total) => {
|
options.onProgress = (done, total) => {
|
||||||
updateTTYProgressMessage(done, total);
|
reportProgress(done, total);
|
||||||
mres.writeChunk({'Content-Type': 'application/json'}, JSON.stringify({done, total}));
|
mres.writeChunk({'Content-Type': 'application/json'}, JSON.stringify({done, total}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -711,6 +727,10 @@ class Server {
|
||||||
const building = this._useCachedOrUpdateOrCreateBundle(options);
|
const building = this._useCachedOrUpdateOrCreateBundle(options);
|
||||||
building.then(
|
building.then(
|
||||||
p => {
|
p => {
|
||||||
|
this._reporter.update({
|
||||||
|
type: 'bundle_built',
|
||||||
|
entryFilePath: options.entryFile,
|
||||||
|
});
|
||||||
if (requestType === 'bundle') {
|
if (requestType === 'bundle') {
|
||||||
debug('Generating source code');
|
debug('Generating source code');
|
||||||
const bundleSource = p.getSource({
|
const bundleSource = p.getSource({
|
||||||
|
@ -731,7 +751,7 @@ class Server {
|
||||||
mres.end(bundleSource);
|
mres.end(bundleSource);
|
||||||
}
|
}
|
||||||
debug('Finished response');
|
debug('Finished response');
|
||||||
print(log(createActionEndEntry(requestingBundleLogEntry)), ['bundle_url']);
|
log(createActionEndEntry(requestingBundleLogEntry));
|
||||||
} else if (requestType === 'map') {
|
} else if (requestType === 'map') {
|
||||||
let sourceMap = p.getSourceMap({
|
let sourceMap = p.getSourceMap({
|
||||||
minify: options.minify,
|
minify: options.minify,
|
||||||
|
@ -744,12 +764,12 @@ class Server {
|
||||||
|
|
||||||
mres.setHeader('Content-Type', 'application/json');
|
mres.setHeader('Content-Type', 'application/json');
|
||||||
mres.end(sourceMap);
|
mres.end(sourceMap);
|
||||||
print(log(createActionEndEntry(requestingBundleLogEntry)), ['bundle_url']);
|
log(createActionEndEntry(requestingBundleLogEntry));
|
||||||
} else if (requestType === 'assets') {
|
} else if (requestType === 'assets') {
|
||||||
const assetsList = JSON.stringify(p.getAssets());
|
const assetsList = JSON.stringify(p.getAssets());
|
||||||
mres.setHeader('Content-Type', 'application/json');
|
mres.setHeader('Content-Type', 'application/json');
|
||||||
mres.end(assetsList);
|
mres.end(assetsList);
|
||||||
print(log(createActionEndEntry(requestingBundleLogEntry)), ['bundle_url']);
|
log(createActionEndEntry(requestingBundleLogEntry));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error => this._handleError(mres, this.optionsHash(options), error)
|
error => this._handleError(mres, this.optionsHash(options), error)
|
||||||
|
@ -762,7 +782,7 @@ class Server {
|
||||||
|
|
||||||
_symbolicate(req: IncomingMessage, res: ServerResponse) {
|
_symbolicate(req: IncomingMessage, res: ServerResponse) {
|
||||||
const symbolicatingLogEntry =
|
const symbolicatingLogEntry =
|
||||||
print(log(createActionStartEntry('Symbolicating')));
|
log(createActionStartEntry('Symbolicating'));
|
||||||
|
|
||||||
/* $FlowFixMe: where is `rowBody` defined? Is it added by
|
/* $FlowFixMe: where is `rowBody` defined? Is it added by
|
||||||
* the `connect` framework? */
|
* the `connect` framework? */
|
||||||
|
@ -815,7 +835,7 @@ class Server {
|
||||||
stack => {
|
stack => {
|
||||||
res.end(JSON.stringify({stack: stack}));
|
res.end(JSON.stringify({stack: stack}));
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
print(log(createActionEndEntry(symbolicatingLogEntry)));
|
log(createActionEndEntry(symbolicatingLogEntry));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
|
@ -950,41 +970,6 @@ class Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProgressBar(ratio: number, length: number) {
|
|
||||||
const blockCount = Math.floor(ratio * length);
|
|
||||||
return (
|
|
||||||
'\u2593'.repeat(blockCount) +
|
|
||||||
'\u2591'.repeat(length - blockCount)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We use Math.pow(ratio, 2) to as a conservative measure of progress because we
|
|
||||||
* know the `totalCount` is going to progressively increase as well. We also
|
|
||||||
* prevent the ratio from going backwards.
|
|
||||||
*/
|
|
||||||
function startTTYProgressMessage(
|
|
||||||
): (doneCount: number, totalCount: number) => void {
|
|
||||||
let currentRatio = 0;
|
|
||||||
const updateMessage = (doneCount, totalCount) => {
|
|
||||||
const isDone = doneCount === totalCount;
|
|
||||||
const conservativeRatio = Math.pow(doneCount / totalCount, 2);
|
|
||||||
currentRatio = Math.max(conservativeRatio, currentRatio);
|
|
||||||
terminal.status(
|
|
||||||
'Transforming files %s%s% (%s/%s)%s',
|
|
||||||
isDone ? '' : getProgressBar(currentRatio, 20) + ' ',
|
|
||||||
(100 * currentRatio).toFixed(1),
|
|
||||||
doneCount,
|
|
||||||
totalCount,
|
|
||||||
isDone ? ', done.' : '...',
|
|
||||||
);
|
|
||||||
if (isDone) {
|
|
||||||
terminal.persistStatus();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return throttle(updateMessage, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
function contentsEqual(array: Array<mixed>, set: Set<mixed>): boolean {
|
function contentsEqual(array: Array<mixed>, set: Set<mixed>): boolean {
|
||||||
return array.length === set.size && array.every(set.has, set);
|
return array.length === set.size && array.every(set.has, set);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,246 @@
|
||||||
|
/**
|
||||||
|
* 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 chalk = require('chalk');
|
||||||
|
const path = require('path');
|
||||||
|
const reporting = require('./reporting');
|
||||||
|
const terminal = require('./terminal');
|
||||||
|
const throttle = require('lodash/throttle');
|
||||||
|
const util = require('util');
|
||||||
|
|
||||||
|
import type {ReportableEvent, GlobalCacheDisabledReason} from './reporting';
|
||||||
|
|
||||||
|
const DEP_GRAPH_MESSAGE = 'Loading dependency graph';
|
||||||
|
const GLOBAL_CACHE_DISABLED_MESSAGE_FORMAT =
|
||||||
|
'The global cache is now disabled because %s';
|
||||||
|
|
||||||
|
type BundleProgress = {
|
||||||
|
transformedFileCount: number,
|
||||||
|
totalFileCount: number,
|
||||||
|
ratio: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
const DARK_BLOCK_CHAR = '\u2593';
|
||||||
|
const LIGHT_BLOCK_CHAR = '\u2591';
|
||||||
|
const PACKAGE_EMOJI = '\uD83D\uDCE6';
|
||||||
|
|
||||||
|
function getProgressBar(ratio: number, length: number) {
|
||||||
|
const blockCount = Math.floor(ratio * length);
|
||||||
|
return (
|
||||||
|
DARK_BLOCK_CHAR.repeat(blockCount) +
|
||||||
|
LIGHT_BLOCK_CHAR.repeat(length - blockCount)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type TerminalReportableEvent = ReportableEvent | {
|
||||||
|
type: 'bundle_transform_progressed_throttled',
|
||||||
|
entryFilePath: string,
|
||||||
|
transformedFileCount: number,
|
||||||
|
totalFileCount: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We try to print useful information to the terminal for interactive builds.
|
||||||
|
* This implements the `Reporter` interface from the './reporting' module.
|
||||||
|
*/
|
||||||
|
class TerminalReporter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* built at the same time.
|
||||||
|
*/
|
||||||
|
_activeBundles: Map<string, BundleProgress>;
|
||||||
|
|
||||||
|
_dependencyGraphHasLoaded: boolean;
|
||||||
|
_scheduleUpdateBundleProgress: (data: {
|
||||||
|
entryFilePath: string,
|
||||||
|
transformedFileCount: number,
|
||||||
|
totalFileCount: number,
|
||||||
|
}) => void;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._dependencyGraphHasLoaded = false;
|
||||||
|
this._activeBundles = new Map();
|
||||||
|
this._scheduleUpdateBundleProgress = throttle((data) => {
|
||||||
|
this.update({...data, type: 'bundle_transform_progressed_throttled'});
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a message looking like this:
|
||||||
|
*
|
||||||
|
* Transforming files |#### | 34.2% (324/945)...
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_getFileTransformMessage(
|
||||||
|
{totalFileCount, transformedFileCount, ratio}: BundleProgress,
|
||||||
|
build: 'in_progress' | 'done',
|
||||||
|
): string {
|
||||||
|
if (build === 'done' && totalFileCount === 0) {
|
||||||
|
return 'All files are already up-to-date.';
|
||||||
|
}
|
||||||
|
return util.format(
|
||||||
|
'Transforming files %s%s% (%s/%s)%s',
|
||||||
|
build === 'done' ? '' : getProgressBar(ratio, 30) + ' ',
|
||||||
|
(100 * ratio).toFixed(1),
|
||||||
|
transformedFileCount,
|
||||||
|
totalFileCount,
|
||||||
|
build === 'done' ? ', done.' : '...',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a message that represent the progress of a single bundle build.
|
||||||
|
*/
|
||||||
|
_getBundleStatusMessage(
|
||||||
|
entryFilePath: string,
|
||||||
|
progress: BundleProgress,
|
||||||
|
build: 'in_progress' | 'done',
|
||||||
|
): string {
|
||||||
|
const localPath = path.relative('.', entryFilePath);
|
||||||
|
return [
|
||||||
|
chalk.underline(`Bundling ${PACKAGE_EMOJI} \`${localPath}\``),
|
||||||
|
this._getFileTransformMessage(progress, build),
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
_logCacheDisabled(reason: GlobalCacheDisabledReason): void {
|
||||||
|
const format = GLOBAL_CACHE_DISABLED_MESSAGE_FORMAT;
|
||||||
|
switch (reason) {
|
||||||
|
case 'too_many_errors':
|
||||||
|
reporting.logWarning(terminal, format, 'it has been failing too many times.');
|
||||||
|
break;
|
||||||
|
case 'too_many_misses':
|
||||||
|
reporting.logWarning(terminal, format, 'it has been missing too many consecutive keys.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is only concerned with logging and should not do state
|
||||||
|
* or terminal status updates.
|
||||||
|
*/
|
||||||
|
_log(event: TerminalReportableEvent): void {
|
||||||
|
switch (event.type) {
|
||||||
|
case 'bundle_built':
|
||||||
|
const progress = this._activeBundles.get(event.entryFilePath);
|
||||||
|
if (progress != null) {
|
||||||
|
terminal.log(
|
||||||
|
this._getBundleStatusMessage(event.entryFilePath, progress, 'done'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'dep_graph_loaded':
|
||||||
|
terminal.log(`${DEP_GRAPH_MESSAGE}, done.`);
|
||||||
|
break;
|
||||||
|
case 'global_cache_error':
|
||||||
|
reporting.logWarning(terminal, 'The global cache failed: %s', event.error.stack);
|
||||||
|
break;
|
||||||
|
case 'global_cache_disabled':
|
||||||
|
this._logCacheDisabled(event.reason);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We use Math.pow(ratio, 2) to as a conservative measure of progress because
|
||||||
|
* we know the `totalCount` is going to progressively increase as well. We
|
||||||
|
* also prevent the ratio from going backwards.
|
||||||
|
*/
|
||||||
|
_updateBundleProgress(
|
||||||
|
{entryFilePath, transformedFileCount, totalFileCount}: {
|
||||||
|
entryFilePath: string,
|
||||||
|
transformedFileCount: number,
|
||||||
|
totalFileCount: number,
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const currentProgress = this._activeBundles.get(entryFilePath);
|
||||||
|
if (currentProgress == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rawRatio = transformedFileCount / totalFileCount;
|
||||||
|
const conservativeRatio = Math.pow(rawRatio, 2);
|
||||||
|
const ratio = Math.max(conservativeRatio, currentProgress.ratio);
|
||||||
|
Object.assign(currentProgress, {
|
||||||
|
ratio,
|
||||||
|
transformedFileCount,
|
||||||
|
totalFileCount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is exclusively concerned with updating the internal state.
|
||||||
|
* No logging or status updates should be done at this point.
|
||||||
|
*/
|
||||||
|
_updateState(event: TerminalReportableEvent): void {
|
||||||
|
switch (event.type) {
|
||||||
|
case 'bundle_requested':
|
||||||
|
this._activeBundles.set(event.entryFilePath, {
|
||||||
|
transformedFileCount: 0,
|
||||||
|
totalFileCount: 0,
|
||||||
|
ratio: 0,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'bundle_transform_progressed':
|
||||||
|
this._scheduleUpdateBundleProgress(event);
|
||||||
|
break;
|
||||||
|
case 'bundle_transform_progressed_throttled':
|
||||||
|
this._updateBundleProgress(event);
|
||||||
|
break;
|
||||||
|
case 'bundle_built':
|
||||||
|
this._activeBundles.delete(event.entryFilePath);
|
||||||
|
break;
|
||||||
|
case 'dep_graph_loading':
|
||||||
|
this._dependencyGraphHasLoaded = false;
|
||||||
|
break;
|
||||||
|
case 'dep_graph_loaded':
|
||||||
|
this._dependencyGraphHasLoaded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_getDepGraphStatusMessage(): ?string {
|
||||||
|
if (!this._dependencyGraphHasLoaded) {
|
||||||
|
return `${DEP_GRAPH_MESSAGE}...`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a status message that is always consistent with the current state
|
||||||
|
* of the application. Having this single function ensures we don't have
|
||||||
|
* different callsites overriding each other status messages.
|
||||||
|
*/
|
||||||
|
_getStatusMessage(): string {
|
||||||
|
return [
|
||||||
|
this._getDepGraphStatusMessage(),
|
||||||
|
].concat(Array.from(this._activeBundles.entries()).map(
|
||||||
|
([entryFilePath, progress]) =>
|
||||||
|
this._getBundleStatusMessage(entryFilePath, progress, 'in_progress'),
|
||||||
|
)).filter(str => str != null).join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Everything that happens goes through the same 3 steps. This makes the
|
||||||
|
* output more reliable and consistent, because no matter what additional.
|
||||||
|
*/
|
||||||
|
update(event: TerminalReportableEvent) {
|
||||||
|
this._log(event);
|
||||||
|
this._updateState(event);
|
||||||
|
terminal.status(this._getStatusMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = TerminalReporter;
|
|
@ -0,0 +1,90 @@
|
||||||
|
/**
|
||||||
|
* 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 chalk = require('chalk');
|
||||||
|
const util = require('util');
|
||||||
|
|
||||||
|
import type {Terminal} from './terminal';
|
||||||
|
|
||||||
|
export type GlobalCacheDisabledReason = 'too_many_errors' | 'too_many_misses';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tagged union of all the actions that may happen and we may want to
|
||||||
|
* report to the tool user.
|
||||||
|
*/
|
||||||
|
export type ReportableEvent = {
|
||||||
|
type: 'dep_graph_loading',
|
||||||
|
} | {
|
||||||
|
type: 'dep_graph_loaded',
|
||||||
|
} | {
|
||||||
|
type: 'bundle_requested',
|
||||||
|
entryFilePath: string,
|
||||||
|
} | {
|
||||||
|
type: 'bundle_transform_progressed',
|
||||||
|
entryFilePath: string,
|
||||||
|
transformedFileCount: number,
|
||||||
|
totalFileCount: number,
|
||||||
|
} | {
|
||||||
|
type: 'bundle_built',
|
||||||
|
entryFilePath: string,
|
||||||
|
} | {
|
||||||
|
type: 'global_cache_error',
|
||||||
|
error: Error,
|
||||||
|
} | {
|
||||||
|
type: 'global_cache_disabled',
|
||||||
|
reason: GlobalCacheDisabledReason,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code across the application takes a reporter as an option and calls the
|
||||||
|
* update whenever one of the ReportableEvent happens. Code does not directly
|
||||||
|
* write to the standard output, because a build would be:
|
||||||
|
*
|
||||||
|
* 1. ad-hoc, embedded into another tool, in which case we do not want to
|
||||||
|
* pollute that tool's own output. The tool is free to present the
|
||||||
|
* warnings/progress we generate any way they want, by specifing a custom
|
||||||
|
* reporter.
|
||||||
|
* 2. run as a background process from another tool, in which case we want
|
||||||
|
* to expose updates in a way that is easily machine-readable, for example
|
||||||
|
* a JSON-stream. We don't want to pollute it with textual messages.
|
||||||
|
*
|
||||||
|
* We centralize terminal reporting into a single place because we want the
|
||||||
|
* output to be robust and consistent. The most common reporter is
|
||||||
|
* TerminalReporter, that should be the only place in the application should
|
||||||
|
* access the `terminal` module (nor the `console`).
|
||||||
|
*/
|
||||||
|
export type Reporter = {
|
||||||
|
update(event: ReportableEvent): void,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A standard way to log a warning to the terminal. This should not be called
|
||||||
|
* from some arbitrary packager logic, only from the reporters. Instead of
|
||||||
|
* calling this, add a new type of ReportableEvent instead, and implement a
|
||||||
|
* proper handler in the reporter(s).
|
||||||
|
*/
|
||||||
|
function logWarning(terminal: Terminal, format: string, ...args: Array<mixed>): void {
|
||||||
|
const str = util.format(format, ...args);
|
||||||
|
terminal.log('%s: %s', chalk.yellow('warning'), str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reporter that does nothing. Errors and warnings will be swallowed, that
|
||||||
|
* is generally not what you want.
|
||||||
|
*/
|
||||||
|
const nullReporter: Reporter = {update() {}};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
logWarning,
|
||||||
|
nullReporter,
|
||||||
|
};
|
|
@ -126,9 +126,9 @@ class GlobalTerminal extends Terminal {
|
||||||
Terminal: Class<Terminal>;
|
Terminal: Class<Terminal>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
/* $FlowFixMe: Flow is wrong, Node.js docs specify that process.stderr is an
|
/* $FlowFixMe: Flow is wrong, Node.js docs specify that process.stdout is an
|
||||||
* instance of a net.Socket (a local socket, not network). */
|
* instance of a net.Socket (a local socket, not network). */
|
||||||
super(process.stderr);
|
super(process.stdout);
|
||||||
this.Terminal = Terminal;
|
this.Terminal = Terminal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,20 +14,19 @@
|
||||||
const GlobalTransformCache = require('../lib/GlobalTransformCache');
|
const GlobalTransformCache = require('../lib/GlobalTransformCache');
|
||||||
const TransformCache = require('../lib/TransformCache');
|
const TransformCache = require('../lib/TransformCache');
|
||||||
|
|
||||||
const chalk = require('chalk');
|
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const docblock = require('./DependencyGraph/docblock');
|
const docblock = require('./DependencyGraph/docblock');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const invariant = require('invariant');
|
const invariant = require('invariant');
|
||||||
const isAbsolutePath = require('absolute-path');
|
const isAbsolutePath = require('absolute-path');
|
||||||
const jsonStableStringify = require('json-stable-stringify');
|
const jsonStableStringify = require('json-stable-stringify');
|
||||||
const terminal = require('../lib/terminal');
|
|
||||||
|
|
||||||
const {join: joinPath, relative: relativePath, extname} = require('path');
|
const {join: joinPath, relative: relativePath, extname} = require('path');
|
||||||
|
|
||||||
import type {TransformedCode, Options as TransformOptions} from '../JSTransformer/worker/worker';
|
import type {TransformedCode, Options as TransformOptions} from '../JSTransformer/worker/worker';
|
||||||
import type {SourceMap} from '../lib/SourceMap';
|
import type {SourceMap} from '../lib/SourceMap';
|
||||||
import type {ReadTransformProps} from '../lib/TransformCache';
|
import type {ReadTransformProps} from '../lib/TransformCache';
|
||||||
|
import type {Reporter} from '../lib/reporting';
|
||||||
import type Cache from './Cache';
|
import type Cache from './Cache';
|
||||||
import type DependencyGraphHelpers from './DependencyGraph/DependencyGraphHelpers';
|
import type DependencyGraphHelpers from './DependencyGraph/DependencyGraphHelpers';
|
||||||
import type ModuleCache from './ModuleCache';
|
import type ModuleCache from './ModuleCache';
|
||||||
|
@ -65,6 +64,7 @@ export type ConstructorArgs = {
|
||||||
transformCacheKey: ?string,
|
transformCacheKey: ?string,
|
||||||
depGraphHelpers: DependencyGraphHelpers,
|
depGraphHelpers: DependencyGraphHelpers,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
reporter: Reporter,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Module {
|
class Module {
|
||||||
|
@ -78,6 +78,7 @@ class Module {
|
||||||
_transformCacheKey: ?string;
|
_transformCacheKey: ?string;
|
||||||
_depGraphHelpers: DependencyGraphHelpers;
|
_depGraphHelpers: DependencyGraphHelpers;
|
||||||
_options: Options;
|
_options: Options;
|
||||||
|
_reporter: Reporter;
|
||||||
|
|
||||||
_docBlock: Promise<{id?: string, moduleDocBlock: {[key: string]: mixed}}>;
|
_docBlock: Promise<{id?: string, moduleDocBlock: {[key: string]: mixed}}>;
|
||||||
_readSourceCodePromise: Promise<string>;
|
_readSourceCodePromise: Promise<string>;
|
||||||
|
@ -93,6 +94,7 @@ class Module {
|
||||||
transformCode,
|
transformCode,
|
||||||
transformCacheKey,
|
transformCacheKey,
|
||||||
depGraphHelpers,
|
depGraphHelpers,
|
||||||
|
reporter,
|
||||||
options,
|
options,
|
||||||
}: ConstructorArgs) {
|
}: ConstructorArgs) {
|
||||||
if (!isAbsolutePath(file)) {
|
if (!isAbsolutePath(file)) {
|
||||||
|
@ -112,6 +114,7 @@ class Module {
|
||||||
);
|
);
|
||||||
this._depGraphHelpers = depGraphHelpers;
|
this._depGraphHelpers = depGraphHelpers;
|
||||||
this._options = options || {};
|
this._options = options || {};
|
||||||
|
this._reporter = reporter;
|
||||||
|
|
||||||
this._readPromises = new Map();
|
this._readPromises = new Map();
|
||||||
}
|
}
|
||||||
|
@ -276,25 +279,25 @@ class Module {
|
||||||
}
|
}
|
||||||
globalCache.fetch(cacheProps, (globalCacheError, globalCachedResult) => {
|
globalCache.fetch(cacheProps, (globalCacheError, globalCachedResult) => {
|
||||||
if (globalCacheError != null && Module._globalCacheRetries > 0) {
|
if (globalCacheError != null && Module._globalCacheRetries > 0) {
|
||||||
terminal.log(chalk.red(
|
this._reporter.update({
|
||||||
'Warning: the global cache failed with error:',
|
type: 'global_cache_error',
|
||||||
));
|
error: globalCacheError,
|
||||||
terminal.log(chalk.red(globalCacheError.stack));
|
});
|
||||||
Module._globalCacheRetries--;
|
Module._globalCacheRetries--;
|
||||||
if (Module._globalCacheRetries <= 0) {
|
if (Module._globalCacheRetries <= 0) {
|
||||||
terminal.log(chalk.red(
|
this._reporter.update({
|
||||||
'No more retries, the global cache will be disabled for the ' +
|
type: 'global_cache_disabled',
|
||||||
'remainder of the transformation.',
|
reason: 'too_many_errors',
|
||||||
));
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (globalCachedResult == null) {
|
if (globalCachedResult == null) {
|
||||||
--Module._globalCacheMaxMisses;
|
--Module._globalCacheMaxMisses;
|
||||||
if (Module._globalCacheMaxMisses === 0) {
|
if (Module._globalCacheMaxMisses === 0) {
|
||||||
terminal.log(
|
this._reporter.update({
|
||||||
'warning: global cache is now disabled because it ' +
|
type: 'global_cache_disabled',
|
||||||
'has been missing too many consecutive keys.',
|
reason: 'too_many_misses',
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
this._transformAndStoreCodeGlobally(cacheProps, globalCache, callback);
|
this._transformAndStoreCodeGlobally(cacheProps, globalCache, callback);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -16,6 +16,7 @@ const Module = require('./Module');
|
||||||
const Package = require('./Package');
|
const Package = require('./Package');
|
||||||
const Polyfill = require('./Polyfill');
|
const Polyfill = require('./Polyfill');
|
||||||
|
|
||||||
|
import type {Reporter} from '../lib/reporting';
|
||||||
import type Cache from './Cache';
|
import type Cache from './Cache';
|
||||||
import type DependencyGraphHelpers from './DependencyGraph/DependencyGraphHelpers';
|
import type DependencyGraphHelpers from './DependencyGraph/DependencyGraphHelpers';
|
||||||
import type {
|
import type {
|
||||||
|
@ -38,6 +39,7 @@ class ModuleCache {
|
||||||
_platforms: mixed;
|
_platforms: mixed;
|
||||||
_transformCacheKey: string;
|
_transformCacheKey: string;
|
||||||
_transformCode: TransformCode;
|
_transformCode: TransformCode;
|
||||||
|
_reporter: Reporter;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
assetDependencies,
|
assetDependencies,
|
||||||
|
@ -48,6 +50,7 @@ class ModuleCache {
|
||||||
moduleOptions,
|
moduleOptions,
|
||||||
transformCacheKey,
|
transformCacheKey,
|
||||||
transformCode,
|
transformCode,
|
||||||
|
reporter,
|
||||||
}: {
|
}: {
|
||||||
assetDependencies: mixed,
|
assetDependencies: mixed,
|
||||||
cache: Cache,
|
cache: Cache,
|
||||||
|
@ -56,6 +59,7 @@ class ModuleCache {
|
||||||
moduleOptions: ModuleOptions,
|
moduleOptions: ModuleOptions,
|
||||||
transformCacheKey: string,
|
transformCacheKey: string,
|
||||||
transformCode: TransformCode,
|
transformCode: TransformCode,
|
||||||
|
reporter: Reporter,
|
||||||
}, platforms: mixed) {
|
}, platforms: mixed) {
|
||||||
this._assetDependencies = assetDependencies;
|
this._assetDependencies = assetDependencies;
|
||||||
this._getClosestPackage = getClosestPackage;
|
this._getClosestPackage = getClosestPackage;
|
||||||
|
@ -68,6 +72,7 @@ class ModuleCache {
|
||||||
this._platforms = platforms;
|
this._platforms = platforms;
|
||||||
this._transformCacheKey = transformCacheKey;
|
this._transformCacheKey = transformCacheKey;
|
||||||
this._transformCode = transformCode;
|
this._transformCode = transformCode;
|
||||||
|
this._reporter = reporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
getModule(filePath: string) {
|
getModule(filePath: string) {
|
||||||
|
@ -80,6 +85,7 @@ class ModuleCache {
|
||||||
transformCacheKey: this._transformCacheKey,
|
transformCacheKey: this._transformCacheKey,
|
||||||
depGraphHelpers: this._depGraphHelpers,
|
depGraphHelpers: this._depGraphHelpers,
|
||||||
options: this._moduleOptions,
|
options: this._moduleOptions,
|
||||||
|
reporter: this._reporter,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return this._moduleCache[filePath];
|
return this._moduleCache[filePath];
|
||||||
|
|
|
@ -127,6 +127,7 @@ describe('DependencyGraph', function() {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
transformCacheKey,
|
transformCacheKey,
|
||||||
|
reporter: require('../../lib/reporting').nullReporter,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,10 @@ const {
|
||||||
createActionEndEntry,
|
createActionEndEntry,
|
||||||
createActionStartEntry,
|
createActionStartEntry,
|
||||||
log,
|
log,
|
||||||
print,
|
|
||||||
} = require('../Logger');
|
} = require('../Logger');
|
||||||
|
|
||||||
import type {Options as TransformOptions} from '../JSTransformer/worker/worker';
|
import type {Options as TransformOptions} from '../JSTransformer/worker/worker';
|
||||||
|
import type {Reporter} from '../lib/reporting';
|
||||||
import type {
|
import type {
|
||||||
Options as ModuleOptions,
|
Options as ModuleOptions,
|
||||||
TransformCode,
|
TransformCode,
|
||||||
|
@ -76,6 +76,7 @@ class DependencyGraph {
|
||||||
_hasteMapError: ?Error;
|
_hasteMapError: ?Error;
|
||||||
_helpers: DependencyGraphHelpers;
|
_helpers: DependencyGraphHelpers;
|
||||||
_moduleCache: ModuleCache;
|
_moduleCache: ModuleCache;
|
||||||
|
_reporter: Reporter;
|
||||||
|
|
||||||
_loading: Promise<mixed>;
|
_loading: Promise<mixed>;
|
||||||
|
|
||||||
|
@ -100,6 +101,7 @@ class DependencyGraph {
|
||||||
transformCode,
|
transformCode,
|
||||||
useWatchman,
|
useWatchman,
|
||||||
watch,
|
watch,
|
||||||
|
reporter,
|
||||||
}: {
|
}: {
|
||||||
assetDependencies: mixed,
|
assetDependencies: mixed,
|
||||||
assetExts: Array<string>,
|
assetExts: Array<string>,
|
||||||
|
@ -121,6 +123,7 @@ class DependencyGraph {
|
||||||
transformCode: TransformCode,
|
transformCode: TransformCode,
|
||||||
useWatchman?: ?boolean,
|
useWatchman?: ?boolean,
|
||||||
watch: boolean,
|
watch: boolean,
|
||||||
|
reporter: Reporter,
|
||||||
}) {
|
}) {
|
||||||
this._opts = {
|
this._opts = {
|
||||||
assetExts: assetExts || [],
|
assetExts: assetExts || [],
|
||||||
|
@ -145,6 +148,7 @@ class DependencyGraph {
|
||||||
watch: !!watch,
|
watch: !!watch,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this._reporter = reporter;
|
||||||
this._cache = cache;
|
this._cache = cache;
|
||||||
this._assetDependencies = assetDependencies;
|
this._assetDependencies = assetDependencies;
|
||||||
this._helpers = new DependencyGraphHelpers(this._opts);
|
this._helpers = new DependencyGraphHelpers(this._opts);
|
||||||
|
@ -174,7 +178,8 @@ class DependencyGraph {
|
||||||
});
|
});
|
||||||
|
|
||||||
const initializingPackagerLogEntry =
|
const initializingPackagerLogEntry =
|
||||||
print(log(createActionStartEntry('Initializing Packager')));
|
log(createActionStartEntry('Initializing Packager'));
|
||||||
|
this._reporter.update({type: 'dep_graph_loading'});
|
||||||
this._loading = this._haste.build().then(({hasteFS}) => {
|
this._loading = this._haste.build().then(({hasteFS}) => {
|
||||||
this._hasteFS = hasteFS;
|
this._hasteFS = hasteFS;
|
||||||
const hasteFSFiles = hasteFS.getAllFiles();
|
const hasteFSFiles = hasteFS.getAllFiles();
|
||||||
|
@ -186,6 +191,7 @@ class DependencyGraph {
|
||||||
depGraphHelpers: this._helpers,
|
depGraphHelpers: this._helpers,
|
||||||
assetDependencies: this._assetDependencies,
|
assetDependencies: this._assetDependencies,
|
||||||
moduleOptions: this._opts.moduleOptions,
|
moduleOptions: this._opts.moduleOptions,
|
||||||
|
reporter: this._reporter,
|
||||||
getClosestPackage: filePath => {
|
getClosestPackage: filePath => {
|
||||||
let {dir, root} = path.parse(filePath);
|
let {dir, root} = path.parse(filePath);
|
||||||
do {
|
do {
|
||||||
|
@ -216,12 +222,13 @@ class DependencyGraph {
|
||||||
});
|
});
|
||||||
|
|
||||||
const buildingHasteMapLogEntry =
|
const buildingHasteMapLogEntry =
|
||||||
print(log(createActionStartEntry('Building Haste Map')));
|
log(createActionStartEntry('Building Haste Map'));
|
||||||
|
|
||||||
return this._hasteMap.build().then(
|
return this._hasteMap.build().then(
|
||||||
map => {
|
map => {
|
||||||
print(log(createActionEndEntry(buildingHasteMapLogEntry)));
|
log(createActionEndEntry(buildingHasteMapLogEntry));
|
||||||
print(log(createActionEndEntry(initializingPackagerLogEntry)));
|
log(createActionEndEntry(initializingPackagerLogEntry));
|
||||||
|
this._reporter.update({type: 'dep_graph_loaded'});
|
||||||
return map;
|
return map;
|
||||||
},
|
},
|
||||||
err => {
|
err => {
|
||||||
|
|
Loading…
Reference in New Issue