mirror of https://github.com/status-im/metro.git
2015-02-05 updates
- [ReactServer] Fix newly introduced bug | Amjad Masad - [ReactServer] Last sync from github | Amjad Masad - [RFC-ReactNative] Subscribable overhaul, clean up AppState/Reachability | Eric Vicenti - [ReactKit] Enable tappable <Text /> subnodes | Alex Akers
This commit is contained in:
parent
ddaccf41d8
commit
4fb7489ee9
|
@ -1,100 +1,19 @@
|
||||||
var Packager = require('./src/Packager');
|
'use strict';
|
||||||
|
|
||||||
var Activity = require('./src/Activity');
|
var Activity = require('./src/Activity');
|
||||||
var url = require('url');
|
var Server = require('./src/Server');
|
||||||
|
|
||||||
|
exports.middleware = function(options) {
|
||||||
|
var server = new Server(options);
|
||||||
|
return server.processRequest.bind(server);
|
||||||
|
};
|
||||||
|
|
||||||
exports.buildPackageFromUrl = function(options, reqUrl) {
|
exports.buildPackageFromUrl = function(options, reqUrl) {
|
||||||
Activity.disable();
|
Activity.disable();
|
||||||
var packager = createPackager(options);
|
var server = new Server(options);
|
||||||
var params = getOptionsFromPath(url.parse(reqUrl).pathname);
|
return server.buildPackageFromUrl(reqUrl)
|
||||||
return packager.package(
|
.then(function(p) {
|
||||||
params.main,
|
server.kill();
|
||||||
params.runModule,
|
|
||||||
params.sourceMapUrl
|
|
||||||
).then(function(p) {
|
|
||||||
packager.kill();
|
|
||||||
return p;
|
return p;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.catalystMiddleware = function(options) {
|
|
||||||
var packager = createPackager(options);
|
|
||||||
|
|
||||||
return function(req, res, next) {
|
|
||||||
var options;
|
|
||||||
if (req.url.match(/\.bundle$/)) {
|
|
||||||
options = getOptionsFromPath(url.parse(req.url).pathname);
|
|
||||||
packager.package(
|
|
||||||
options.main,
|
|
||||||
options.runModule,
|
|
||||||
options.sourceMapUrl
|
|
||||||
).then(
|
|
||||||
function(package) {
|
|
||||||
res.end(package.getSource());
|
|
||||||
},
|
|
||||||
function(error) {
|
|
||||||
handleError(res, error);
|
|
||||||
}
|
|
||||||
).done();
|
|
||||||
} else if (req.url.match(/\.map$/)) {
|
|
||||||
options = getOptionsFromPath(url.parse(req.url).pathname);
|
|
||||||
packager.package(
|
|
||||||
options.main,
|
|
||||||
options.runModule,
|
|
||||||
options.sourceMapUrl
|
|
||||||
).then(
|
|
||||||
function(package) {
|
|
||||||
res.end(JSON.stringify(package.getSourceMap()));
|
|
||||||
},
|
|
||||||
function(error) {
|
|
||||||
handleError(res, error);
|
|
||||||
}
|
|
||||||
).done();
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function getOptionsFromPath(pathname) {
|
|
||||||
var parts = pathname.split('.');
|
|
||||||
// Remove the leading slash.
|
|
||||||
var main = parts[0].slice(1) + '.js';
|
|
||||||
return {
|
|
||||||
runModule: parts.slice(1).some(function(part) {
|
|
||||||
return part === 'runModule';
|
|
||||||
}),
|
|
||||||
main: main,
|
|
||||||
sourceMapUrl: parts.slice(0, -1).join('.') + '.map'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleError(res, error) {
|
|
||||||
res.writeHead(500, {
|
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (error.type === 'TransformError') {
|
|
||||||
res.end(JSON.stringify(error));
|
|
||||||
} else {
|
|
||||||
console.error(error.stack || error);
|
|
||||||
res.end(JSON.stringify({
|
|
||||||
type: 'InternalError',
|
|
||||||
message: 'React packager has encountered an internal error, ' +
|
|
||||||
'please check your terminal error output for more details',
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createPackager(options) {
|
|
||||||
return new Packager({
|
|
||||||
projectRoot: options.projectRoot,
|
|
||||||
blacklistRE: options.blacklistRE,
|
|
||||||
polyfillModuleNames: options.polyfillModuleNames || [],
|
|
||||||
runtimeCode: options.runtimeCode,
|
|
||||||
cacheVersion: options.cacheVersion,
|
|
||||||
resetCache: options.resetCache,
|
|
||||||
dev: options.dev,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.kill = Packager.kill;
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "react-packager",
|
"name": "react-packager",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js"
|
"main": "index.js",
|
||||||
|
"jest": {
|
||||||
|
"unmockedModulePathPatterns": ["source-map"],
|
||||||
|
"testPathIgnorePatterns": ["JSAppServer/node_modules"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,11 +131,12 @@ function _writeAction(action) {
|
||||||
|
|
||||||
case 'endEvent':
|
case 'endEvent':
|
||||||
var startAction = _eventStarts[action.eventId];
|
var startAction = _eventStarts[action.eventId];
|
||||||
|
var startData = startAction.data ? ': ' + JSON.stringify(startAction.data) : '';
|
||||||
console.log(
|
console.log(
|
||||||
'[' + fmtTime + '] ' +
|
'[' + fmtTime + '] ' +
|
||||||
'<END> ' + startAction.eventName +
|
'<END> ' + startAction.eventName +
|
||||||
'(' + (action.tstamp - startAction.tstamp) + 'ms)' +
|
'(' + (action.tstamp - startAction.tstamp) + 'ms)' +
|
||||||
data
|
startData
|
||||||
);
|
);
|
||||||
delete _eventStarts[action.eventId];
|
delete _eventStarts[action.eventId];
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -29,7 +29,7 @@ function HasteDependencyResolver(config) {
|
||||||
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)
|
fileWatcher: new FileWatcher(config.projectRoot)
|
||||||
});
|
});
|
||||||
|
|
||||||
this._polyfillModuleNames = [
|
this._polyfillModuleNames = [
|
||||||
|
|
|
@ -17,14 +17,14 @@ describe('FileWatcher', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('it should get the watcher instance when ready', function() {
|
pit('it should get the watcher instance when ready', function() {
|
||||||
var fileWatcher = new FileWatcher({projectRoot: 'rootDir'});
|
var fileWatcher = new FileWatcher('rootDir');
|
||||||
return fileWatcher.getWatcher().then(function(watcher) {
|
return fileWatcher.getWatcher().then(function(watcher) {
|
||||||
expect(watcher instanceof Watcher).toBe(true);
|
expect(watcher instanceof Watcher).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('it should end the watcher', function() {
|
pit('it should end the watcher', function() {
|
||||||
var fileWatcher = new FileWatcher({projectRoot: 'rootDir'});
|
var fileWatcher = new FileWatcher('rootDir');
|
||||||
Watcher.prototype.close.mockImplementation(function(callback) {
|
Watcher.prototype.close.mockImplementation(function(callback) {
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,15 +22,15 @@ var MAX_WAIT_TIME = 3000;
|
||||||
|
|
||||||
var memoizedInstances = Object.create(null);
|
var memoizedInstances = Object.create(null);
|
||||||
|
|
||||||
function FileWatcher(projectConfig) {
|
function FileWatcher(projectRoot) {
|
||||||
if (memoizedInstances[projectConfig.projectRoot]) {
|
if (memoizedInstances[projectRoot]) {
|
||||||
return memoizedInstances[projectConfig.projectRoot];
|
return memoizedInstances[projectRoot];
|
||||||
} else {
|
} else {
|
||||||
memoizedInstances[projectConfig.projectRoot] = this;
|
memoizedInstances[projectRoot] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._loadingWatcher = detectingWatcherClass.then(function(Watcher) {
|
this._loadingWatcher = detectingWatcherClass.then(function(Watcher) {
|
||||||
var watcher = new Watcher(projectConfig.projectRoot, {glob: '**/*.js'});
|
var watcher = new Watcher(projectRoot, {glob: '**/*.js'});
|
||||||
|
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
var rejectTimeout = setTimeout(function() {
|
var rejectTimeout = setTimeout(function() {
|
||||||
|
|
|
@ -4,7 +4,6 @@ var path = require('path');
|
||||||
var version = require('../../package.json').version;
|
var version = require('../../package.json').version;
|
||||||
var tmpdir = require('os').tmpDir();
|
var tmpdir = require('os').tmpDir();
|
||||||
var pathUtils = require('../fb-path-utils');
|
var pathUtils = require('../fb-path-utils');
|
||||||
var FileWatcher = require('../FileWatcher');
|
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
var q = require('q');
|
var q = require('q');
|
||||||
|
@ -34,16 +33,6 @@ function Cache(projectConfig) {
|
||||||
this._persistCache.bind(this),
|
this._persistCache.bind(this),
|
||||||
2000
|
2000
|
||||||
);
|
);
|
||||||
|
|
||||||
this._fileWatcher = new FileWatcher(projectConfig);
|
|
||||||
this._fileWatcher
|
|
||||||
.getWatcher()
|
|
||||||
.done(function(watcher) {
|
|
||||||
watcher.on('all', function(type, filepath) {
|
|
||||||
var absPath = path.join(projectConfig.projectRoot, filepath);
|
|
||||||
delete data[absPath];
|
|
||||||
});
|
|
||||||
}.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cache.prototype.get = function(filepath, loaderCb) {
|
Cache.prototype.get = function(filepath, loaderCb) {
|
||||||
|
@ -75,11 +64,14 @@ Cache.prototype._set = function(filepath, loaderPromise) {
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Cache.prototype.invalidate = function(filepath){
|
||||||
|
if(this._has(filepath)) {
|
||||||
|
delete this._data[filepath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Cache.prototype.end = function() {
|
Cache.prototype.end = function() {
|
||||||
return q.all([
|
return this._persistCache();
|
||||||
this._persistCache(),
|
|
||||||
this._fileWatcher.end()
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Cache.prototype._persistCache = function() {
|
Cache.prototype._persistCache = function() {
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function (data, callback) {
|
||||||
|
callback(null, {});
|
||||||
|
};
|
|
@ -19,13 +19,6 @@ describe('JSTransformer Cache', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
Cache = require('../Cache');
|
Cache = require('../Cache');
|
||||||
|
|
||||||
var FileWatcher = require('../../FileWatcher');
|
|
||||||
FileWatcher.prototype.getWatcher = function() {
|
|
||||||
return q({
|
|
||||||
on: function() {}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getting/settig', function() {
|
describe('getting/settig', function() {
|
||||||
|
@ -76,41 +69,6 @@ describe('JSTransformer Cache', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('it invalidates cache after a file has changed', function() {
|
|
||||||
require('fs').stat.mockImpl(function(file, callback) {
|
|
||||||
callback(null, {
|
|
||||||
mtime: {
|
|
||||||
getTime: function() {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
var FileWatcher = require('../../FileWatcher');
|
|
||||||
var triggerChangeFile;
|
|
||||||
FileWatcher.prototype.getWatcher = function() {
|
|
||||||
return q({
|
|
||||||
on: function(type, callback) {
|
|
||||||
triggerChangeFile = callback;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var cache = new Cache({projectRoot: '/rootDir'});
|
|
||||||
var loaderCb = jest.genMockFn().mockImpl(function() {
|
|
||||||
return q('lol');
|
|
||||||
});
|
|
||||||
|
|
||||||
return cache.get('/rootDir/someFile', loaderCb).then(function(value) {
|
|
||||||
expect(value).toBe('lol');
|
|
||||||
triggerChangeFile('change', 'someFile');
|
|
||||||
var loaderCb2 = jest.genMockFn().mockImpl(function() {
|
|
||||||
return q('lol2');
|
|
||||||
});
|
|
||||||
return cache.get('/rootDir/someFile', loaderCb2).then(function(value2) {
|
|
||||||
expect(value2).toBe('lol2');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('loading cache from disk', function() {
|
describe('loading cache from disk', function() {
|
||||||
|
|
|
@ -25,6 +25,12 @@ Transformer.prototype.kill = function() {
|
||||||
return this._cache.end();
|
return this._cache.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Transformer.prototype.invalidateFile = function(filePath) {
|
||||||
|
this._cache.invalidate(filePath);
|
||||||
|
//TODO: We can read the file and put it into the cache right here
|
||||||
|
// This would simplify some caching logic as we can be sure that the cache is up to date
|
||||||
|
}
|
||||||
|
|
||||||
Transformer.prototype.loadFileAndTransform = function(
|
Transformer.prototype.loadFileAndTransform = function(
|
||||||
transformSets,
|
transformSets,
|
||||||
filePath,
|
filePath,
|
||||||
|
|
|
@ -42,9 +42,11 @@ Package.prototype.finalize = function(options) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Package.prototype.getSource = function() {
|
Package.prototype.getSource = function() {
|
||||||
return _.pluck(this._modules, 'transformedCode').join('\n') + '\n' +
|
return this._source || (
|
||||||
'RAW_SOURCE_MAP = ' + JSON.stringify(this.getSourceMap({excludeSource: true})) + ';\n' +
|
this._source = _.pluck(this._modules, 'transformedCode').join('\n') + '\n' +
|
||||||
'\/\/@ sourceMappingURL=' + this._sourceMapUrl;
|
'RAW_SOURCE_MAP = ' + JSON.stringify(this.getSourceMap({excludeSource: true})) +
|
||||||
|
';\n' + '\/\/@ sourceMappingURL=' + this._sourceMapUrl
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Package.prototype.getSourceMap = function(options) {
|
Package.prototype.getSourceMap = function(options) {
|
||||||
|
|
|
@ -97,6 +97,10 @@ Packager.prototype.package = function(main, runModule, sourceMapUrl) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Packager.prototype.invalidateFile = function(filePath){
|
||||||
|
this._transformer.invalidateFile(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
Packager.prototype._transformModule = function(module) {
|
Packager.prototype._transformModule = function(module) {
|
||||||
var resolver = this._resolver;
|
var resolver = this._resolver;
|
||||||
return this._transformer.loadFileAndTransform(
|
return this._transformer.loadFileAndTransform(
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
jest.dontMock('worker-farm')
|
||||||
|
.dontMock('q')
|
||||||
|
.dontMock('os')
|
||||||
|
.dontMock('errno/custom')
|
||||||
|
.dontMock('path')
|
||||||
|
.dontMock('url')
|
||||||
|
.dontMock('../');
|
||||||
|
|
||||||
|
|
||||||
|
var server = require('../');
|
||||||
|
var q = require('q');
|
||||||
|
|
||||||
|
describe('processRequest', function(){
|
||||||
|
var server;
|
||||||
|
var Activity;
|
||||||
|
var Packager;
|
||||||
|
var FileWatcher;
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
projectRoot: 'root',
|
||||||
|
blacklistRE: null,
|
||||||
|
cacheVersion: null,
|
||||||
|
polyfillModuleNames: null
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeRequest = function(requestHandler, requrl){
|
||||||
|
var deferred = q.defer();
|
||||||
|
requestHandler({
|
||||||
|
url: requrl
|
||||||
|
},{
|
||||||
|
end: function(res){
|
||||||
|
deferred.resolve(res);
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
next: function(){}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return deferred.promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
var invalidatorFunc = jest.genMockFunction();
|
||||||
|
var watcherFunc = jest.genMockFunction();
|
||||||
|
var requestHandler;
|
||||||
|
|
||||||
|
beforeEach(function(){
|
||||||
|
Activity = require('../../Activity');
|
||||||
|
Packager = require('../../Packager');
|
||||||
|
FileWatcher = require('../../FileWatcher')
|
||||||
|
|
||||||
|
Packager.prototype.package = function(main, runModule, sourceMapUrl) {
|
||||||
|
return q({
|
||||||
|
getSource: function(){
|
||||||
|
return "this is the source"
|
||||||
|
},
|
||||||
|
getSourceMap: function(){
|
||||||
|
return "this is the source map"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
FileWatcher.prototype.getWatcher = function() {
|
||||||
|
return q({
|
||||||
|
on: watcherFunc
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Packager.prototype.invalidateFile = invalidatorFunc;
|
||||||
|
|
||||||
|
var Server = require('../');
|
||||||
|
server = new Server(options);
|
||||||
|
requestHandler = server.processRequest.bind(server);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('returns JS bundle source on request of *.bundle',function(){
|
||||||
|
result = makeRequest(requestHandler,'mybundle.includeRequire.runModule.bundle');
|
||||||
|
return result.then(function(response){
|
||||||
|
expect(response).toEqual("this is the source");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('returns sourcemap on request of *.map', function(){
|
||||||
|
result = makeRequest(requestHandler,'mybundle.includeRequire.runModule.bundle.map');
|
||||||
|
return result.then(function(response){
|
||||||
|
expect(response).toEqual('"this is the source map"');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('watches all files in projectRoot', function(){
|
||||||
|
result = makeRequest(requestHandler,'mybundle.includeRequire.runModule.bundle');
|
||||||
|
return result.then(function(response){
|
||||||
|
expect(watcherFunc.mock.calls[0][0]).toEqual('all');
|
||||||
|
expect(watcherFunc.mock.calls[0][1]).not.toBe(null);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('file changes', function() {
|
||||||
|
var triggerFileChange;
|
||||||
|
beforeEach(function() {
|
||||||
|
FileWatcher.prototype.getWatcher = function() {
|
||||||
|
return q({
|
||||||
|
on: function(eventType, callback) {
|
||||||
|
if (eventType !== 'all') {
|
||||||
|
throw new Error('Can only handle "all" event in watcher.');
|
||||||
|
}
|
||||||
|
triggerFileChange = callback;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('invalides files in package when file is updated', function() {
|
||||||
|
result = makeRequest(requestHandler,'mybundle.includeRequire.runModule.bundle');
|
||||||
|
return result.then(function(response){
|
||||||
|
var onFileChange = watcherFunc.mock.calls[0][1];
|
||||||
|
onFileChange('all','path/file.js');
|
||||||
|
expect(invalidatorFunc.mock.calls[0][0]).toEqual('root/path/file.js');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('rebuilds the packages that contain a file when that file is changed', function() {
|
||||||
|
var packageFunc = jest.genMockFunction();
|
||||||
|
packageFunc
|
||||||
|
.mockReturnValueOnce(
|
||||||
|
q({
|
||||||
|
getSource: function(){
|
||||||
|
return "this is the first source"
|
||||||
|
},
|
||||||
|
getSourceMap: function(){},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.mockReturnValue(
|
||||||
|
q({
|
||||||
|
getSource: function(){
|
||||||
|
return "this is the rebuilt source"
|
||||||
|
},
|
||||||
|
getSourceMap: function(){},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
Packager.prototype.package = packageFunc;
|
||||||
|
|
||||||
|
var Server = require('../../Server');
|
||||||
|
var server = new Server(options);
|
||||||
|
|
||||||
|
requestHandler = server.processRequest.bind(server);
|
||||||
|
|
||||||
|
|
||||||
|
return makeRequest(requestHandler,'mybundle.includeRequire.runModule.bundle')
|
||||||
|
.then(function(response){
|
||||||
|
expect(response).toEqual("this is the first source");
|
||||||
|
expect(packageFunc.mock.calls.length).toBe(1);
|
||||||
|
triggerFileChange('all','path/file.js');
|
||||||
|
})
|
||||||
|
.then(function(){
|
||||||
|
expect(packageFunc.mock.calls.length).toBe(2);
|
||||||
|
return makeRequest(requestHandler,'mybundle.includeRequire.runModule.bundle')
|
||||||
|
.then(function(response){
|
||||||
|
expect(response).toEqual("this is the rebuilt source");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,128 @@
|
||||||
|
var url = require('url');
|
||||||
|
var path = require('path');
|
||||||
|
var FileWatcher = require('../FileWatcher')
|
||||||
|
var Packager = require('../Packager');
|
||||||
|
var Activity = require('../Activity');
|
||||||
|
var q = require('q');
|
||||||
|
|
||||||
|
module.exports = Server;
|
||||||
|
|
||||||
|
function Server(options) {
|
||||||
|
this._projectRoot = options.projectRoot;
|
||||||
|
this._packages = Object.create(null);
|
||||||
|
this._packager = new Packager({
|
||||||
|
projectRoot: options.projectRoot,
|
||||||
|
blacklistRE: options.blacklistRE,
|
||||||
|
polyfillModuleNames: options.polyfillModuleNames || [],
|
||||||
|
runtimeCode: options.runtimeCode,
|
||||||
|
cacheVersion: options.cacheVersion,
|
||||||
|
resetCache: options.resetCache,
|
||||||
|
dev: options.dev,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._fileWatcher = new FileWatcher(options.projectRoot);
|
||||||
|
|
||||||
|
var onFileChange = this._onFileChange.bind(this);
|
||||||
|
this._fileWatcher.getWatcher().done(function(watcher) {
|
||||||
|
watcher.on('all', onFileChange);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Server.prototype._onFileChange = function(type, filepath) {
|
||||||
|
var absPath = path.join(this._projectRoot, filepath);
|
||||||
|
this._packager.invalidateFile(absPath);
|
||||||
|
this._rebuildPackages(absPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
Server.prototype._rebuildPackages = function(filepath) {
|
||||||
|
var buildPackage = this._buildPackage.bind(this);
|
||||||
|
var packages = this._packages;
|
||||||
|
Object.keys(packages).forEach(function(key) {
|
||||||
|
var options = getOptionsFromPath(url.parse(key).pathname);
|
||||||
|
packages[key] = buildPackage(options).then(function(p) {
|
||||||
|
// Make a throwaway call to getSource to cache the source string.
|
||||||
|
p.getSource();
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Server.prototype.kill = function() {
|
||||||
|
q.all([
|
||||||
|
this._fileWatcher.end(),
|
||||||
|
this._packager.kill(),
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
Server.prototype._buildPackage = function(options) {
|
||||||
|
return this._packager.package(
|
||||||
|
options.main,
|
||||||
|
options.runModule,
|
||||||
|
options.sourceMapUrl
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Server.prototype.buildPackageFromUrl = function(reqUrl) {
|
||||||
|
var options = getOptionsFromPath(url.parse(reqUrl).pathname);
|
||||||
|
return this._buildPackage(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
Server.prototype.processRequest = function(req, res, next) {
|
||||||
|
var requestType;
|
||||||
|
if (req.url.match(/\.bundle$/)) {
|
||||||
|
requestType = 'bundle';
|
||||||
|
} else if (req.url.match(/\.map$/)) {
|
||||||
|
requestType = 'map';
|
||||||
|
} else {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
var startReqEventId = Activity.startEvent('request:' + req.url);
|
||||||
|
var options = getOptionsFromPath(url.parse(req.url).pathname);
|
||||||
|
var building = this._packages[req.url] || this._buildPackage(options)
|
||||||
|
this._packages[req.url] = building;
|
||||||
|
building.then(
|
||||||
|
function(p) {
|
||||||
|
if (requestType === 'bundle') {
|
||||||
|
res.end(p.getSource());
|
||||||
|
Activity.endEvent(startReqEventId);
|
||||||
|
} else if (requestType === 'map') {
|
||||||
|
res.end(JSON.stringify(p.getSourceMap()));
|
||||||
|
Activity.endEvent(startReqEventId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
handleError(res, error);
|
||||||
|
}
|
||||||
|
).done();
|
||||||
|
};
|
||||||
|
|
||||||
|
function getOptionsFromPath(pathname) {
|
||||||
|
var parts = pathname.split('.');
|
||||||
|
// Remove the leading slash.
|
||||||
|
var main = parts[0].slice(1) + '.js';
|
||||||
|
return {
|
||||||
|
runModule: parts.slice(1).some(function(part) {
|
||||||
|
return part === 'runModule';
|
||||||
|
}),
|
||||||
|
main: main,
|
||||||
|
sourceMapUrl: parts.slice(0, -1).join('.') + '.map'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleError(res, error) {
|
||||||
|
res.writeHead(500, {
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error.type === 'TransformError') {
|
||||||
|
res.end(JSON.stringify(error));
|
||||||
|
} else {
|
||||||
|
console.error(error.stack || error);
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
type: 'InternalError',
|
||||||
|
message: 'react-packager has encountered an internal error, ' +
|
||||||
|
'please check your terminal error output for more details',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue