2015-02-09 updates
- [react-server] Support multiple roots | Amjad Masad - [react-packager] Add a helpful error message when watcher fails to start | Amjad Masad - [madman] Fix review screen button and animation | Eric Vicenti
This commit is contained in:
parent
d29d07ee06
commit
ff2f1ce945
|
@ -88,7 +88,7 @@ class Subscribable {
|
||||||
this._internalEmitter = new EventEmitter();
|
this._internalEmitter = new EventEmitter();
|
||||||
this._eventMapping = eventMapping || (data => data);
|
this._eventMapping = eventMapping || (data => data);
|
||||||
|
|
||||||
eventEmitter.addListener(
|
this._upstreamSubscription = eventEmitter.addListener(
|
||||||
eventName,
|
eventName,
|
||||||
this._handleEmit,
|
this._handleEmit,
|
||||||
this
|
this
|
||||||
|
@ -105,6 +105,13 @@ class Subscribable {
|
||||||
return this._lastData;
|
return this._lastData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from the upstream EventEmitter
|
||||||
|
*/
|
||||||
|
cleanup() {
|
||||||
|
this._upstreamSubscription && this._upstreamSubscription.remove();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new listener to the subscribable. This should almost never be used
|
* Add a new listener to the subscribable. This should almost never be used
|
||||||
* directly, and instead through Subscribable.Mixin.subscribeTo
|
* directly, and instead through Subscribable.Mixin.subscribeTo
|
||||||
|
@ -229,6 +236,55 @@ Subscribable.Mixin = {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a Subscribable store, scoped to the component, that can be passed to
|
||||||
|
* children. The component will automatically clean up the subscribable's
|
||||||
|
* subscription to the eventEmitter when unmounting.
|
||||||
|
*
|
||||||
|
* `provideSubscribable` will always return the same Subscribable for any
|
||||||
|
* particular emitter/eventName combo, so it can be called directly from
|
||||||
|
* render, and it will never create duplicate Subscribables.
|
||||||
|
*
|
||||||
|
* @param {EventEmitter} eventEmitter Emitter to trigger subscription events.
|
||||||
|
* @param {string} eventName Name of emitted event that triggers subscription
|
||||||
|
* events.
|
||||||
|
* @param {function} eventMapping (optional) Function to convert the output
|
||||||
|
* of the eventEmitter to the subscription output.
|
||||||
|
* @param {function} getInitData (optional) Async function to grab the initial
|
||||||
|
* data to publish. Signature `function(successCallback, errorCallback)`.
|
||||||
|
* The resolved data will be transformed with the eventMapping before it
|
||||||
|
* gets emitted.
|
||||||
|
*/
|
||||||
|
provideSubscribable: function(eventEmitter, eventName, eventMapping, getInitData) {
|
||||||
|
this._localSubscribables = this._localSubscribables || {};
|
||||||
|
this._localSubscribables[eventEmitter] =
|
||||||
|
this._localSubscribables[eventEmitter] || {};
|
||||||
|
if (!this._localSubscribables[eventEmitter][eventName]) {
|
||||||
|
this._localSubscribables[eventEmitter][eventName] =
|
||||||
|
new Subscribable(eventEmitter, eventName, eventMapping, getInitData);
|
||||||
|
}
|
||||||
|
return this._localSubscribables[eventEmitter][eventName];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes any local Subscribables created with `provideSubscribable`, so the
|
||||||
|
* component can unmount without leaving any dangling listeners on
|
||||||
|
* eventEmitters
|
||||||
|
*/
|
||||||
|
_cleanupLocalSubscribables: function() {
|
||||||
|
if (!this._localSubscribables) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var emitterSubscribables;
|
||||||
|
Object.keys(this._localSubscribables).forEach((eventEmitter) => {
|
||||||
|
emitterSubscribables = this._localSubscribables[eventEmitter];
|
||||||
|
Object.keys(emitterSubscribables).forEach((eventName) => {
|
||||||
|
emitterSubscribables[eventName].cleanup();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this._localSubscribables = null;
|
||||||
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
this._endSubscribableLifespanCallbacks = [];
|
this._endSubscribableLifespanCallbacks = [];
|
||||||
|
|
||||||
|
@ -241,6 +297,8 @@ Subscribable.Mixin = {
|
||||||
// remaining subscriptions
|
// remaining subscriptions
|
||||||
this._endSubscribableLifespan && this._endSubscribableLifespan();
|
this._endSubscribableLifespan && this._endSubscribableLifespan();
|
||||||
|
|
||||||
|
this._cleanupLocalSubscribables();
|
||||||
|
|
||||||
// DEPRECATED addListenerOn* usage uses _subscribableSubscriptions array
|
// DEPRECATED addListenerOn* usage uses _subscribableSubscriptions array
|
||||||
// instead of lifespan
|
// instead of lifespan
|
||||||
this._subscribableSubscriptions.forEach(
|
this._subscribableSubscriptions.forEach(
|
||||||
|
|
24
README.md
24
README.md
|
@ -34,9 +34,9 @@ products with no compromises in quality or capability.
|
||||||
Get up and running with our Movies sample app:
|
Get up and running with our Movies sample app:
|
||||||
|
|
||||||
1. Once you have the repo cloned and met all the requirements above, start the
|
1. Once you have the repo cloned and met all the requirements above, start the
|
||||||
packager that will transform your JS code on-the-fly:
|
packager that will transform your JS code on-the-fly:
|
||||||
`npm install`
|
`npm install`
|
||||||
`npm start`
|
`npm start`
|
||||||
2. Open the `Examples/Movies/Movies.xcodeproj` project in Xcode.
|
2. Open the `Examples/Movies/Movies.xcodeproj` project in Xcode.
|
||||||
3. Make sure the target is set to `Movies` and that you have an iOS simulator
|
3. Make sure the target is set to `Movies` and that you have an iOS simulator
|
||||||
selected to run the app.
|
selected to run the app.
|
||||||
|
@ -93,27 +93,27 @@ the responder system.
|
||||||
|
|
||||||
# FAQ
|
# FAQ
|
||||||
|
|
||||||
Q. How does debugging work? Can I set breakpoints in my JS?
|
Q. How does debugging work? Can I set breakpoints in my JS?
|
||||||
A. We are going to add the ability to use the Chrome developer tools soon. We
|
A. We are going to add the ability to use the Chrome developer tools soon. We
|
||||||
are very passionate about building the best possible developer experience.
|
are very passionate about building the best possible developer experience.
|
||||||
|
|
||||||
Q. When is this coming to Android/Windows/OS X/etc?
|
Q. When is this coming to Android/Windows/OS X/etc?
|
||||||
A. We're working on Android, and we are excited to release it as soon as we can.
|
A. We're working on Android, and we are excited to release it as soon as we can.
|
||||||
We are looking forward to the community helping us target other platforms as
|
We are looking forward to the community helping us target other platforms as
|
||||||
well :)
|
well :)
|
||||||
|
|
||||||
Q. How do I create my own app?
|
Q. How do I create my own app?
|
||||||
A. Copy the entire `Examples/TicTacToe` folder, rename stuff in Xcode, and
|
A. Copy the entire `Examples/TicTacToe` folder, rename stuff in Xcode, and
|
||||||
replace the `TicTacToeApp.js` with your own. Then, in `AppDelegate.m`, update
|
replace the `TicTacToeApp.js` with your own. Then, in `AppDelegate.m`, update
|
||||||
`moduleName` to match your call to
|
`moduleName` to match your call to
|
||||||
`Bundler.registerComponent(<moduleName>, <componentName>)` at the bottom of your
|
`Bundler.registerComponent(<moduleName>, <componentName>)` at the bottom of your
|
||||||
JS file, and update `jsCodeLocation` to match your JS file name and location.
|
JS file, and update `jsCodeLocation` to match your JS file name and location.
|
||||||
|
|
||||||
Q. Can I submit my own React Native app to the App Store?
|
Q. Can I submit my own React Native app to the App Store?
|
||||||
A. Not yet, but you will be able to soon. If you build something you want to
|
A. Not yet, but you will be able to soon. If you build something you want to
|
||||||
submit to the App Store, come talk to us ASAP.
|
submit to the App Store, come talk to us ASAP.
|
||||||
|
|
||||||
Q. How do I deploy to my device?
|
Q. How do I deploy to my device?
|
||||||
A. You can change `localhost` in `AppDelegate.m` to your laptop's IP address and
|
A. You can change `localhost` in `AppDelegate.m` to your laptop's IP address and
|
||||||
grab the bundle over the same Wi-Fi network. You can also download the bundle
|
grab the bundle over the same Wi-Fi network. You can also download the bundle
|
||||||
that the React packager generates, save it to the file `main.jsbundle`, and add it
|
that the React packager generates, save it to the file `main.jsbundle`, and add it
|
||||||
|
@ -121,20 +121,20 @@ as a static resource in your Xcode project. Then set the `jsCodeLocation` in
|
||||||
`AppDelegate.m` to point to that file and deploy to your device like you would
|
`AppDelegate.m` to point to that file and deploy to your device like you would
|
||||||
any other app.
|
any other app.
|
||||||
|
|
||||||
Q. What's up with this private repo? Why aren't you just open sourcing it now?
|
Q. What's up with this private repo? Why aren't you just open sourcing it now?
|
||||||
A. We want input from the React community before we open the floodgates so we
|
A. We want input from the React community before we open the floodgates so we
|
||||||
can incorporate your feedback, and we also have a bunch more features we want to
|
can incorporate your feedback, and we also have a bunch more features we want to
|
||||||
add to make a more complete offering before we open source.
|
add to make a more complete offering before we open source.
|
||||||
|
|
||||||
Q. Do you have to ship a JS runtime with your apps?
|
Q. Do you have to ship a JS runtime with your apps?
|
||||||
A. No, we just use the JavaScriptCore public API that is part of iOS 7 and
|
A. No, we just use the JavaScriptCore public API that is part of iOS 7 and
|
||||||
later.
|
later.
|
||||||
|
|
||||||
Q. How do I add more native capabilities?
|
Q. How do I add more native capabilities?
|
||||||
A. React Native is designed to be extensible - come talk to us, we would love to
|
A. React Native is designed to be extensible - come talk to us, we would love to
|
||||||
work with you.
|
work with you.
|
||||||
|
|
||||||
Q. Can I reuse existing iOS code?
|
Q. Can I reuse existing iOS code?
|
||||||
A. Yes, React Native is designed to be extensible and allow integration of all
|
A. Yes, React Native is designed to be extensible and allow integration of all
|
||||||
sorts of native components, such as `UINavigationController` (available as
|
sorts of native components, such as `UINavigationController` (available as
|
||||||
`<NavigatorIOS>`), `MKMapView` (not available yet), or your own custom
|
`<NavigatorIOS>`), `MKMapView` (not available yet), or your own custom
|
||||||
|
|
|
@ -28,8 +28,8 @@ var options = parseCommandLine([{
|
||||||
default: 8081,
|
default: 8081,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
if (!options.projectRoot) {
|
if (!options.projectRoots) {
|
||||||
options.projectRoot = path.resolve(__dirname, '..');
|
options.projectRoots = [path.resolve(__dirname, '..')];
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('\n' +
|
console.log('\n' +
|
||||||
|
|
|
@ -21,12 +21,8 @@ describe('DependencyGraph', function() {
|
||||||
DependencyGraph = require('../index');
|
DependencyGraph = require('../index');
|
||||||
|
|
||||||
fileWatcher = {
|
fileWatcher = {
|
||||||
getWatcher: function() {
|
on: function() {
|
||||||
return q({
|
return this;
|
||||||
on: function() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -50,7 +46,7 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
|
@ -79,7 +75,7 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
|
@ -109,7 +105,7 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
|
@ -179,7 +175,7 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/somedir/somefile.js'))
|
expect(dgraph.getOrderedDependencies('/root/somedir/somefile.js'))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
|
@ -220,7 +216,7 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
|
@ -249,7 +245,7 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
|
@ -284,7 +280,7 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
|
@ -324,7 +320,7 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
|
@ -364,7 +360,7 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
|
@ -395,16 +391,12 @@ describe('DependencyGraph', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
fileWatcher = {
|
fileWatcher = {
|
||||||
getWatcher: function() {
|
on: function(eventType, callback) {
|
||||||
return q({
|
if (eventType !== 'all') {
|
||||||
on: function(eventType, callback) {
|
throw new Error('Can only handle "all" event in watcher.');
|
||||||
if (eventType !== 'all') {
|
}
|
||||||
throw new Error('Can only handle "all" event in watcher.');
|
triggerFileChange = callback;
|
||||||
}
|
return this;
|
||||||
triggerFileChange = callback;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -436,11 +428,11 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
filesystem.root['index.js'] =
|
filesystem.root['index.js'] =
|
||||||
filesystem.root['index.js'].replace('require("foo")', '');
|
filesystem.root['index.js'].replace('require("foo")', '');
|
||||||
triggerFileChange('change', 'index.js');
|
triggerFileChange('change', 'index.js', root);
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
|
@ -484,11 +476,11 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
filesystem.root['index.js'] =
|
filesystem.root['index.js'] =
|
||||||
filesystem.root['index.js'].replace('require("foo")', '');
|
filesystem.root['index.js'].replace('require("foo")', '');
|
||||||
triggerFileChange('change', 'index.js');
|
triggerFileChange('change', 'index.js', root);
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
|
@ -532,10 +524,10 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
delete filesystem.root.foo;
|
delete filesystem.root.foo;
|
||||||
triggerFileChange('delete', 'foo.js');
|
triggerFileChange('delete', 'foo.js', root);
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
|
@ -579,7 +571,7 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
filesystem.root['bar.js'] = [
|
filesystem.root['bar.js'] = [
|
||||||
'/**',
|
'/**',
|
||||||
|
@ -587,10 +579,10 @@ describe('DependencyGraph', function() {
|
||||||
' */',
|
' */',
|
||||||
'require("foo")'
|
'require("foo")'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
triggerFileChange('add', 'bar.js');
|
triggerFileChange('add', 'bar.js', root);
|
||||||
|
|
||||||
filesystem.root.aPackage['main.js'] = 'require("bar")';
|
filesystem.root.aPackage['main.js'] = 'require("bar")';
|
||||||
triggerFileChange('change', 'aPackage/main.js');
|
triggerFileChange('change', 'aPackage/main.js', root);
|
||||||
|
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||||
|
@ -644,7 +636,7 @@ describe('DependencyGraph', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
var dgraph = new DependencyGraph({
|
var dgraph = new DependencyGraph({
|
||||||
root: root,
|
roots: [root],
|
||||||
fileWatcher: fileWatcher,
|
fileWatcher: fileWatcher,
|
||||||
ignoreFilePath: function(filePath) {
|
ignoreFilePath: function(filePath) {
|
||||||
if (filePath === '/root/bar.js') {
|
if (filePath === '/root/bar.js') {
|
||||||
|
@ -660,10 +652,10 @@ describe('DependencyGraph', function() {
|
||||||
' */',
|
' */',
|
||||||
'require("foo")'
|
'require("foo")'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
triggerFileChange('add', 'bar.js');
|
triggerFileChange('add', 'bar.js', root);
|
||||||
|
|
||||||
filesystem.root.aPackage['main.js'] = 'require("bar")';
|
filesystem.root.aPackage['main.js'] = 'require("bar")';
|
||||||
triggerFileChange('change', 'aPackage/main.js');
|
triggerFileChange('change', 'aPackage/main.js', root);
|
||||||
|
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||||
|
@ -711,7 +703,7 @@ describe('DependencyGraph', function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var dgraph = new DependencyGraph({root: root, fileWatcher: fileWatcher});
|
var dgraph = new DependencyGraph({roots: [root], fileWatcher: fileWatcher});
|
||||||
return dgraph.load().then(function() {
|
return dgraph.load().then(function() {
|
||||||
triggerFileChange('change', 'aPackage', '/root', {
|
triggerFileChange('change', 'aPackage', '/root', {
|
||||||
isDirectory: function(){ return true; }
|
isDirectory: function(){ return true; }
|
||||||
|
|
|
@ -14,10 +14,10 @@ var lstat = q.nfbind(fs.lstat);
|
||||||
var realpath = q.nfbind(fs.realpath);
|
var realpath = q.nfbind(fs.realpath);
|
||||||
|
|
||||||
function DependecyGraph(options) {
|
function DependecyGraph(options) {
|
||||||
this._root = options.root;
|
this._roots = options.roots;
|
||||||
this._ignoreFilePath = options.ignoreFilePath || function(){};
|
this._ignoreFilePath = options.ignoreFilePath || function(){};
|
||||||
this._loaded = false;
|
this._loaded = false;
|
||||||
this._queue = [this._root];
|
this._queue = this._roots.slice();
|
||||||
this._graph = Object.create(null);
|
this._graph = Object.create(null);
|
||||||
this._packageByRoot = Object.create(null);
|
this._packageByRoot = Object.create(null);
|
||||||
this._packagesById = Object.create(null);
|
this._packagesById = Object.create(null);
|
||||||
|
@ -36,16 +36,14 @@ DependecyGraph.prototype.load = function() {
|
||||||
* Given an entry file return an array of all the dependent module descriptors.
|
* Given an entry file return an array of all the dependent module descriptors.
|
||||||
*/
|
*/
|
||||||
DependecyGraph.prototype.getOrderedDependencies = function(entryPath) {
|
DependecyGraph.prototype.getOrderedDependencies = function(entryPath) {
|
||||||
var absolutePath;
|
var absolutePath = this._getAbsolutePath(entryPath);
|
||||||
if (!isAbsolutePath(entryPath)) {
|
if (absolutePath == null) {
|
||||||
absolutePath = path.join(this._root, entryPath);
|
throw new Error('Cannot find entry file in any of the roots: ' + entryPath);
|
||||||
} else {
|
|
||||||
absolutePath = entryPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var module = this._graph[absolutePath];
|
var module = this._graph[absolutePath];
|
||||||
if (module == null) {
|
if (module == null) {
|
||||||
throw new Error('Module with path "' + absolutePath + '" is not in graph');
|
throw new Error('Module with path "' + entryPath + '" is not in graph');
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -172,15 +170,11 @@ DependecyGraph.prototype.resolveDependency = function(
|
||||||
*/
|
*/
|
||||||
DependecyGraph.prototype._init = function() {
|
DependecyGraph.prototype._init = function() {
|
||||||
var processChange = this._processFileChange.bind(this);
|
var processChange = this._processFileChange.bind(this);
|
||||||
var loadingWatcher = this._fileWatcher.getWatcher();
|
var watcher = this._fileWatcher;
|
||||||
|
|
||||||
this._loading = this.load()
|
this._loading = this.load().then(function() {
|
||||||
.then(function() {
|
watcher.on('all', processChange);
|
||||||
return loadingWatcher;
|
});
|
||||||
})
|
|
||||||
.then(function(watcher) {
|
|
||||||
watcher.on('all', processChange);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -370,7 +364,6 @@ DependecyGraph.prototype._updateGraphWithModule = function(module) {
|
||||||
* Find the nearest package to a module.
|
* Find the nearest package to a module.
|
||||||
*/
|
*/
|
||||||
DependecyGraph.prototype._lookupPackage = function(modulePath) {
|
DependecyGraph.prototype._lookupPackage = function(modulePath) {
|
||||||
var root = this._root;
|
|
||||||
var packageByRoot = this._packageByRoot;
|
var packageByRoot = this._packageByRoot;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -380,8 +373,7 @@ DependecyGraph.prototype._lookupPackage = function(modulePath) {
|
||||||
// ideally we stop once we're outside root and this can be a simple child
|
// ideally we stop once we're outside root and this can be a simple child
|
||||||
// dir check. However, we have to support modules that was symlinked inside
|
// dir check. However, we have to support modules that was symlinked inside
|
||||||
// our project root.
|
// our project root.
|
||||||
if (!path.relative(root, currDir) === '' || currDir === '.'
|
if (currDir === '/') {
|
||||||
|| currDir === '/') {
|
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
var packageJson = packageByRoot[currDir];
|
var packageJson = packageByRoot[currDir];
|
||||||
|
@ -400,7 +392,7 @@ DependecyGraph.prototype._lookupPackage = function(modulePath) {
|
||||||
* Process a filewatcher change event.
|
* 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(this._root, filePath);
|
var absPath = path.join(root, filePath);
|
||||||
if (this._ignoreFilePath(absPath)) {
|
if (this._ignoreFilePath(absPath)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -420,6 +412,24 @@ DependecyGraph.prototype._processFileChange = function(eventType, filePath, root
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)) {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0, root; root = this._roots[i]; i++) {
|
||||||
|
var absPath = path.join(root, filePath);
|
||||||
|
if (this._graph[absPath]) {
|
||||||
|
return absPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract all required modules from a `code` string.
|
* Extract all required modules from a `code` string.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -23,13 +23,14 @@ var DEFINE_MODULE_REPLACE_RE = /_moduleName_|_code_|_deps_/g;
|
||||||
var REL_REQUIRE_STMT = /require\(['"]([\.\/0-9A-Z_$\-]*)['"]\)/gi;
|
var REL_REQUIRE_STMT = /require\(['"]([\.\/0-9A-Z_$\-]*)['"]\)/gi;
|
||||||
|
|
||||||
function HasteDependencyResolver(config) {
|
function HasteDependencyResolver(config) {
|
||||||
|
this._fileWatcher = new FileWatcher(config.projectRoots);
|
||||||
this._depGraph = new DependencyGraph({
|
this._depGraph = new DependencyGraph({
|
||||||
root: config.projectRoot,
|
roots: config.projectRoots,
|
||||||
ignoreFilePath: function(filepath) {
|
ignoreFilePath: function(filepath) {
|
||||||
return filepath.indexOf('__tests__') !== -1 ||
|
return filepath.indexOf('__tests__') !== -1 ||
|
||||||
(config.blacklistRE && config.blacklistRE.test(filepath));
|
(config.blacklistRE && config.blacklistRE.test(filepath));
|
||||||
},
|
},
|
||||||
fileWatcher: new FileWatcher(config.projectRoot)
|
fileWatcher: this._fileWatcher
|
||||||
});
|
});
|
||||||
|
|
||||||
this._polyfillModuleNames = [
|
this._polyfillModuleNames = [
|
||||||
|
@ -118,4 +119,8 @@ HasteDependencyResolver.prototype.wrapModule = function(module, code) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HasteDependencyResolver.prototype.end = function() {
|
||||||
|
return this._fileWatcher.end();
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = HasteDependencyResolver;
|
module.exports = HasteDependencyResolver;
|
||||||
|
|
|
@ -16,15 +16,17 @@ describe('FileWatcher', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('it should get the watcher instance when ready', function() {
|
it('it should get the watcher instance when ready', function() {
|
||||||
var fileWatcher = new FileWatcher('rootDir');
|
var fileWatcher = new FileWatcher(['rootDir']);
|
||||||
return fileWatcher.getWatcher().then(function(watcher) {
|
return fileWatcher._loading.then(function(watchers) {
|
||||||
expect(watcher instanceof Watcher).toBe(true);
|
watchers.forEach(function(watcher) {
|
||||||
|
expect(watcher instanceof Watcher).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('it should end the watcher', function() {
|
pit('it should end the watcher', function() {
|
||||||
var fileWatcher = new FileWatcher('rootDir');
|
var fileWatcher = new FileWatcher(['rootDir']);
|
||||||
Watcher.prototype.close.mockImplementation(function(callback) {
|
Watcher.prototype.close.mockImplementation(function(callback) {
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var sane = require('sane');
|
var sane = require('sane');
|
||||||
var q = require('q');
|
var q = require('q');
|
||||||
|
var util = require('util');
|
||||||
var exec = require('child_process').exec;
|
var exec = require('child_process').exec;
|
||||||
|
|
||||||
var Promise = q.Promise;
|
var Promise = q.Promise;
|
||||||
|
@ -20,37 +22,57 @@ module.exports = FileWatcher;
|
||||||
|
|
||||||
var MAX_WAIT_TIME = 3000;
|
var MAX_WAIT_TIME = 3000;
|
||||||
|
|
||||||
var memoizedInstances = Object.create(null);
|
function FileWatcher(projectRoots) {
|
||||||
|
var self = this;
|
||||||
|
this._loading = q.all(
|
||||||
|
projectRoots.map(createWatcher)
|
||||||
|
).then(function(watchers) {
|
||||||
|
watchers.forEach(function(watcher) {
|
||||||
|
watcher.on('all', function(type, filepath, root) {
|
||||||
|
self.emit('all', type, filepath, root);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return watchers;
|
||||||
|
});
|
||||||
|
this._loading.done();
|
||||||
|
}
|
||||||
|
|
||||||
function FileWatcher(projectRoot) {
|
util.inherits(FileWatcher, EventEmitter);
|
||||||
if (memoizedInstances[projectRoot]) {
|
|
||||||
return memoizedInstances[projectRoot];
|
FileWatcher.prototype.end = function() {
|
||||||
} else {
|
return this._loading.then(function(watchers) {
|
||||||
memoizedInstances[projectRoot] = this;
|
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._loadingWatcher = detectingWatcherClass.then(function(Watcher) {
|
return detectingWatcherClass.then(function(Watcher) {
|
||||||
var watcher = new Watcher(projectRoot, {glob: '**/*.js'});
|
var watcher = new Watcher(root, {glob: '**/*.js'});
|
||||||
|
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
var rejectTimeout = setTimeout(function() {
|
var rejectTimeout = setTimeout(function() {
|
||||||
reject(new Error('Watcher took too long to load.'));
|
reject(new Error([
|
||||||
|
'Watcher took too long to load',
|
||||||
|
'Try running `watchman` from your terminal',
|
||||||
|
'https://facebook.github.io/watchman/docs/troubleshooting.html',
|
||||||
|
].join('\n')));
|
||||||
}, MAX_WAIT_TIME);
|
}, MAX_WAIT_TIME);
|
||||||
|
|
||||||
watcher.once('ready', function() {
|
watcher.once('ready', function() {
|
||||||
clearTimeout(rejectTimeout);
|
clearTimeout(rejectTimeout);
|
||||||
|
watchersByRoot[root] = watcher;
|
||||||
|
watcher._root = root;
|
||||||
resolve(watcher);
|
resolve(watcher);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
FileWatcher.prototype.getWatcher = function() {
|
|
||||||
return this._loadingWatcher;
|
|
||||||
};
|
|
||||||
|
|
||||||
FileWatcher.prototype.end = function() {
|
|
||||||
return this._loadingWatcher.then(function(watcher) {
|
|
||||||
return q.ninvoke(watcher, 'close');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
|
@ -13,12 +13,7 @@ var Promise = q.Promise;
|
||||||
module.exports = Cache;
|
module.exports = Cache;
|
||||||
|
|
||||||
function Cache(projectConfig) {
|
function Cache(projectConfig) {
|
||||||
this._cacheFilePath = path.join(
|
this._cacheFilePath = cacheFilePath(projectConfig);
|
||||||
tmpdir,
|
|
||||||
'React-Packager-JSTransformer-' + version + '-' +
|
|
||||||
projectConfig.projectRoot.split(path.sep).join('-') +
|
|
||||||
'-' + (projectConfig.cacheVersion || '0')
|
|
||||||
);
|
|
||||||
|
|
||||||
var data;
|
var data;
|
||||||
if (!projectConfig.resetCache) {
|
if (!projectConfig.resetCache) {
|
||||||
|
@ -118,3 +113,17 @@ function loadCacheSync(cacheFilepath) {
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cacheFilePath(projectConfig) {
|
||||||
|
var roots = projectConfig.projectRoots.join(',').split(path.sep).join('-');
|
||||||
|
var cacheVersion = projectConfig.cacheVersion || '0';
|
||||||
|
return path.join(
|
||||||
|
tmpdir,
|
||||||
|
[
|
||||||
|
'react-packager-cache',
|
||||||
|
version,
|
||||||
|
cacheVersion,
|
||||||
|
roots,
|
||||||
|
].join('-')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ describe('JSTransformer Cache', function() {
|
||||||
|
|
||||||
describe('getting/settig', function() {
|
describe('getting/settig', function() {
|
||||||
it('calls loader callback for uncached file', function() {
|
it('calls loader callback for uncached file', function() {
|
||||||
var cache = new Cache({projectRoot: '/rootDir'});
|
var cache = new Cache({projectRoots: ['/rootDir']});
|
||||||
var loaderCb = jest.genMockFn().mockImpl(function() {
|
var loaderCb = jest.genMockFn().mockImpl(function() {
|
||||||
return q();
|
return q();
|
||||||
});
|
});
|
||||||
|
@ -39,7 +39,7 @@ describe('JSTransformer Cache', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
var cache = new Cache({projectRoot: '/rootDir'});
|
var cache = new Cache({projectRoots: ['/rootDir']});
|
||||||
var loaderCb = jest.genMockFn().mockImpl(function() {
|
var loaderCb = jest.genMockFn().mockImpl(function() {
|
||||||
return q('lol');
|
return q('lol');
|
||||||
});
|
});
|
||||||
|
@ -56,7 +56,7 @@ describe('JSTransformer Cache', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
var cache = new Cache({projectRoot: '/rootDir'});
|
var cache = new Cache({projectRoots: ['/rootDir']});
|
||||||
var loaderCb = jest.genMockFn().mockImpl(function() {
|
var loaderCb = jest.genMockFn().mockImpl(function() {
|
||||||
return q('lol');
|
return q('lol');
|
||||||
});
|
});
|
||||||
|
@ -117,7 +117,7 @@ describe('JSTransformer Cache', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('should load cache from disk', function() {
|
pit('should load cache from disk', function() {
|
||||||
var cache = new Cache({projectRoot: '/rootDir'});
|
var cache = new Cache({projectRoots: ['/rootDir']});
|
||||||
var loaderCb = jest.genMockFn();
|
var loaderCb = jest.genMockFn();
|
||||||
return cache.get('/rootDir/someFile', loaderCb).then(function(value) {
|
return cache.get('/rootDir/someFile', loaderCb).then(function(value) {
|
||||||
expect(loaderCb).not.toBeCalled();
|
expect(loaderCb).not.toBeCalled();
|
||||||
|
@ -143,7 +143,7 @@ describe('JSTransformer Cache', function() {
|
||||||
return 123;
|
return 123;
|
||||||
};
|
};
|
||||||
|
|
||||||
var cache = new Cache({projectRoot: '/rootDir'});
|
var cache = new Cache({projectRoots: ['/rootDir']});
|
||||||
var loaderCb = jest.genMockFn().mockImpl(function() {
|
var loaderCb = jest.genMockFn().mockImpl(function() {
|
||||||
return q('new value');
|
return q('new value');
|
||||||
});
|
});
|
||||||
|
@ -184,7 +184,7 @@ describe('JSTransformer Cache', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var cache = new Cache({projectRoot: '/rootDir'});
|
var cache = new Cache({projectRoots: ['/rootDir']});
|
||||||
cache.get('/rootDir/bar', function() {
|
cache.get('/rootDir/bar', function() {
|
||||||
return q('bar value');
|
return q('bar value');
|
||||||
});
|
});
|
||||||
|
|
|
@ -35,7 +35,7 @@ describe('Packager', function() {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
var packager = new Packager({});
|
var packager = new Packager({projectRoots: []});
|
||||||
var modules = [
|
var modules = [
|
||||||
{id: 'foo', path: '/root/foo.js', dependencies: []},
|
{id: 'foo', path: '/root/foo.js', dependencies: []},
|
||||||
{id: 'bar', path: '/root/bar.js', dependencies: []},
|
{id: 'bar', path: '/root/bar.js', dependencies: []},
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
var q = require('q');
|
||||||
var Promise = require('q').Promise;
|
var Promise = require('q').Promise;
|
||||||
var Transformer = require('../JSTransformer');
|
var Transformer = require('../JSTransformer');
|
||||||
var DependencyResolver = require('../DependencyResolver');
|
var DependencyResolver = require('../DependencyResolver');
|
||||||
|
@ -45,10 +46,7 @@ var DEFAULT_CONFIG = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function Packager(projectConfig) {
|
function Packager(projectConfig) {
|
||||||
// Verify that the root exists.
|
projectConfig.projectRoots.forEach(verifyRootExists);
|
||||||
var root = projectConfig.projectRoot;
|
|
||||||
assert(fs.statSync(root).isDirectory(), 'Root has to be a valid directory');
|
|
||||||
this._rootPath = root;
|
|
||||||
|
|
||||||
this._config = Object.create(DEFAULT_CONFIG);
|
this._config = Object.create(DEFAULT_CONFIG);
|
||||||
for (var key in projectConfig) {
|
for (var key in projectConfig) {
|
||||||
|
@ -61,7 +59,10 @@ function Packager(projectConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Packager.prototype.kill = function() {
|
Packager.prototype.kill = function() {
|
||||||
return this._transformer.kill();
|
return q.all([
|
||||||
|
this._transformer.kill(),
|
||||||
|
this._resolver.end(),
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
Packager.prototype.package = function(main, runModule, sourceMapUrl) {
|
Packager.prototype.package = function(main, runModule, sourceMapUrl) {
|
||||||
|
@ -116,4 +117,9 @@ Packager.prototype._transformModule = function(module) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function verifyRootExists(root) {
|
||||||
|
// Verify that the root exists.
|
||||||
|
assert(fs.statSync(root).isDirectory(), 'Root has to be a valid directory');
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = Packager;
|
module.exports = Packager;
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
'use strict';
|
jest.setMock('worker-farm', function(){ return function(){}; })
|
||||||
|
|
||||||
jest.dontMock('worker-farm')
|
|
||||||
.dontMock('q')
|
.dontMock('q')
|
||||||
.dontMock('os')
|
.dontMock('os')
|
||||||
.dontMock('errno/custom')
|
.dontMock('errno/custom')
|
||||||
|
@ -19,7 +17,7 @@ describe('processRequest', function(){
|
||||||
var FileWatcher;
|
var FileWatcher;
|
||||||
|
|
||||||
var options = {
|
var options = {
|
||||||
projectRoot: 'root',
|
projectRoots: ['root'],
|
||||||
blacklistRE: null,
|
blacklistRE: null,
|
||||||
cacheVersion: null,
|
cacheVersion: null,
|
||||||
polyfillModuleNames: null
|
polyfillModuleNames: null
|
||||||
|
@ -59,11 +57,8 @@ describe('processRequest', function(){
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
FileWatcher.prototype.getWatcher = function() {
|
|
||||||
return q({
|
FileWatcher.prototype.on = watcherFunc;
|
||||||
on: watcherFunc
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Packager.prototype.invalidateFile = invalidatorFunc;
|
Packager.prototype.invalidateFile = invalidatorFunc;
|
||||||
|
|
||||||
|
@ -98,16 +93,12 @@ describe('processRequest', function(){
|
||||||
describe('file changes', function() {
|
describe('file changes', function() {
|
||||||
var triggerFileChange;
|
var triggerFileChange;
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
FileWatcher.prototype.getWatcher = function() {
|
FileWatcher.prototype.on = function(eventType, callback) {
|
||||||
return q({
|
if (eventType !== 'all') {
|
||||||
on: function(eventType, callback) {
|
throw new Error('Can only handle "all" event in watcher.');
|
||||||
if (eventType !== 'all') {
|
}
|
||||||
throw new Error('Can only handle "all" event in watcher.');
|
triggerFileChange = callback;
|
||||||
}
|
return this;
|
||||||
triggerFileChange = callback;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -115,7 +106,7 @@ describe('processRequest', function(){
|
||||||
result = makeRequest(requestHandler,'mybundle.includeRequire.runModule.bundle');
|
result = makeRequest(requestHandler,'mybundle.includeRequire.runModule.bundle');
|
||||||
return result.then(function(response){
|
return result.then(function(response){
|
||||||
var onFileChange = watcherFunc.mock.calls[0][1];
|
var onFileChange = watcherFunc.mock.calls[0][1];
|
||||||
onFileChange('all','path/file.js');
|
onFileChange('all','path/file.js', options.projectRoots[0]);
|
||||||
expect(invalidatorFunc.mock.calls[0][0]).toEqual('root/path/file.js');
|
expect(invalidatorFunc.mock.calls[0][0]).toEqual('root/path/file.js');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -152,7 +143,7 @@ describe('processRequest', function(){
|
||||||
.then(function(response){
|
.then(function(response){
|
||||||
expect(response).toEqual("this is the first source");
|
expect(response).toEqual("this is the first source");
|
||||||
expect(packageFunc.mock.calls.length).toBe(1);
|
expect(packageFunc.mock.calls.length).toBe(1);
|
||||||
triggerFileChange('all','path/file.js');
|
triggerFileChange('all','path/file.js', options.projectRoots[0]);
|
||||||
})
|
})
|
||||||
.then(function(){
|
.then(function(){
|
||||||
expect(packageFunc.mock.calls.length).toBe(2);
|
expect(packageFunc.mock.calls.length).toBe(2);
|
||||||
|
|
|
@ -8,10 +8,10 @@ var q = require('q');
|
||||||
module.exports = Server;
|
module.exports = Server;
|
||||||
|
|
||||||
function Server(options) {
|
function Server(options) {
|
||||||
this._projectRoot = options.projectRoot;
|
this._projectRoots = options.projectRoots;
|
||||||
this._packages = Object.create(null);
|
this._packages = Object.create(null);
|
||||||
this._packager = new Packager({
|
this._packager = new Packager({
|
||||||
projectRoot: options.projectRoot,
|
projectRoots: options.projectRoots,
|
||||||
blacklistRE: options.blacklistRE,
|
blacklistRE: options.blacklistRE,
|
||||||
polyfillModuleNames: options.polyfillModuleNames || [],
|
polyfillModuleNames: options.polyfillModuleNames || [],
|
||||||
runtimeCode: options.runtimeCode,
|
runtimeCode: options.runtimeCode,
|
||||||
|
@ -20,18 +20,18 @@ function Server(options) {
|
||||||
dev: options.dev,
|
dev: options.dev,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._fileWatcher = new FileWatcher(options.projectRoot);
|
this._fileWatcher = new FileWatcher(options.projectRoots);
|
||||||
|
|
||||||
var onFileChange = this._onFileChange.bind(this);
|
var onFileChange = this._onFileChange.bind(this);
|
||||||
this._fileWatcher.getWatcher().done(function(watcher) {
|
this._fileWatcher.on('all', onFileChange);
|
||||||
watcher.on('all', onFileChange);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Server.prototype._onFileChange = function(type, filepath) {
|
Server.prototype._onFileChange = function(type, filepath, root) {
|
||||||
var absPath = path.join(this._projectRoot, filepath);
|
var absPath = path.join(root, filepath);
|
||||||
this._packager.invalidateFile(absPath);
|
this._packager.invalidateFile(absPath);
|
||||||
this._rebuildPackages(absPath);
|
// Make sure the file watcher event runs through the system before
|
||||||
|
// we rebuild the packages.
|
||||||
|
setImmediate(this._rebuildPackages.bind(this, absPath))
|
||||||
};
|
};
|
||||||
|
|
||||||
Server.prototype._rebuildPackages = function(filepath) {
|
Server.prototype._rebuildPackages = function(filepath) {
|
||||||
|
|
Loading…
Reference in New Issue