Update from Friday March 27

- [React Native] Sync from github | Amjad Masad
- [react-packager] Watch asset roots for changes to update dependency graph | Amjad Masad
- Fix sourceTree of RCTActionSheet.xcodeproj | Alex Kotliarskyi
- Cancel contents animation before setting new contents in RCTNetworkImageView | Alex Akers
- [react-packager] move dependencies to root package.json | Amjad Masad
- Fix font crash on iOS < 8.2 | Nick Lockwood
- [react-packager] Fix node v0.11.14 query parse bug | Amjad Masad
- [ReactNative][Docs] Remove references to ReactNavigator from docs | Tadeu Zagallo
- [CLI] react-native start won't run from dir with spaces | Amjad Masad
- Revert .buckversion bumps. | Jakub Zika
- [react_native] Update default bundle name to org.reactjs.native.* | Krzysztof Magiera
- [react-packager] better error when main file not found | Amjad Masad
- [React Kit] Remove embarrassing TODOs | Alex Akers
- [ReactNative][MAdMan] Clean up after D1942269 | Philipp von Weitershausen
- flowify a few more Libraries | Basil Hosmer
- [ReactNative] PushNotificationIOS documentation | Eric Vicenti
- [ReactNative][CustomComponents] Update old headers | Tadeu Zagallo
- [ReactNative] UIViewControllerBasedStatusBarAppearance = NO in SampleApp | Alex Kotliarskyi
- [React Native] Fix CocoaPods spec | Alex Akers
- [ReactNative] Navigator Example Overhaul | Eric Vicenti
- [React Native] Fix incorrect if-statement in RCTGeolocation | Alex Akers
- [ReactNative] s/ReactKit/React/g | Tadeu Zagallo
- [React Native] [FRC - Don't accept] View border support | Nick Lockwood
- [Assets] Allow scripts to override assetRoots | Amjad Masad
- [ReactNative] Navigator docs | Eric Vicenti
- [ReactNative] License headers and renaming | Eric Vicenti
- [React Native] Add CocoaPods spec | Tadeu Zagallo
- Added explicit types for all view properties | Nick Lockwood
- [ReactNative] s/ReactNavigator/Navigator/ | Tadeu Zagallo
- [ReactNative] Add copyright header for code copied from the jQuery UI project | Martin Konicek
- [ReactNative] PanResponder documentation | Eric Vicenti
- [ReactNative] Add deep linking api | Tadeu Zagallo
- [ReactNative] Add gitignore example for SampleApp | Alex Kotliarskyi
- [ReactNative] Add react-native-start bin to react-native packge | Alex Kotliarskyi
- [ReactNative] Update package.json to be npm-ready | Christopher Chedeau
- [RFC][ReactNative] Integrate dev menu directly into RootView | Alex Kotliarskyi
- flowify Libraries/ReactIOS | Marshall Roch
- [WIP] Added support for italics and additional font weights | Nick Lockwood
- [ReactNative] Improve View documentation | Christopher Chedeau
- [react-packager] Readme | Amjad Masad
- Fix for incorrect contentSize reported by RCTScrollView | Nick Lockwood
- [ReactNative] Flow and doc formatting for NetInfo | Eric Vicenti
- [ReactNative] Document AppStateIOS | Eric Vicenti
- [MAdMan][Android] Make things look more Androidy | Philipp von Weitershausen
- flowified Libraries from Avik | Basil Hosmer
- flowify some Libraries | Basil Hosmer
- [ReactKit] Add shake development menu | Alex Kotliarskyi
- [ReactNative] Add debugger and change SampleApp files structure | Alex Kotliarskyi
- Flowify ReactIOSEventEmitter | Marshall Roch
- [react_native] JS files from D1941151: Allow fontWeight to be 100,200,...,900 | Krzysztof Magiera
- [ReactNative] Add snapshot tests for examples | Spencer Ahrens
- [ReactNative] bring back some native modules | Spencer Ahrens
- [ReactNative] Rename JSNavigationStack to ReactNavigator, rename scene config | Eric Vicenti
- [ReactNative] cleanup view example | Spencer Ahrens
- Flowify a bunch of Libraries | Marshall Roch
- [ReactNative] JSNavigationStack - Use key to blow away old scenes | Eric Vicenti
- [ReactNative] Add more logging to RCTJSONParse | Sumeet Vaidya
- Unfork UIManager | Nick Lockwood
- [react-packager] kill non-standard RAW_SOURCE_MAP | Amjad Masad
- Flowify Libraries/StyleSheet and Libraries/Text | Marshall Roch
- [ReactNative] Fix OSS Dependency Issues | Eric Vicenti
- [react-packager] Fix more issues with node modules | Amjad Masad
- [ReactNative] rename navigationOperations to navigator | Eric Vicenti
- JS files from D1936817: Add to XMLHttpRequest android and share code with ios | Olivia Bishop
- flowify some Libraries | Basil Hosmer
- last batch of UIExplorer flowification | Basil Hosmer
- [ReactNative] JSNavigationStack rename routeMapper to renderSceneForRoute | Eric Vicenti
- Flowify renderApplication | Marshall Roch
- [ReactNative] OSS Responder example | Eric Vicenti
- [ReactNative] Use oss TimerMixin | Tadeu Zagallo
- [ReactNative] Remove auto permission request from setAppIconBadgeNumber | Tadeu Zagallo
- [ReactNative] OSS snapshot tests | Spencer Ahrens
- [ReactNative] OSS JSNavigationStack w/ Examples | Eric Vicenti
- Fix build - remove relative import path | Jakub Zika
- Bump .buckversion to a5b8b8ef45d714018ba3542cf98d48ef6aab7088. | Jakub Zika
- [ReactNative] Open Source PushNotifications and move Badge Number methods and permission into it | Tadeu Zagallo
- [react-packager] Fix regression with transform errors | Amjad Masad
- Flowify TextStylePropTypes and fix a bug with unsupported props | Marshall Roch
- [ReactNative] Remove `arc build` instructions from require | Alex Kotliarskyi
- Flowify Library/Utilities/ | Marshall Roch
- [react-packager] Default to index.js from main if it's a dir | Amjad Masad
- [ReactNative] Use deprecated ix in TabBarExample | Amjad Masad
- [ReactNative] Expanded license on obj-c files | Christopher Chedeau
- [ReactNative] Expanded license on js files | Christopher Chedeau
- [ReactNative] Fix React Devtools integration | Alex Kotliarskyi
- [Text] Account for font leading so descenders are not clipped | Alex Kotliarskyi
- [ReactNative] Expanded license on js packager files | Christopher Chedeau
- more UIExplorer flow | Basil Hosmer
- [react-packager] Pick up package changes while running | Amjad Masad
- Added a graph view and a ReactNative metric that displays current queue and execution time for the JS thread. | Bryce Redd
- [ReactNative] Add NativeModules and DeviceEventEmitter to react-native exports | Alex Kotliarskyi
- [React Native] Fix iOS 7 crashes b/c missing Photos.fmwk | Alex Akers
- UIExplorer flowification | Basil Hosmer
- Add clearImmediate module | Marshall Roch
- [ReactNative] Print directories packager is serving files from | Alex Kotliarskyi
- Work around flow bug with exports | Marshall Roch
- [ReactNative] Move packager/init.sh to GitHub | Alex Kotliarskyi
- [ReactNative] Remove react-native/package.json | Christopher Chedeau
- [ReactNative] Returning actual contentSize for RCTScrollViewManager | Henry Lung
- declare timeoutID | Basil Hosmer
- [ReactNative] Add root package.json name back | Tadeu Zagallo
- [react-packager] Allow entry point extensions like .ios.js | Amjad Masad
- [react-native] Use SpreadProperty to make react-docgen happy | Felix Kling
- clean Examples/2048 | Basil Hosmer
- [ReactNative] Adjust packager default root when running from within node_modules | Alex Kotliarskyi
- [ReactNative] Add missing websocket dependency | Alex Kotliarskyi
- [react-packager] change all but one `ix` to `require` | Amjad Masad
- [react-packager] Make sure projectRoots is converted to an array | Amjad Masad
- [ReactNative] Init script that bootstraps new Xcode project | Alex Kotliarskyi
- [ReactNative] New SampleApp | Alex Kotliarskyi
- [ReactNative] Touchable invoke press on longPress when longPress handler missing | Eric Vicenti
- [ReactNative] Commit missing RCTWebSocketDebugger.xcodeproj | Alex Kotliarskyi
This commit is contained in:
Amjad Masad 2015-03-27 11:46:17 -07:00
parent 4db001a925
commit e7d1d95d0d
9 changed files with 198 additions and 65 deletions

View File

@ -69,11 +69,7 @@ if (options.assetRoots) {
options.assetRoots = options.assetRoots.split(',');
}
} else {
if (__dirname.match(/node_modules\/react-native\/packager$/)) {
options.assetRoots = [path.resolve(__dirname, '../../..')];
} else {
options.assetRoots = [path.resolve(__dirname, '..')];
}
options.assetRoots = [path.resolve(__dirname, '..')];
}
console.log('\n' +

View File

@ -10,4 +10,4 @@
ulimit -n 4096
THIS_DIR=$(dirname "$0")
node $THIS_DIR/packager.js "$@"
node "$THIS_DIR/packager.js" "$@"

View File

@ -821,6 +821,55 @@ describe('DependencyGraph', function() {
});
});
pit('updates module dependencies on asset add', function() {
var root = '/root';
var filesystem = fs.__setMockFilesystem({
'root': {
'index.js': [
'/**',
' * @providesModule index',
' */',
'require("image!foo")'
].join('\n'),
},
});
var dgraph = new DependencyGraph({
roots: [root],
assetRoots: [root],
assetExts: ['png'],
fileWatcher: fileWatcher
});
return dgraph.load().then(function() {
expect(dgraph.getOrderedDependencies('/root/index.js'))
.toEqual([
{ id: 'index', altId: '/root/index.js',
path: '/root/index.js',
dependencies: ['image!foo']
}
]);
filesystem.root['foo.png'] = '';
triggerFileChange('add', 'foo.png', root);
return dgraph.load().then(function() {
expect(dgraph.getOrderedDependencies('/root/index.js'))
.toEqual([
{ id: 'index', altId: '/root/index.js',
path: '/root/index.js',
dependencies: ['image!foo']
},
{ id: 'image!foo',
path: '/root/foo.png',
dependencies: [],
isAsset: true,
},
]);
});
});
});
pit('runs changes through ignore filter', function() {
var root = '/root';
var filesystem = fs.__setMockFilesystem({

View File

@ -86,7 +86,11 @@ DependecyGraph.prototype.load = function() {
DependecyGraph.prototype.getOrderedDependencies = function(entryPath) {
var absolutePath = this._getAbsolutePath(entryPath);
if (absolutePath == null) {
throw new Error('Cannot find entry file in any of the roots: ' + entryPath);
throw new NotFoundError(
'Cannot find entry file %s in any of the roots: %j',
entryPath,
this._roots
);
}
var module = this._graph[absolutePath];
@ -478,7 +482,12 @@ DependecyGraph.prototype._lookupPackage = function(modulePath) {
/**
* Process a filewatcher change event.
*/
DependecyGraph.prototype._processFileChange = function(eventType, filePath, root, stat) {
DependecyGraph.prototype._processFileChange = function(
eventType,
filePath,
root,
stat
) {
var absPath = path.join(root, filePath);
if (this._ignoreFilePath(absPath)) {
return;
@ -486,6 +495,11 @@ DependecyGraph.prototype._processFileChange = function(eventType, filePath, root
this._debugUpdateEvents.push({event: eventType, path: filePath});
if (this._assetExts.indexOf(extname(filePath)) > -1) {
this._processAssetChange(eventType, absPath);
return;
}
var isPackage = path.basename(filePath) === 'package.json';
if (eventType === 'delete') {
if (isPackage) {
@ -520,7 +534,8 @@ DependecyGraph.prototype.getDebugInfo = function() {
};
/**
* Searches all roots for the file and returns the first one that has file of the same path.
* Searches all roots for the file and returns the first one that has file of
* the same path.
*/
DependecyGraph.prototype._getAbsolutePath = function(filePath) {
if (isAbsolutePath(filePath)) {
@ -543,12 +558,43 @@ DependecyGraph.prototype._buildAssetMap = function() {
return q();
}
var self = this;
return buildAssetMap(this._assetRoots, this._assetExts)
.then(function(map) {
self._assetMap = map;
return map;
this._assetMap = Object.create(null);
return buildAssetMap(
this._assetRoots,
this._processAsset.bind(this)
);
};
DependecyGraph.prototype._processAsset = function(file) {
var ext = extname(file);
if (this._assetExts.indexOf(ext) !== -1) {
var name = assetName(file, ext);
if (this._assetMap[name] != null) {
debug('Conflcting assets', name);
}
this._assetMap[name] = new ModuleDescriptor({
id: 'image!' + name,
path: path.resolve(file),
isAsset: true,
dependencies: [],
});
}
};
DependecyGraph.prototype._processAssetChange = function(eventType, file) {
if (this._assetMap == null) {
return;
}
var name = assetName(file, extname(file));
if (eventType === 'change' || eventType === 'delete') {
delete this._assetMap[name];
}
if (eventType === 'change' || eventType === 'add') {
this._processAsset(file);
}
};
/**
@ -623,15 +669,14 @@ function readAndStatDir(dir) {
* Given a list of roots and list of extensions find all the files in
* the directory with that extension and build a map of those assets.
*/
function buildAssetMap(roots, exts) {
function buildAssetMap(roots, processAsset) {
var queue = roots.slice(0);
var map = Object.create(null);
function search() {
var root = queue.shift();
if (root == null) {
return q(map);
return q();
}
return readAndStatDir(root).spread(function(files, stats) {
@ -639,21 +684,7 @@ function buildAssetMap(roots, exts) {
if (stats[i].isDirectory()) {
queue.push(file);
} else {
var ext = path.extname(file).replace(/^\./, '');
if (exts.indexOf(ext) !== -1) {
var assetName = path.basename(file, '.' + ext)
.replace(/@[\d\.]+x/, '');
if (map[assetName] != null) {
debug('Conflcting assets', assetName);
}
map[assetName] = new ModuleDescriptor({
id: 'image!' + assetName,
path: path.resolve(file),
isAsset: true,
dependencies: [],
});
}
processAsset(file);
}
});
@ -664,4 +695,24 @@ function buildAssetMap(roots, exts) {
return search();
}
function assetName(file, ext) {
return path.basename(file, '.' + ext).replace(/@[\d\.]+x/, '');
}
function extname(name) {
return path.extname(name).replace(/^\./, '');
}
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;
}
NotFoundError.__proto__ = Error.prototype;
module.exports = DependecyGraph;

View File

@ -51,15 +51,15 @@ var validateOpts = declareOpts({
type: 'array',
default: [],
},
fileWatcher: {
type: 'object',
required: true,
},
});
function HasteDependencyResolver(options) {
var opts = validateOpts(options);
this._fileWatcher = opts.nonPersistent
? FileWatcher.createDummyWatcher()
: new FileWatcher(opts.projectRoots);
this._depGraph = new DependencyGraph({
roots: opts.projectRoots,
assetRoots: opts.assetRoots,
@ -67,7 +67,7 @@ function HasteDependencyResolver(options) {
return filepath.indexOf('__tests__') !== -1 ||
(opts.blacklistRE && opts.blacklistRE.test(filepath));
},
fileWatcher: this._fileWatcher,
fileWatcher: opts.fileWatcher,
});
@ -164,10 +164,6 @@ HasteDependencyResolver.prototype.wrapModule = function(module, code) {
});
};
HasteDependencyResolver.prototype.end = function() {
return this._fileWatcher.end();
};
HasteDependencyResolver.prototype.getDebugInfo = function() {
return this._depGraph.getDebugInfo();
};

View File

@ -21,6 +21,7 @@ describe('FileWatcher', function() {
var Watcher;
beforeEach(function() {
require('mock-modules').dumpCache();
FileWatcher = require('../');
Watcher = require('sane').WatchmanWatcher;
Watcher.prototype.once.mockImplementation(function(type, callback) {

View File

@ -16,7 +16,7 @@ var exec = require('child_process').exec;
var Promise = q.Promise;
var detectingWatcherClass = new Promise(function(resolve, reject) {
var detectingWatcherClass = new Promise(function(resolve) {
exec('which watchman', function(err, out) {
if (err || out.length === 0) {
resolve(sane.NodeWatcher);
@ -30,14 +30,23 @@ module.exports = FileWatcher;
var MAX_WAIT_TIME = 3000;
function FileWatcher(projectRoots) {
var self = this;
// Singleton
var fileWatcher = null;
function FileWatcher(rootConfigs) {
if (fileWatcher) {
// This allows us to optimize watching in the future by merging roots etc.
throw new Error('FileWatcher can only be instantiated once');
}
fileWatcher = this;
this._loading = q.all(
projectRoots.map(createWatcher)
rootConfigs.map(createWatcher)
).then(function(watchers) {
watchers.forEach(function(watcher) {
watcher.on('all', function(type, filepath, root) {
self.emit('all', type, filepath, root);
fileWatcher.emit('all', type, filepath, root);
});
});
return watchers;
@ -50,21 +59,14 @@ util.inherits(FileWatcher, EventEmitter);
FileWatcher.prototype.end = function() {
return this._loading.then(function(watchers) {
watchers.forEach(function(watcher) {
delete watchersByRoot[watcher._root];
return q.ninvoke(watcher, 'close');
});
});
};
var watchersByRoot = Object.create(null);
function createWatcher(root) {
if (watchersByRoot[root] != null) {
return Promise.resolve(watchersByRoot[root]);
}
function createWatcher(rootConfig) {
return detectingWatcherClass.then(function(Watcher) {
var watcher = new Watcher(root, {glob: ['**/*.js', '**/package.json']});
var watcher = new Watcher(rootConfig.dir, rootConfig.globs);
return new Promise(function(resolve, reject) {
var rejectTimeout = setTimeout(function() {
@ -77,8 +79,6 @@ function createWatcher(root) {
watcher.once('ready', function() {
clearTimeout(rejectTimeout);
watchersByRoot[root] = watcher;
watcher._root = root;
resolve(watcher);
});
});

View File

@ -56,6 +56,14 @@ var validateOpts = declareOpts({
type: 'array',
required: false,
},
assetExts: {
type: 'array',
default: ['png'],
},
fileWatcher: {
type: 'object',
required: true,
},
});
function Packager(options) {
@ -70,6 +78,7 @@ function Packager(options) {
nonPersistent: opts.nonPersistent,
moduleFormat: opts.moduleFormat,
assetRoots: opts.assetRoots,
fileWatcher: opts.fileWatcher,
});
this._transformer = new Transformer({
@ -83,10 +92,7 @@ function Packager(options) {
}
Packager.prototype.kill = function() {
return q.all([
this._transformer.kill(),
this._resolver.end(),
]);
return this._transformer.kill();
};
Packager.prototype.package = function(main, runModule, sourceMapUrl, isDev) {

View File

@ -55,18 +55,49 @@ var validateOpts = declareOpts({
type: 'array',
required: false,
},
assetExts: {
type: 'array',
default: ['png'],
},
});
function Server(options) {
var opts = validateOpts(options);
this._projectRoots = opts.projectRoots;
this._packages = Object.create(null);
this._packager = new Packager(opts);
this._changeWatchers = [];
var watchRootConfigs = opts.projectRoots.map(function(dir) {
return {
dir: dir,
globs: [
'**/*.js',
'**/package.json',
]
};
});
if (opts.assetRoots != null) {
watchRootConfigs = watchRootConfigs.concat(
opts.assetRoots.map(function(dir) {
return {
dir: dir,
globs: opts.assetExts.map(function(ext) {
return '**/*.' + ext;
}),
};
})
);
}
this._fileWatcher = options.nonPersistent
? FileWatcher.createDummyWatcher()
: new FileWatcher(options.projectRoots);
: new FileWatcher(watchRootConfigs);
var packagerOpts = Object.create(opts);
packagerOpts.fileWatcher = this._fileWatcher;
this._packager = new Packager(packagerOpts);
var onFileChange = this._onFileChange.bind(this);
this._fileWatcher.on('all', onFileChange);
@ -246,6 +277,9 @@ Server.prototype.processRequest = function(req, res, next) {
function getOptionsFromUrl(reqUrl) {
// `true` to parse the query param as an object.
var urlObj = url.parse(reqUrl, true);
// node v0.11.14 bug see https://github.com/facebook/react-native/issues/218
urlObj.query = urlObj.query || {};
var pathname = urlObj.pathname;
// Backwards compatibility. Options used to be as added as '.' to the
@ -281,11 +315,11 @@ function getBoolOptionFromQuery(query, opt, defaultVal) {
}
function handleError(res, error) {
res.writeHead(500, {
res.writeHead(error.status || 500, {
'Content-Type': 'application/json; charset=UTF-8',
});
if (error.type === 'TransformError') {
if (error.type === 'TransformError' || error.type === 'NotFoundError') {
res.end(JSON.stringify(error));
} else {
console.error(error.stack || error);