Files
react-native/packager/react-packager/src/node-haste/index.js
David Aurelio 8240339dca Clear bundles for potential dependency resolution changes
Summary:
This clears the packager server cache for potential changes of module resolutions.
We will make this a lot better in the future, but for now this crude mechanism will have to do.

Reviewed By: cpojer

Differential Revision: D3713143

fbshipit-source-id: 7c81f40e8ec71404c3369211b29f63928d6634b9
2016-08-15 08:43:34 -07:00

337 lines
9.9 KiB
JavaScript

/**
* 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.
*/
'use strict';
const Cache = require('./Cache');
const Fastfs = require('./fastfs');
const FileWatcher = require('./FileWatcher');
const Module = require('./Module');
const ModuleCache = require('./ModuleCache');
const Polyfill = require('./Polyfill');
const crawl = require('./crawlers');
const extractRequires = require('./lib/extractRequires');
const getAssetDataFromName = require('./lib/getAssetDataFromName');
const getInverseDependencies = require('./lib/getInverseDependencies');
const getPlatformExtension = require('./lib/getPlatformExtension');
const isAbsolutePath = require('absolute-path');
const replacePatterns = require('./lib/replacePatterns');
const path = require('./fastpath');
const util = require('util');
const DependencyGraphHelpers = require('./DependencyGraph/DependencyGraphHelpers');
const ResolutionRequest = require('./DependencyGraph/ResolutionRequest');
const ResolutionResponse = require('./DependencyGraph/ResolutionResponse');
const HasteMap = require('./DependencyGraph/HasteMap');
const DeprecatedAssetMap = require('./DependencyGraph/DeprecatedAssetMap');
const ERROR_BUILDING_DEP_GRAPH = 'DependencyGraphError';
const defaultActivity = {
startEvent: () => {},
endEvent: () => {},
};
class DependencyGraph {
constructor({
activity,
roots,
ignoreFilePath,
fileWatcher,
assetRoots_DEPRECATED,
assetExts,
providesModuleNodeModules,
platforms,
preferNativePlatform,
cache,
extensions,
mocksPattern,
extractRequires,
transformCode,
shouldThrowOnUnresolvedErrors = () => true,
enableAssetMap,
assetDependencies,
moduleOptions,
extraNodeModules,
}) {
this._opts = {
activity: activity || defaultActivity,
roots,
ignoreFilePath: ignoreFilePath || (() => {}),
fileWatcher,
assetRoots_DEPRECATED: assetRoots_DEPRECATED || [],
assetExts: assetExts || [],
providesModuleNodeModules,
platforms: new Set(platforms || []),
preferNativePlatform: preferNativePlatform || false,
extensions: extensions || ['js', 'json'],
mocksPattern,
extractRequires,
transformCode,
shouldThrowOnUnresolvedErrors,
enableAssetMap: enableAssetMap || true,
moduleOptions: moduleOptions || {
cacheTransformResults: true,
},
extraNodeModules,
};
this._cache = cache;
this._assetDependencies = assetDependencies;
this._helpers = new DependencyGraphHelpers(this._opts);
this.load();
}
load() {
if (this._loading) {
return this._loading;
}
const {activity} = this._opts;
const depGraphActivity = activity.startEvent('Building Dependency Graph');
const crawlActivity = activity.startEvent('Crawling File System');
const allRoots = this._opts.roots.concat(this._opts.assetRoots_DEPRECATED);
this._crawling = crawl(allRoots, {
ignore: this._opts.ignoreFilePath,
exts: this._opts.extensions.concat(this._opts.assetExts),
fileWatcher: this._opts.fileWatcher,
});
this._crawling.then((files) => activity.endEvent(crawlActivity));
this._fastfs = new Fastfs(
'JavaScript',
this._opts.roots,
this._opts.fileWatcher,
{
ignore: this._opts.ignoreFilePath,
crawling: this._crawling,
activity: activity,
}
);
this._fastfs.on('change', this._processFileChange.bind(this));
this._moduleCache = new ModuleCache({
fastfs: this._fastfs,
cache: this._cache,
extractRequires: this._opts.extractRequires,
transformCode: this._opts.transformCode,
depGraphHelpers: this._helpers,
assetDependencies: this._assetDependencies,
moduleOptions: this._opts.moduleOptions,
}, this._opts.platfomrs);
this._hasteMap = new HasteMap({
fastfs: this._fastfs,
extensions: this._opts.extensions,
moduleCache: this._moduleCache,
preferNativePlatform: this._opts.preferNativePlatform,
helpers: this._helpers,
platforms: this._opts.platforms,
});
this._deprecatedAssetMap = new DeprecatedAssetMap({
fsCrawl: this._crawling,
roots: this._opts.assetRoots_DEPRECATED,
helpers: this._helpers,
fileWatcher: this._opts.fileWatcher,
ignoreFilePath: this._opts.ignoreFilePath,
assetExts: this._opts.assetExts,
activity: this._opts.activity,
enabled: this._opts.enableAssetMap,
platforms: this._opts.platforms,
});
this._loading = Promise.all([
this._fastfs.build()
.then(() => {
const hasteActivity = activity.startEvent('Building Haste Map');
return this._hasteMap.build().then(map => {
activity.endEvent(hasteActivity);
return map;
});
}),
this._deprecatedAssetMap.build(),
]).then(
response => {
activity.endEvent(depGraphActivity);
return response[0]; // Return the haste map
},
err => {
const error = new Error(
`Failed to build DependencyGraph: ${err.message}`
);
error.type = ERROR_BUILDING_DEP_GRAPH;
error.stack = err.stack;
throw error;
}
);
return this._loading;
}
/**
* Returns a promise with the direct dependencies the module associated to
* the given entryPath has.
*/
getShallowDependencies(entryPath, transformOptions) {
return this._moduleCache
.getModule(entryPath)
.getDependencies(transformOptions);
}
getFS() {
return this._fastfs;
}
/**
* Returns the module object for the given path.
*/
getModuleForPath(entryFile) {
return this._moduleCache.getModule(entryFile);
}
getAllModules() {
return this.load().then(() => this._moduleCache.getAllModules());
}
getDependencies({
entryPath,
platform,
transformOptions,
onProgress,
recursive = true,
}) {
return this.load().then(() => {
platform = this._getRequestPlatform(entryPath, platform);
const absPath = this._getAbsolutePath(entryPath);
const req = new ResolutionRequest({
platform,
platforms: this._opts.platforms,
preferNativePlatform: this._opts.preferNativePlatform,
entryPath: absPath,
deprecatedAssetMap: this._deprecatedAssetMap,
hasteMap: this._hasteMap,
helpers: this._helpers,
moduleCache: this._moduleCache,
fastfs: this._fastfs,
shouldThrowOnUnresolvedErrors: this._opts.shouldThrowOnUnresolvedErrors,
extraNodeModules: this._opts.extraNodeModules,
});
const response = new ResolutionResponse({transformOptions});
return req.getOrderedDependencies({
response,
mocksPattern: this._opts.mocksPattern,
transformOptions,
onProgress,
recursive,
}).then(() => response);
});
}
matchFilesByPattern(pattern) {
return this.load().then(() => this._fastfs.matchFilesByPattern(pattern));
}
_getRequestPlatform(entryPath, platform) {
if (platform == null) {
platform = getPlatformExtension(entryPath, this._opts.platforms);
} else if (!this._opts.platforms.has(platform)) {
throw new Error('Unrecognized platform: ' + platform);
}
return platform;
}
_getAbsolutePath(filePath) {
if (isAbsolutePath(filePath)) {
return path.resolve(filePath);
}
for (let i = 0; i < this._opts.roots.length; i++) {
const root = this._opts.roots[i];
const potentialAbsPath = path.join(root, filePath);
if (this._fastfs.fileExists(potentialAbsPath)) {
return path.resolve(potentialAbsPath);
}
}
throw new NotFoundError(
'Cannot find entry file %s in any of the roots: %j',
filePath,
this._opts.roots
);
}
_processFileChange(type, filePath, root, fstat) {
const absPath = path.join(root, filePath);
if (fstat && fstat.isDirectory() ||
this._opts.ignoreFilePath(absPath) ||
this._helpers.isNodeModulesDir(absPath)) {
return;
}
// Ok, this is some tricky promise code. Our requirements are:
// * we need to report back failures
// * failures shouldn't block recovery
// * Errors can leave `hasteMap` in an incorrect state, and we need to rebuild
// After we process a file change we record any errors which will also be
// reported via the next request. On the next file change, we'll see that
// we are in an error state and we should decide to do a full rebuild.
const resolve = () => {
if (this._hasteMapError) {
console.warn(
'Rebuilding haste map to recover from error:\n' +
this._hasteMapError.stack
);
this._hasteMapError = null;
// Rebuild the entire map if last change resulted in an error.
this._loading = this._hasteMap.build();
} else {
this._loading = this._hasteMap.processFileChange(type, absPath);
this._loading.catch((e) => this._hasteMapError = e);
}
return this._loading;
};
this._loading = this._loading.then(resolve, resolve);
}
createPolyfill(options) {
return this._moduleCache.createPolyfill(options);
}
getHasteMap() {
return this._hasteMap;
}
}
Object.assign(DependencyGraph, {
Cache,
Fastfs,
FileWatcher,
Module,
Polyfill,
extractRequires,
getAssetDataFromName,
getPlatformExtension,
replacePatterns,
getInverseDependencies,
});
function NotFoundError() {
Error.call(this);
Error.captureStackTrace(this, this.constructor);
var msg = util.format.apply(util, arguments);
this.message = msg;
this.type = this.name = 'NotFoundError';
this.status = 404;
}
util.inherits(NotFoundError, Error);
module.exports = DependencyGraph;