mirror of https://github.com/status-im/metro.git
Updates from Tue 25 Aug
This commit is contained in:
commit
39681a6649
|
@ -23,7 +23,7 @@ window.localStorage.removeItem('sessionID');
|
||||||
window.onbeforeunload = function() {
|
window.onbeforeunload = function() {
|
||||||
if (sessionID) {
|
if (sessionID) {
|
||||||
return 'If you reload this page, it is going to break the debugging session. ' +
|
return 'If you reload this page, it is going to break the debugging session. ' +
|
||||||
'You should ⌘+R in the iOS simulator to reload.';
|
'You should press ⌘R in simulator to reload.';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -76,10 +76,10 @@ function connectToDebuggerProxy() {
|
||||||
|
|
||||||
ws.onopen = function() {
|
ws.onopen = function() {
|
||||||
if (sessionID) {
|
if (sessionID) {
|
||||||
setStatus('Debugger session #' + sessionID + ' active');
|
setStatus('Debugger session #' + sessionID + ' active.');
|
||||||
ws.send(JSON.stringify({replyID: parseInt(sessionID, 10)}));
|
ws.send(JSON.stringify({replyID: parseInt(sessionID, 10)}));
|
||||||
} else {
|
} else {
|
||||||
setStatus('Waiting, press ⌘R in simulator to reload and connect');
|
setStatus('Waiting, press <span class="shortcut">⌘R</span> in simulator to reload and connect.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -126,7 +126,8 @@ function loadScript(src, callback) {
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
}
|
}
|
||||||
.shortcut {
|
.shortcut {
|
||||||
font-family: monospace;
|
font-family: "Monaco", monospace;
|
||||||
|
font-size: medium;
|
||||||
color: #eee;
|
color: #eee;
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
@ -175,10 +176,10 @@ function loadScript(src, callback) {
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p>
|
<p>
|
||||||
React Native JS code runs inside this Chrome tab
|
React Native JS code runs inside this Chrome tab.
|
||||||
</p>
|
</p>
|
||||||
<p>Press <span class="shortcut">⌘⌥J</span> to open Developer Tools. Enable <a href="http://stackoverflow.com/a/17324511/232122" target="_blank">Pause On Caught Exceptions</a> for a better debugging experience.</p>
|
<p>Press <span class="shortcut">⌘⌥J</span> to open Developer Tools. Enable <a href="http://stackoverflow.com/a/17324511/232122" target="_blank">Pause On Caught Exceptions</a> for a better debugging experience.</p>
|
||||||
<p>Status: <span id="status">Loading</span></p>
|
<p>Status: <span id="status">Loading...</span></p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -10,17 +10,23 @@
|
||||||
|
|
||||||
var chalk = require('chalk');
|
var chalk = require('chalk');
|
||||||
var exec = require('child_process').exec;
|
var exec = require('child_process').exec;
|
||||||
var Activity = require('./react-packager/src/Activity');
|
var url = require('url');
|
||||||
|
var Activity = require('./react-packager').Activity;
|
||||||
|
|
||||||
var hasWarned = {};
|
var hasWarned = {};
|
||||||
var DISABLE_FLOW_CHECK = true; // temporarily disable while we figure out versioning issues.
|
|
||||||
|
|
||||||
function getFlowTypeCheckMiddleware(options) {
|
function getFlowTypeCheckMiddleware(options) {
|
||||||
return function(req, res, next) {
|
return function(req, res, next) {
|
||||||
var isBundle = req.url.indexOf('.bundle') !== -1;
|
var reqObj = url.parse(req.url);
|
||||||
if (DISABLE_FLOW_CHECK || options.skipflow || !isBundle) {
|
var isFlowCheck = (reqObj.path.match(/^\/flow\//));
|
||||||
|
|
||||||
|
if (!isFlowCheck) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
if (options.skipflow) {
|
||||||
|
_endSkipFlow(res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (options.flowroot || options.projectRoots.length === 1) {
|
if (options.flowroot || options.projectRoots.length === 1) {
|
||||||
var flowroot = options.flowroot || options.projectRoots[0];
|
var flowroot = options.flowroot || options.projectRoots[0];
|
||||||
} else {
|
} else {
|
||||||
|
@ -28,7 +34,8 @@ function getFlowTypeCheckMiddleware(options) {
|
||||||
hasWarned.noRoot = true;
|
hasWarned.noRoot = true;
|
||||||
console.warn('flow: No suitable root');
|
console.warn('flow: No suitable root');
|
||||||
}
|
}
|
||||||
return next();
|
_endFlowBad(res);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
exec('command -v flow >/dev/null 2>&1', function(error, stdout) {
|
exec('command -v flow >/dev/null 2>&1', function(error, stdout) {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -37,7 +44,8 @@ function getFlowTypeCheckMiddleware(options) {
|
||||||
console.warn(chalk.yellow('flow: Skipping because not installed. Install with ' +
|
console.warn(chalk.yellow('flow: Skipping because not installed. Install with ' +
|
||||||
'`brew install flow`.'));
|
'`brew install flow`.'));
|
||||||
}
|
}
|
||||||
return next();
|
_endFlowBad(res);
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
return doFlowTypecheck(res, flowroot, next);
|
return doFlowTypecheck(res, flowroot, next);
|
||||||
}
|
}
|
||||||
|
@ -51,7 +59,8 @@ function doFlowTypecheck(res, flowroot, next) {
|
||||||
exec(flowCmd, function(flowError, stdout, stderr) {
|
exec(flowCmd, function(flowError, stdout, stderr) {
|
||||||
Activity.endEvent(eventId);
|
Activity.endEvent(eventId);
|
||||||
if (!flowError) {
|
if (!flowError) {
|
||||||
return next();
|
_endFlowOk(res);
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
var flowResponse = JSON.parse(stdout);
|
var flowResponse = JSON.parse(stdout);
|
||||||
|
@ -73,16 +82,13 @@ function doFlowTypecheck(res, flowroot, next) {
|
||||||
errorNum++;
|
errorNum++;
|
||||||
});
|
});
|
||||||
var error = {
|
var error = {
|
||||||
status: 500,
|
status: 200,
|
||||||
message: 'Flow found type errors. If you think these are wrong, ' +
|
message: 'Flow found type errors. If you think these are wrong, ' +
|
||||||
'make sure your flow bin and .flowconfig are up to date, or ' +
|
'make sure your flow bin and .flowconfig are up to date, or ' +
|
||||||
'disable with --skipflow.',
|
'disable with --skipflow.',
|
||||||
type: 'FlowError',
|
type: 'FlowError',
|
||||||
errors: errors,
|
errors: errors,
|
||||||
};
|
};
|
||||||
console.error(chalk.yellow('flow: Error running command `' + flowCmd +
|
|
||||||
'`:\n' + JSON.stringify(error))
|
|
||||||
);
|
|
||||||
res.writeHead(error.status, {
|
res.writeHead(error.status, {
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
});
|
});
|
||||||
|
@ -93,6 +99,13 @@ function doFlowTypecheck(res, flowroot, next) {
|
||||||
hasWarned.noConfig = true;
|
hasWarned.noConfig = true;
|
||||||
console.warn(chalk.yellow('flow: ' + stderr));
|
console.warn(chalk.yellow('flow: ' + stderr));
|
||||||
}
|
}
|
||||||
|
_endFlowBad(res);
|
||||||
|
} else if (flowError.code === 3) {
|
||||||
|
if (!hasWarned.timeout) {
|
||||||
|
hasWarned.timeout = true;
|
||||||
|
console.warn(chalk.yellow('flow: ' + stdout));
|
||||||
|
}
|
||||||
|
_endSkipFlow(res);
|
||||||
} else {
|
} else {
|
||||||
if (!hasWarned.brokenFlow) {
|
if (!hasWarned.brokenFlow) {
|
||||||
hasWarned.brokenFlow = true;
|
hasWarned.brokenFlow = true;
|
||||||
|
@ -101,11 +114,37 @@ function doFlowTypecheck(res, flowroot, next) {
|
||||||
'`.\n' + 'stderr: `' + stderr + '`'
|
'`.\n' + 'stderr: `' + stderr + '`'
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
_endFlowBad(res);
|
||||||
}
|
}
|
||||||
return next();
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _endRes(res, message, code, silentError) {
|
||||||
|
res.writeHead(code, {
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
});
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
message: message,
|
||||||
|
errors: [],
|
||||||
|
silentError: silentError,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function _endFlowOk(res) {
|
||||||
|
_endRes(res, 'No Flow Error', '200', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _endFlowBad(res) {
|
||||||
|
// we want to show that flow failed
|
||||||
|
// status 200 is need for the fetch to not be rejected
|
||||||
|
_endRes(res, 'Flow failed to run! Please look at the console for more details.', '200', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _endSkipFlow(res) {
|
||||||
|
_endRes(res, 'Flow was skipped, check the server options', '200', true);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = getFlowTypeCheckMiddleware;
|
module.exports = getFlowTypeCheckMiddleware;
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var EventEmitter = require('events').EventEmitter;
|
|
||||||
var servers = {};
|
|
||||||
exports.createServer = function(listener) {
|
|
||||||
var server = {
|
|
||||||
_listener: listener,
|
|
||||||
|
|
||||||
socket: new EventEmitter(),
|
|
||||||
|
|
||||||
listen: function(path) {
|
|
||||||
listener(this.socket);
|
|
||||||
servers[path] = this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
server.socket.setEncoding = function() {};
|
|
||||||
server.socket.write = function(data) {
|
|
||||||
this.emit('data', data);
|
|
||||||
};
|
|
||||||
|
|
||||||
return server;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.connect = function(options) {
|
|
||||||
var server = servers[options.path || options.port];
|
|
||||||
return server.socket;
|
|
||||||
};
|
|
|
@ -1,12 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @providesModule bar
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = setInterval;
|
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"port": 3000,
|
|
||||||
"devPort": 3001,
|
|
||||||
"publicDir": "./public",
|
|
||||||
"rootPath": "../example_project",
|
|
||||||
"moduleOptions": {
|
|
||||||
"format": "haste",
|
|
||||||
"main": "index.js"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @providesModule foo
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
var bar = require('bar');
|
|
||||||
|
|
||||||
class Logger {
|
|
||||||
log() {
|
|
||||||
console.log('youll have to change me lol');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SecretLogger extends Logger {
|
|
||||||
log(secret) {
|
|
||||||
console.log('logging ', secret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = (secret) => {
|
|
||||||
if (secret !== 'secret') throw new Error('wrong secret');
|
|
||||||
bar(new SecretLogger().log.bind(SecretLogger, secret), 400);
|
|
||||||
};
|
|
|
@ -1,16 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @providesModule index
|
|
||||||
*/
|
|
||||||
|
|
||||||
require('main');
|
|
||||||
require('code');
|
|
||||||
|
|
||||||
var foo = require('foo');
|
|
||||||
foo('secret');
|
|
|
@ -1,53 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @providesModule Channel
|
|
||||||
*/
|
|
||||||
|
|
||||||
var XHR = require('XHR');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Client implementation of a server-push channel.
|
|
||||||
*
|
|
||||||
* @see Channel.js for full documentation
|
|
||||||
*/
|
|
||||||
var channel = null, at = null, delay = 0;
|
|
||||||
var Channel = {};
|
|
||||||
|
|
||||||
Channel.connect = function() {
|
|
||||||
var url = '/pull';
|
|
||||||
if (channel) {
|
|
||||||
url += '?channel=' + channel + '&at=' + at;
|
|
||||||
}
|
|
||||||
XHR.get(url, function(err, xhr) {
|
|
||||||
if (err) {
|
|
||||||
delay = Math.min(Math.max(1000, delay * 2), 30000);
|
|
||||||
} else {
|
|
||||||
var res = xhr.responseText;
|
|
||||||
res = JSON.parse(res);
|
|
||||||
|
|
||||||
delay = 0;
|
|
||||||
|
|
||||||
// Cache channel state
|
|
||||||
channel = res.channel;
|
|
||||||
at = res.at;
|
|
||||||
|
|
||||||
var messages = res.messages;
|
|
||||||
messages.forEach(function(message) {
|
|
||||||
var ev = document.createEvent('CustomEvent');
|
|
||||||
ev.initCustomEvent(message.event, true, true, message.detail);
|
|
||||||
window.dispatchEvent(ev);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reconnect
|
|
||||||
setTimeout(Channel.connect, delay);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = Channel;
|
|
|
@ -1,29 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @providesModule XHR
|
|
||||||
*/
|
|
||||||
|
|
||||||
function request(method, url, callback) {
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
xhr.open(method, url);
|
|
||||||
xhr.onreadystatechange = function() {
|
|
||||||
if (xhr.readyState === 4) {
|
|
||||||
if (xhr.status === 200) {
|
|
||||||
callback(null, xhr);
|
|
||||||
} else {
|
|
||||||
callback(new Error('status = ' + xhr.status, xhr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhr.send();
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.get = function(url, callback) {
|
|
||||||
request('GET', url, callback);
|
|
||||||
};
|
|
|
@ -1,58 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @providesModule code
|
|
||||||
*/
|
|
||||||
var XHR = require('XHR');
|
|
||||||
|
|
||||||
var $ = function(sel) {return document.querySelector(sel);};
|
|
||||||
|
|
||||||
function getListItems(files) {
|
|
||||||
var items = [];
|
|
||||||
files.forEach(function(file) {
|
|
||||||
var displayName = file.name + (file.type == 1 ? '/' : '');
|
|
||||||
items.push(
|
|
||||||
React.DOM.li({
|
|
||||||
className: 'type' + file.type,
|
|
||||||
key: file.ino
|
|
||||||
}, displayName)
|
|
||||||
);
|
|
||||||
if (file.type === 1) {
|
|
||||||
items.push(getListItems(file.nodes));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return React.DOM.ol(null, items);
|
|
||||||
}
|
|
||||||
|
|
||||||
var FileList = React.createClass({
|
|
||||||
getInitialState: function() {
|
|
||||||
return {files: []};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
XHR.get(
|
|
||||||
this.props.source,
|
|
||||||
function(err, xhr) {
|
|
||||||
if (err) {throw err;}
|
|
||||||
|
|
||||||
var files = JSON.parse(xhr.responseText);
|
|
||||||
this.setState({files: files});
|
|
||||||
}.bind(this)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
return getListItems(this.state.files);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('load', function() {
|
|
||||||
React.render(React.createElement(FileList, {source: '/files'}),
|
|
||||||
$('#code'));
|
|
||||||
});
|
|
|
@ -1,64 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @providesModule main
|
|
||||||
*/
|
|
||||||
var Channel = require('Channel');
|
|
||||||
|
|
||||||
function toArray(arr) {return Array.prototype.slice.apply(arr);}
|
|
||||||
function $(sel) {return document.querySelector(sel);}
|
|
||||||
function $$(sel) {return toArray(document.querySelectorAll(sel));}
|
|
||||||
|
|
||||||
window.addEventListener('load', function() {
|
|
||||||
function channelLog() {
|
|
||||||
var args = Array.prototype.slice.apply(arguments);
|
|
||||||
var ts = new Date();
|
|
||||||
var el = document.createElement('li');
|
|
||||||
args.unshift(ts.getHours() + ':' +
|
|
||||||
('0' + ts.getMinutes()).substr(0,2) + ':' +
|
|
||||||
('0' + ts.getSeconds()).substr(0,2));
|
|
||||||
el.className = 'console-entry';
|
|
||||||
el.innerHTML = args.join(' ');
|
|
||||||
$('#console').appendChild(el);
|
|
||||||
el.scrollIntoView();
|
|
||||||
}
|
|
||||||
|
|
||||||
global.addEventListener('ChannelInit', function(event) {
|
|
||||||
$('#console').innerHTML = '';
|
|
||||||
channelLog(event.type);
|
|
||||||
});
|
|
||||||
|
|
||||||
global.addEventListener('ChannelLog', function(event) {
|
|
||||||
channelLog.apply(null, event.detail);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Tab pane support
|
|
||||||
function showTab(paneId) {
|
|
||||||
paneId = paneId.replace(/\W/g, '');
|
|
||||||
if (paneId) {
|
|
||||||
$$('#nav-panes > div').forEach(function(pane) {
|
|
||||||
pane.classList.toggle('active', pane.id === paneId);
|
|
||||||
});
|
|
||||||
$$('#nav-tabs li').forEach(function(tab) {
|
|
||||||
tab.classList.toggle('active',
|
|
||||||
tab.getAttribute('data-pane') === paneId);
|
|
||||||
});
|
|
||||||
global.history.replaceState(null, null, '#' + paneId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#nav-tabs').onclick = function(e) {
|
|
||||||
showTab(e.target.getAttribute('data-pane'));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Show current pane
|
|
||||||
showTab(location.hash);
|
|
||||||
|
|
||||||
// Connect to server-push channel
|
|
||||||
Channel.connect();
|
|
||||||
});
|
|
|
@ -1,104 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
html {
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
margin-right: 200px
|
|
||||||
}
|
|
||||||
|
|
||||||
#nav-tabs {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
background-color: #eee;
|
|
||||||
border-bottom: solid 1px black;
|
|
||||||
font-size: 10pt;
|
|
||||||
font-weight: bold;
|
|
||||||
vertical-align: bottom;
|
|
||||||
line-height: 20px;
|
|
||||||
height: 29px;
|
|
||||||
}
|
|
||||||
#nav-tabs li {
|
|
||||||
padding: 0 10px;
|
|
||||||
margin: 0;
|
|
||||||
border-bottom-width: 0;
|
|
||||||
display:inline-block;
|
|
||||||
cursor: pointer;
|
|
||||||
line-height: 29px;
|
|
||||||
}
|
|
||||||
#nav-tabs li:first-child {
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
#nav-tabs li.active {
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nav-panes {
|
|
||||||
position: absolute;
|
|
||||||
top: 30px;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
bottom: 0px;
|
|
||||||
scroll: auto;
|
|
||||||
overflow: auto;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nav-panes .pane {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
#nav-panes .active {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pane {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#console {
|
|
||||||
padding-left: 5px;
|
|
||||||
}
|
|
||||||
#console li {
|
|
||||||
font-size: 10pt;
|
|
||||||
font-family: monospace;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin: 0;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#code > ol {
|
|
||||||
font-size: 10pt;
|
|
||||||
font-family: monospace;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
#code ol ol {
|
|
||||||
margin-left: 1em;
|
|
||||||
padding-left: 1em;
|
|
||||||
border-left: dashed 1px #ddd;
|
|
||||||
}
|
|
||||||
#code li {
|
|
||||||
color: #000;
|
|
||||||
font-weight: normal;
|
|
||||||
list-style: none;
|
|
||||||
line-height: 1.2em;
|
|
||||||
}
|
|
||||||
#code .type1 {
|
|
||||||
color: #009;
|
|
||||||
}
|
|
||||||
#code .type2 {
|
|
||||||
color: #909;
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<!--
|
|
||||||
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.
|
|
||||||
-->
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<link rel="stylesheet" type="text/css" href="css/index.css">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<ul id="nav-tabs">
|
|
||||||
<li data-pane="main">JS App Server</li>
|
|
||||||
<li data-pane="console">Console</li>
|
|
||||||
<li data-pane="code">Code</li>
|
|
||||||
<li data-pane="activity">Activity</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div id="nav-panes">
|
|
||||||
<div id="main" class="pane">
|
|
||||||
<p>Welcome to the react packager project.</p>
|
|
||||||
<a href="http://localhost:3000/resource?path=index.js">Get example index.js package</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="console" class="pane"></div>
|
|
||||||
<div id="code" class="pane"></div>
|
|
||||||
<div id="activity" class="pane">react packager tasks in progress</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="http://fb.me/react-0.12.0.js"></script>
|
|
||||||
<script src="http://localhost:3000/resource?path=index.js"></script>
|
|
||||||
<script type="text/javascript">require('index');</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -16,24 +16,31 @@ useGracefulFs();
|
||||||
|
|
||||||
var Activity = require('./src/Activity');
|
var Activity = require('./src/Activity');
|
||||||
var Server = require('./src/Server');
|
var Server = require('./src/Server');
|
||||||
|
var SocketInterface = require('./src/SocketInterface');
|
||||||
|
|
||||||
exports.middleware = function(options) {
|
exports.middleware = function(options) {
|
||||||
var server = new Server(options);
|
var server = new Server(options);
|
||||||
return server.processRequest.bind(server);
|
return server.processRequest.bind(server);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.buildPackage = function(options, packageOptions) {
|
exports.Activity = Activity;
|
||||||
|
|
||||||
|
// Renamed "package" to "bundle". But maintain backwards
|
||||||
|
// compat.
|
||||||
|
exports.buildPackage =
|
||||||
|
exports.buildBundle = function(options, bundleOptions) {
|
||||||
var server = createServer(options);
|
var server = createServer(options);
|
||||||
return server.buildPackage(packageOptions)
|
return server.buildBundle(bundleOptions)
|
||||||
.then(function(p) {
|
.then(function(p) {
|
||||||
server.end();
|
server.end();
|
||||||
return p;
|
return p;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.buildPackageFromUrl = function(options, reqUrl) {
|
exports.buildPackageFromUrl =
|
||||||
|
exports.buildBundleFromUrl = function(options, reqUrl) {
|
||||||
var server = createServer(options);
|
var server = createServer(options);
|
||||||
return server.buildPackageFromUrl(reqUrl)
|
return server.buildBundleFromUrl(reqUrl)
|
||||||
.then(function(p) {
|
.then(function(p) {
|
||||||
server.end();
|
server.end();
|
||||||
return p;
|
return p;
|
||||||
|
@ -49,17 +56,44 @@ exports.getDependencies = function(options, main) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.createClientFor = function(options) {
|
||||||
|
return SocketInterface.getOrCreateSocketFor(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
process.on('message', function(m) {
|
||||||
|
if (m && m.type && m.type === 'createSocketServer') {
|
||||||
|
console.log('server got ipc message', m);
|
||||||
|
var options = m.data.options;
|
||||||
|
|
||||||
|
// regexp doesn't naturally serialize to json.
|
||||||
|
options.blacklistRE = new RegExp(options.blacklistRE.source);
|
||||||
|
|
||||||
|
SocketInterface.createSocketServer(
|
||||||
|
m.data.sockPath,
|
||||||
|
m.data.options
|
||||||
|
).then(
|
||||||
|
function() {
|
||||||
|
console.log('succesfully created server', m);
|
||||||
|
process.send({ type: 'createdServer' });
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
console.log('error creating server', error.code);
|
||||||
|
if (error.code === 'EADDRINUSE') {
|
||||||
|
// Server already listening, this may happen if multiple
|
||||||
|
// clients where started in quick succussion (buck).
|
||||||
|
process.send({ type: 'createdServer' });
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function useGracefulFs() {
|
function useGracefulFs() {
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var gracefulFs = require('graceful-fs');
|
var gracefulFs = require('graceful-fs');
|
||||||
|
gracefulFs.gracefulify(fs);
|
||||||
// A bit sneaky but it's not straightforward to update all the
|
|
||||||
// modules we depend on.
|
|
||||||
Object.keys(fs).forEach(function(method) {
|
|
||||||
if (typeof fs[method] === 'function' && gracefulFs[method]) {
|
|
||||||
fs[method] = gracefulFs[method];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createServer(options) {
|
function createServer(options) {
|
||||||
|
|
|
@ -13,64 +13,63 @@ jest.setMock('chalk', {
|
||||||
dim: function(s) { return s; },
|
dim: function(s) { return s; },
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Activity', function() {
|
describe('Activity', () => {
|
||||||
var Activity;
|
const origConsoleLog = console.log;
|
||||||
|
let Activity;
|
||||||
|
|
||||||
var origConsoleLog = console.log;
|
beforeEach(() => {
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
console.log = jest.genMockFn();
|
console.log = jest.genMockFn();
|
||||||
Activity = require('../');
|
Activity = require('../');
|
||||||
jest.runOnlyPendingTimers();
|
jest.runOnlyPendingTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(() => {
|
||||||
console.log = origConsoleLog;
|
console.log = origConsoleLog;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('startEvent', function() {
|
describe('startEvent', () => {
|
||||||
it('writes a START event out to the console', function() {
|
it('writes a START event out to the console', () => {
|
||||||
var EVENT_NAME = 'EVENT_NAME';
|
const EVENT_NAME = 'EVENT_NAME';
|
||||||
var DATA = {someData: 42};
|
const DATA = {someData: 42};
|
||||||
|
|
||||||
Activity.startEvent(EVENT_NAME, DATA);
|
Activity.startEvent(EVENT_NAME, DATA);
|
||||||
jest.runOnlyPendingTimers();
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
expect(console.log.mock.calls.length).toBe(1);
|
expect(console.log.mock.calls.length).toBe(1);
|
||||||
var consoleMsg = console.log.mock.calls[0][0];
|
const consoleMsg = console.log.mock.calls[0][0];
|
||||||
expect(consoleMsg).toContain('START');
|
expect(consoleMsg).toContain('START');
|
||||||
expect(consoleMsg).toContain(EVENT_NAME);
|
expect(consoleMsg).toContain(EVENT_NAME);
|
||||||
expect(consoleMsg).toContain(JSON.stringify(DATA));
|
expect(consoleMsg).toContain(JSON.stringify(DATA));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('endEvent', function() {
|
describe('endEvent', () => {
|
||||||
it('writes an END event out to the console', function() {
|
it('writes an END event out to the console', () => {
|
||||||
var EVENT_NAME = 'EVENT_NAME';
|
const EVENT_NAME = 'EVENT_NAME';
|
||||||
var DATA = {someData: 42};
|
const DATA = {someData: 42};
|
||||||
|
|
||||||
var eventID = Activity.startEvent(EVENT_NAME, DATA);
|
const eventID = Activity.startEvent(EVENT_NAME, DATA);
|
||||||
Activity.endEvent(eventID);
|
Activity.endEvent(eventID);
|
||||||
jest.runOnlyPendingTimers();
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
expect(console.log.mock.calls.length).toBe(2);
|
expect(console.log.mock.calls.length).toBe(2);
|
||||||
var consoleMsg = console.log.mock.calls[1][0];
|
const consoleMsg = console.log.mock.calls[1][0];
|
||||||
expect(consoleMsg).toContain('END');
|
expect(consoleMsg).toContain('END');
|
||||||
expect(consoleMsg).toContain(EVENT_NAME);
|
expect(consoleMsg).toContain(EVENT_NAME);
|
||||||
expect(consoleMsg).toContain(JSON.stringify(DATA));
|
expect(consoleMsg).toContain(JSON.stringify(DATA));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws when called with an invalid eventId', function() {
|
it('throws when called with an invalid eventId', () => {
|
||||||
expect(function() {
|
expect(() => Activity.endEvent(42)).toThrow(
|
||||||
Activity.endEvent(42);
|
'event(42) is not a valid event id!',
|
||||||
}).toThrow('event(42) is not a valid event id!');
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws when called with an expired eventId', function() {
|
it('throws when called with an expired eventId', () => {
|
||||||
var eid = Activity.startEvent('', '');
|
const eid = Activity.startEvent('', '');
|
||||||
Activity.endEvent(eid);
|
Activity.endEvent(eid);
|
||||||
|
|
||||||
expect(function() {
|
expect(() => {
|
||||||
Activity.endEvent(eid);
|
Activity.endEvent(eid);
|
||||||
}).toThrow('event(3) has already ended!');
|
}).toThrow('event(3) has already ended!');
|
||||||
|
|
||||||
|
@ -78,17 +77,16 @@ describe('Activity', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('signal', function() {
|
describe('signal', () => {
|
||||||
it('writes a SIGNAL event out to the console', function() {
|
it('writes a SIGNAL event out to the console', () => {
|
||||||
|
const EVENT_NAME = 'EVENT_NAME';
|
||||||
var EVENT_NAME = 'EVENT_NAME';
|
const DATA = {someData: 42};
|
||||||
var DATA = {someData: 42};
|
|
||||||
|
|
||||||
Activity.signal(EVENT_NAME, DATA);
|
Activity.signal(EVENT_NAME, DATA);
|
||||||
jest.runOnlyPendingTimers();
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
expect(console.log.mock.calls.length).toBe(1);
|
expect(console.log.mock.calls.length).toBe(1);
|
||||||
var consoleMsg = console.log.mock.calls[0][0];
|
const consoleMsg = console.log.mock.calls[0][0];
|
||||||
expect(consoleMsg).toContain(EVENT_NAME);
|
expect(consoleMsg).toContain(EVENT_NAME);
|
||||||
expect(consoleMsg).toContain(JSON.stringify(DATA));
|
expect(consoleMsg).toContain(JSON.stringify(DATA));
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,19 +8,22 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
|
const events = require('events');
|
||||||
|
|
||||||
var COLLECTION_PERIOD = 1000;
|
const COLLECTION_PERIOD = 1000;
|
||||||
|
|
||||||
var _endedEvents = Object.create(null);
|
const _endedEvents = Object.create(null);
|
||||||
var _eventStarts = Object.create(null);
|
const _eventStarts = Object.create(null);
|
||||||
var _queuedActions = [];
|
const _queuedActions = [];
|
||||||
var _scheduledCollectionTimer = null;
|
const _eventEmitter = new events.EventEmitter();
|
||||||
var _uuid = 1;
|
|
||||||
var _enabled = true;
|
let _scheduledCollectionTimer = null;
|
||||||
|
let _uuid = 1;
|
||||||
|
let _enabled = true;
|
||||||
|
|
||||||
function endEvent(eventId) {
|
function endEvent(eventId) {
|
||||||
var eventEndTime = Date.now();
|
const eventEndTime = Date.now();
|
||||||
|
|
||||||
if (!_eventStarts[eventId]) {
|
if (!_eventStarts[eventId]) {
|
||||||
_throw('event(' + eventId + ') is not a valid event id!');
|
_throw('event(' + eventId + ') is not a valid event id!');
|
||||||
|
@ -39,7 +42,7 @@ function endEvent(eventId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function signal(eventName, data) {
|
function signal(eventName, data) {
|
||||||
var signalTime = Date.now();
|
const signalTime = Date.now();
|
||||||
|
|
||||||
if (eventName == null) {
|
if (eventName == null) {
|
||||||
_throw('No event name specified');
|
_throw('No event name specified');
|
||||||
|
@ -58,7 +61,7 @@ function signal(eventName, data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function startEvent(eventName, data) {
|
function startEvent(eventName, data) {
|
||||||
var eventStartTime = Date.now();
|
const eventStartTime = Date.now();
|
||||||
|
|
||||||
if (eventName == null) {
|
if (eventName == null) {
|
||||||
_throw('No event name specified');
|
_throw('No event name specified');
|
||||||
|
@ -68,8 +71,8 @@ function startEvent(eventName, data) {
|
||||||
data = null;
|
data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var eventId = _uuid++;
|
const eventId = _uuid++;
|
||||||
var action = {
|
const action = {
|
||||||
action: 'startEvent',
|
action: 'startEvent',
|
||||||
data: data,
|
data: data,
|
||||||
eventId: eventId,
|
eventId: eventId,
|
||||||
|
@ -88,7 +91,7 @@ function disable() {
|
||||||
|
|
||||||
function _runCollection() {
|
function _runCollection() {
|
||||||
/* jshint -W084 */
|
/* jshint -W084 */
|
||||||
var action;
|
let action;
|
||||||
while ((action = _queuedActions.shift())) {
|
while ((action = _queuedActions.shift())) {
|
||||||
_writeAction(action);
|
_writeAction(action);
|
||||||
}
|
}
|
||||||
|
@ -98,6 +101,7 @@ function _runCollection() {
|
||||||
|
|
||||||
function _scheduleAction(action) {
|
function _scheduleAction(action) {
|
||||||
_queuedActions.push(action);
|
_queuedActions.push(action);
|
||||||
|
_eventEmitter.emit(action.action, action);
|
||||||
|
|
||||||
if (_scheduledCollectionTimer === null) {
|
if (_scheduledCollectionTimer === null) {
|
||||||
_scheduledCollectionTimer = setTimeout(_runCollection, COLLECTION_PERIOD);
|
_scheduledCollectionTimer = setTimeout(_runCollection, COLLECTION_PERIOD);
|
||||||
|
@ -114,10 +118,10 @@ function _scheduleAction(action) {
|
||||||
* won't be adding such a non-trivial optimization anytime soon)
|
* won't be adding such a non-trivial optimization anytime soon)
|
||||||
*/
|
*/
|
||||||
function _throw(msg) {
|
function _throw(msg) {
|
||||||
var err = new Error(msg);
|
const err = new Error(msg);
|
||||||
|
|
||||||
// Strip off the call to _throw()
|
// Strip off the call to _throw()
|
||||||
var stack = err.stack.split('\n');
|
const stack = err.stack.split('\n');
|
||||||
stack.splice(1, 1);
|
stack.splice(1, 1);
|
||||||
err.stack = stack.join('\n');
|
err.stack = stack.join('\n');
|
||||||
|
|
||||||
|
@ -129,8 +133,8 @@ function _writeAction(action) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = action.data ? ': ' + JSON.stringify(action.data) : '';
|
const data = action.data ? ': ' + JSON.stringify(action.data) : '';
|
||||||
var fmtTime = new Date(action.tstamp).toLocaleTimeString();
|
const fmtTime = new Date(action.tstamp).toLocaleTimeString();
|
||||||
|
|
||||||
switch (action.action) {
|
switch (action.action) {
|
||||||
case 'startEvent':
|
case 'startEvent':
|
||||||
|
@ -142,8 +146,8 @@ function _writeAction(action) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'endEvent':
|
case 'endEvent':
|
||||||
var startAction = _eventStarts[action.eventId];
|
const startAction = _eventStarts[action.eventId];
|
||||||
var startData = startAction.data ? ': ' + JSON.stringify(startAction.data) : '';
|
const startData = startAction.data ? ': ' + JSON.stringify(startAction.data) : '';
|
||||||
console.log(chalk.dim(
|
console.log(chalk.dim(
|
||||||
'[' + fmtTime + '] ' +
|
'[' + fmtTime + '] ' +
|
||||||
'<END> ' + startAction.eventName +
|
'<END> ' + startAction.eventName +
|
||||||
|
@ -171,3 +175,4 @@ exports.endEvent = endEvent;
|
||||||
exports.signal = signal;
|
exports.signal = signal;
|
||||||
exports.startEvent = startEvent;
|
exports.startEvent = startEvent;
|
||||||
exports.disable = disable;
|
exports.disable = disable;
|
||||||
|
exports.eventEmitter = _eventEmitter;
|
||||||
|
|
|
@ -8,22 +8,22 @@ jest
|
||||||
.mock('crypto')
|
.mock('crypto')
|
||||||
.mock('fs');
|
.mock('fs');
|
||||||
|
|
||||||
var Promise = require('promise');
|
const Promise = require('promise');
|
||||||
|
|
||||||
describe('AssetServer', function() {
|
describe('AssetServer', () => {
|
||||||
var AssetServer;
|
let AssetServer;
|
||||||
var crypto;
|
let crypto;
|
||||||
var fs;
|
let fs;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
AssetServer = require('../');
|
AssetServer = require('../');
|
||||||
crypto = require('crypto');
|
crypto = require('crypto');
|
||||||
fs = require('fs');
|
fs = require('fs');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('assetServer.get', function() {
|
describe('assetServer.get', () => {
|
||||||
pit('should work for the simple case', function() {
|
pit('should work for the simple case', () => {
|
||||||
var server = new AssetServer({
|
const server = new AssetServer({
|
||||||
projectRoots: ['/root'],
|
projectRoots: ['/root'],
|
||||||
assetExts: ['png'],
|
assetExts: ['png'],
|
||||||
});
|
});
|
||||||
|
@ -40,15 +40,15 @@ describe('AssetServer', function() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
server.get('imgs/b.png'),
|
server.get('imgs/b.png'),
|
||||||
server.get('imgs/b@1x.png'),
|
server.get('imgs/b@1x.png'),
|
||||||
]).then(function(resp) {
|
]).then(resp =>
|
||||||
resp.forEach(function(data) {
|
resp.forEach(data =>
|
||||||
expect(data).toBe('b image');
|
expect(data).toBe('b image')
|
||||||
});
|
)
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('should work for the simple case with jpg', function() {
|
pit('should work for the simple case with jpg', () => {
|
||||||
var server = new AssetServer({
|
const server = new AssetServer({
|
||||||
projectRoots: ['/root'],
|
projectRoots: ['/root'],
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
});
|
});
|
||||||
|
@ -65,16 +65,16 @@ describe('AssetServer', function() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
server.get('imgs/b.jpg'),
|
server.get('imgs/b.jpg'),
|
||||||
server.get('imgs/b.png'),
|
server.get('imgs/b.png'),
|
||||||
]).then(function(data) {
|
]).then(data =>
|
||||||
expect(data).toEqual([
|
expect(data).toEqual([
|
||||||
'jpeg image',
|
'jpeg image',
|
||||||
'png image',
|
'png image',
|
||||||
]);
|
])
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('should pick the bigger one', function() {
|
pit('should pick the bigger one', () => {
|
||||||
var server = new AssetServer({
|
const server = new AssetServer({
|
||||||
projectRoots: ['/root'],
|
projectRoots: ['/root'],
|
||||||
assetExts: ['png'],
|
assetExts: ['png'],
|
||||||
});
|
});
|
||||||
|
@ -90,13 +90,13 @@ describe('AssetServer', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return server.get('imgs/b@3x.png').then(function(data) {
|
return server.get('imgs/b@3x.png').then(data =>
|
||||||
expect(data).toBe('b4 image');
|
expect(data).toBe('b4 image')
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('should support multiple project roots', function() {
|
pit('should support multiple project roots', () => {
|
||||||
var server = new AssetServer({
|
const server = new AssetServer({
|
||||||
projectRoots: ['/root', '/root2'],
|
projectRoots: ['/root', '/root2'],
|
||||||
assetExts: ['png'],
|
assetExts: ['png'],
|
||||||
});
|
});
|
||||||
|
@ -116,27 +116,23 @@ describe('AssetServer', function() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return server.get('newImages/imgs/b.png').then(function(data) {
|
return server.get('newImages/imgs/b.png').then(data =>
|
||||||
expect(data).toBe('b1 image');
|
expect(data).toBe('b1 image')
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('assetSerer.getAssetData', function() {
|
describe('assetSerer.getAssetData', () => {
|
||||||
pit('should get assetData', function() {
|
pit('should get assetData', () => {
|
||||||
var hash = {
|
const hash = {
|
||||||
update: jest.genMockFn(),
|
update: jest.genMockFn(),
|
||||||
digest: jest.genMockFn(),
|
digest: jest.genMockFn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
hash.digest.mockImpl(function() {
|
hash.digest.mockImpl(() => 'wow such hash');
|
||||||
return 'wow such hash';
|
crypto.createHash.mockImpl(() => hash);
|
||||||
});
|
|
||||||
crypto.createHash.mockImpl(function() {
|
|
||||||
return hash;
|
|
||||||
});
|
|
||||||
|
|
||||||
var server = new AssetServer({
|
const server = new AssetServer({
|
||||||
projectRoots: ['/root'],
|
projectRoots: ['/root'],
|
||||||
assetExts: ['png'],
|
assetExts: ['png'],
|
||||||
});
|
});
|
||||||
|
@ -152,7 +148,7 @@ describe('AssetServer', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return server.getAssetData('imgs/b.png').then(function(data) {
|
return server.getAssetData('imgs/b.png').then(data => {
|
||||||
expect(hash.update.mock.calls.length).toBe(4);
|
expect(hash.update.mock.calls.length).toBe(4);
|
||||||
expect(data).toEqual({
|
expect(data).toEqual({
|
||||||
type: 'png',
|
type: 'png',
|
||||||
|
@ -163,20 +159,16 @@ describe('AssetServer', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('should get assetData for non-png images', function() {
|
pit('should get assetData for non-png images', () => {
|
||||||
var hash = {
|
const hash = {
|
||||||
update: jest.genMockFn(),
|
update: jest.genMockFn(),
|
||||||
digest: jest.genMockFn(),
|
digest: jest.genMockFn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
hash.digest.mockImpl(function() {
|
hash.digest.mockImpl(() => 'wow such hash');
|
||||||
return 'wow such hash';
|
crypto.createHash.mockImpl(() => hash);
|
||||||
});
|
|
||||||
crypto.createHash.mockImpl(function() {
|
|
||||||
return hash;
|
|
||||||
});
|
|
||||||
|
|
||||||
var server = new AssetServer({
|
const server = new AssetServer({
|
||||||
projectRoots: ['/root'],
|
projectRoots: ['/root'],
|
||||||
assetExts: ['png', 'jpeg'],
|
assetExts: ['png', 'jpeg'],
|
||||||
});
|
});
|
||||||
|
@ -192,7 +184,7 @@ describe('AssetServer', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return server.getAssetData('imgs/b.jpg').then(function(data) {
|
return server.getAssetData('imgs/b.jpg').then(data => {
|
||||||
expect(hash.update.mock.calls.length).toBe(4);
|
expect(hash.update.mock.calls.length).toBe(4);
|
||||||
expect(data).toEqual({
|
expect(data).toEqual({
|
||||||
type: 'jpg',
|
type: 'jpg',
|
||||||
|
|
|
@ -8,20 +8,20 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var declareOpts = require('../lib/declareOpts');
|
const Promise = require('promise');
|
||||||
var getAssetDataFromName = require('../lib/getAssetDataFromName');
|
|
||||||
var path = require('path');
|
|
||||||
var Promise = require('promise');
|
|
||||||
var fs = require('fs');
|
|
||||||
var crypto = require('crypto');
|
|
||||||
|
|
||||||
var stat = Promise.denodeify(fs.stat);
|
const crypto = require('crypto');
|
||||||
var readDir = Promise.denodeify(fs.readdir);
|
const declareOpts = require('../lib/declareOpts');
|
||||||
var readFile = Promise.denodeify(fs.readFile);
|
const fs = require('fs');
|
||||||
|
const getAssetDataFromName = require('../lib/getAssetDataFromName');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = AssetServer;
|
const stat = Promise.denodeify(fs.stat);
|
||||||
|
const readDir = Promise.denodeify(fs.readdir);
|
||||||
|
const readFile = Promise.denodeify(fs.readFile);
|
||||||
|
|
||||||
var validateOpts = declareOpts({
|
|
||||||
|
const validateOpts = declareOpts({
|
||||||
projectRoots: {
|
projectRoots: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -32,13 +32,52 @@ var validateOpts = declareOpts({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function AssetServer(options) {
|
class AssetServer {
|
||||||
var opts = validateOpts(options);
|
constructor(options) {
|
||||||
|
const opts = validateOpts(options);
|
||||||
this._roots = opts.projectRoots;
|
this._roots = opts.projectRoots;
|
||||||
this._assetExts = opts.assetExts;
|
this._assetExts = opts.assetExts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
get(assetPath) {
|
||||||
|
const assetData = getAssetDataFromName(assetPath);
|
||||||
|
return this._getAssetRecord(assetPath).then(record => {
|
||||||
|
for (let i = 0; i < record.scales.length; i++) {
|
||||||
|
if (record.scales[i] >= assetData.resolution) {
|
||||||
|
return readFile(record.files[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return readFile(record.files[record.files.length - 1]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getAssetData(assetPath) {
|
||||||
|
const nameData = getAssetDataFromName(assetPath);
|
||||||
|
const data = {
|
||||||
|
name: nameData.name,
|
||||||
|
type: nameData.type,
|
||||||
|
};
|
||||||
|
|
||||||
|
return this._getAssetRecord(assetPath).then(record => {
|
||||||
|
data.scales = record.scales;
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
|
record.files.map(file => stat(file))
|
||||||
|
);
|
||||||
|
}).then(stats => {
|
||||||
|
const hash = crypto.createHash('md5');
|
||||||
|
|
||||||
|
stats.forEach(fstat =>
|
||||||
|
hash.update(fstat.mtime.getTime().toString())
|
||||||
|
);
|
||||||
|
|
||||||
|
data.hash = hash.digest('hex');
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* Given a request for an image by path. That could contain a resolution
|
* Given a request for an image by path. That could contain a resolution
|
||||||
* postfix, we need to find that image (or the closest one to it's resolution)
|
* postfix, we need to find that image (or the closest one to it's resolution)
|
||||||
* in one of the project roots:
|
* in one of the project roots:
|
||||||
|
@ -48,102 +87,61 @@ function AssetServer(options) {
|
||||||
* 3. We then build a map of all assets and their scales in this directory
|
* 3. We then build a map of all assets and their scales in this directory
|
||||||
* 4. Then pick the closest resolution (rounding up) to the requested one
|
* 4. Then pick the closest resolution (rounding up) to the requested one
|
||||||
*/
|
*/
|
||||||
|
_getAssetRecord(assetPath) {
|
||||||
|
const filename = path.basename(assetPath);
|
||||||
|
|
||||||
AssetServer.prototype._getAssetRecord = function(assetPath) {
|
return (
|
||||||
var filename = path.basename(assetPath);
|
this._findRoot(
|
||||||
|
|
||||||
return findRoot(
|
|
||||||
this._roots,
|
this._roots,
|
||||||
path.dirname(assetPath)
|
path.dirname(assetPath)
|
||||||
).then(function(dir) {
|
)
|
||||||
return Promise.all([
|
.then(dir => Promise.all([
|
||||||
dir,
|
dir,
|
||||||
readDir(dir),
|
readDir(dir),
|
||||||
]);
|
]))
|
||||||
}).then(function(res) {
|
.then(res => {
|
||||||
var dir = res[0];
|
const dir = res[0];
|
||||||
var files = res[1];
|
const files = res[1];
|
||||||
var assetData = getAssetDataFromName(filename);
|
const assetData = getAssetDataFromName(filename);
|
||||||
|
|
||||||
var map = buildAssetMap(dir, files);
|
const map = this._buildAssetMap(dir, files);
|
||||||
var record = map[assetData.assetName];
|
const record = map[assetData.assetName];
|
||||||
|
|
||||||
if (!record) {
|
if (!record) {
|
||||||
throw new Error('Asset not found');
|
throw new Error('Asset not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
return record;
|
return record;
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
AssetServer.prototype.get = function(assetPath) {
|
|
||||||
var assetData = getAssetDataFromName(assetPath);
|
|
||||||
return this._getAssetRecord(assetPath).then(function(record) {
|
|
||||||
for (var i = 0; i < record.scales.length; i++) {
|
|
||||||
if (record.scales[i] >= assetData.resolution) {
|
|
||||||
return readFile(record.files[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return readFile(record.files[record.files.length - 1]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
AssetServer.prototype.getAssetData = function(assetPath) {
|
|
||||||
var nameData = getAssetDataFromName(assetPath);
|
|
||||||
var data = {
|
|
||||||
name: nameData.name,
|
|
||||||
type: nameData.type,
|
|
||||||
};
|
|
||||||
|
|
||||||
return this._getAssetRecord(assetPath).then(function(record) {
|
|
||||||
data.scales = record.scales;
|
|
||||||
|
|
||||||
return Promise.all(
|
|
||||||
record.files.map(function(file) {
|
|
||||||
return stat(file);
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}).then(function(stats) {
|
}
|
||||||
var hash = crypto.createHash('md5');
|
|
||||||
|
|
||||||
stats.forEach(function(fstat) {
|
_findRoot(roots, dir) {
|
||||||
hash.update(fstat.mtime.getTime().toString());
|
|
||||||
});
|
|
||||||
|
|
||||||
data.hash = hash.digest('hex');
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function findRoot(roots, dir) {
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
roots.map(function(root) {
|
roots.map(root => {
|
||||||
var absPath = path.join(root, dir);
|
const absPath = path.join(root, dir);
|
||||||
return stat(absPath).then(function(fstat) {
|
return stat(absPath).then(fstat => {
|
||||||
return {path: absPath, isDirectory: fstat.isDirectory()};
|
return {path: absPath, isDirectory: fstat.isDirectory()};
|
||||||
}, function (err) {
|
}, err => {
|
||||||
return {path: absPath, isDirectory: false};
|
return {path: absPath, isDirectory: false};
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
).then(
|
).then(stats => {
|
||||||
function(stats) {
|
for (let i = 0; i < stats.length; i++) {
|
||||||
for (var i = 0; i < stats.length; i++) {
|
|
||||||
if (stats[i].isDirectory) {
|
if (stats[i].isDirectory) {
|
||||||
return stats[i].path;
|
return stats[i].path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error('Could not find any directories');
|
throw new Error('Could not find any directories');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildAssetMap(dir, files) {
|
_buildAssetMap(dir, files) {
|
||||||
var assets = files.map(getAssetDataFromName);
|
const assets = files.map(getAssetDataFromName);
|
||||||
var map = Object.create(null);
|
const map = Object.create(null);
|
||||||
assets.forEach(function(asset, i) {
|
assets.forEach(function(asset, i) {
|
||||||
var file = files[i];
|
const file = files[i];
|
||||||
var record = map[asset.assetName];
|
let record = map[asset.assetName];
|
||||||
if (!record) {
|
if (!record) {
|
||||||
record = map[asset.assetName] = {
|
record = map[asset.assetName] = {
|
||||||
scales: [],
|
scales: [],
|
||||||
|
@ -151,8 +149,8 @@ function buildAssetMap(dir, files) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var insertIndex;
|
let insertIndex;
|
||||||
var length = record.scales.length;
|
const length = record.scales.length;
|
||||||
for (insertIndex = 0; insertIndex < length; insertIndex++) {
|
for (insertIndex = 0; insertIndex < length; insertIndex++) {
|
||||||
if (asset.resolution < record.scales[insertIndex]) {
|
if (asset.resolution < record.scales[insertIndex]) {
|
||||||
break;
|
break;
|
||||||
|
@ -163,4 +161,7 @@ function buildAssetMap(dir, files) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = AssetServer;
|
||||||
|
|
|
@ -0,0 +1,337 @@
|
||||||
|
/**
|
||||||
|
* 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 _ = require('underscore');
|
||||||
|
const base64VLQ = require('./base64-vlq');
|
||||||
|
const UglifyJS = require('uglify-js');
|
||||||
|
const ModuleTransport = require('../lib/ModuleTransport');
|
||||||
|
|
||||||
|
const SOURCEMAPPING_URL = '\n\/\/@ sourceMappingURL=';
|
||||||
|
|
||||||
|
class Bundle {
|
||||||
|
constructor(sourceMapUrl) {
|
||||||
|
this._finalized = false;
|
||||||
|
this._modules = [];
|
||||||
|
this._assets = [];
|
||||||
|
this._sourceMapUrl = sourceMapUrl;
|
||||||
|
this._shouldCombineSourceMaps = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMainModuleId(moduleId) {
|
||||||
|
this._mainModuleId = moduleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
addModule(module) {
|
||||||
|
if (!(module instanceof ModuleTransport)) {
|
||||||
|
throw new Error('Expeceted a ModuleTransport object');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get a map from the transformer we'll switch to a mode
|
||||||
|
// were we're combining the source maps as opposed to
|
||||||
|
if (!this._shouldCombineSourceMaps && module.map != null) {
|
||||||
|
this._shouldCombineSourceMaps = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._modules.push(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
getModules() {
|
||||||
|
return this._modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
addAsset(asset) {
|
||||||
|
this._assets.push(asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize(options) {
|
||||||
|
options = options || {};
|
||||||
|
if (options.runMainModule) {
|
||||||
|
const runCode = ';require("' + this._mainModuleId + '");';
|
||||||
|
this.addModule(new ModuleTransport({
|
||||||
|
code: runCode,
|
||||||
|
virtual: true,
|
||||||
|
sourceCode: runCode,
|
||||||
|
sourcePath: 'RunMainModule.js'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.freeze(this._modules);
|
||||||
|
Object.seal(this._modules);
|
||||||
|
Object.freeze(this._assets);
|
||||||
|
Object.seal(this._assets);
|
||||||
|
this._finalized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_assertFinalized() {
|
||||||
|
if (!this._finalized) {
|
||||||
|
throw new Error('Bundle needs to be finalized before getting any source');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_getSource() {
|
||||||
|
if (this._source == null) {
|
||||||
|
this._source = _.pluck(this._modules, 'code').join('\n');
|
||||||
|
}
|
||||||
|
return this._source;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getInlineSourceMap() {
|
||||||
|
if (this._inlineSourceMap == null) {
|
||||||
|
const sourceMap = this.getSourceMap({excludeSource: true});
|
||||||
|
/*eslint-env node*/
|
||||||
|
const encoded = new Buffer(JSON.stringify(sourceMap)).toString('base64');
|
||||||
|
this._inlineSourceMap = 'data:application/json;base64,' + encoded;
|
||||||
|
}
|
||||||
|
return this._inlineSourceMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSource(options) {
|
||||||
|
this._assertFinalized();
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
if (options.minify) {
|
||||||
|
return this.getMinifiedSourceAndMap().code;
|
||||||
|
}
|
||||||
|
|
||||||
|
let source = this._getSource();
|
||||||
|
|
||||||
|
if (options.inlineSourceMap) {
|
||||||
|
source += SOURCEMAPPING_URL + this._getInlineSourceMap();
|
||||||
|
} else if (this._sourceMapUrl) {
|
||||||
|
source += SOURCEMAPPING_URL + this._sourceMapUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMinifiedSourceAndMap() {
|
||||||
|
this._assertFinalized();
|
||||||
|
|
||||||
|
const source = this._getSource();
|
||||||
|
try {
|
||||||
|
return UglifyJS.minify(source, {
|
||||||
|
fromString: true,
|
||||||
|
outSourceMap: 'bundle.js',
|
||||||
|
inSourceMap: this.getSourceMap(),
|
||||||
|
});
|
||||||
|
} catch(e) {
|
||||||
|
// Sometimes, when somebody is using a new syntax feature that we
|
||||||
|
// don't yet have transform for, the untransformed line is sent to
|
||||||
|
// uglify, and it chokes on it. This code tries to print the line
|
||||||
|
// and the module for easier debugging
|
||||||
|
let errorMessage = 'Error while minifying JS\n';
|
||||||
|
if (e.line) {
|
||||||
|
errorMessage += 'Transformed code line: "' +
|
||||||
|
source.split('\n')[e.line - 1] + '"\n';
|
||||||
|
}
|
||||||
|
if (e.pos) {
|
||||||
|
let fromIndex = source.lastIndexOf('__d(\'', e.pos);
|
||||||
|
if (fromIndex > -1) {
|
||||||
|
fromIndex += '__d(\''.length;
|
||||||
|
const toIndex = source.indexOf('\'', fromIndex);
|
||||||
|
errorMessage += 'Module name (best guess): ' +
|
||||||
|
source.substring(fromIndex, toIndex) + '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errorMessage += e.toString();
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I found a neat trick in the sourcemap spec that makes it easy
|
||||||
|
* to concat sourcemaps. The `sections` field allows us to combine
|
||||||
|
* the sourcemap easily by adding an offset. Tested on chrome.
|
||||||
|
* Seems like it's not yet in Firefox but that should be fine for
|
||||||
|
* now.
|
||||||
|
*/
|
||||||
|
_getCombinedSourceMaps(options) {
|
||||||
|
const result = {
|
||||||
|
version: 3,
|
||||||
|
file: 'bundle.js',
|
||||||
|
sections: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
let line = 0;
|
||||||
|
this._modules.forEach(function(module) {
|
||||||
|
let map = module.map;
|
||||||
|
if (module.virtual) {
|
||||||
|
map = generateSourceMapForVirtualModule(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.excludeSource) {
|
||||||
|
map = _.extend({}, map, {sourcesContent: []});
|
||||||
|
}
|
||||||
|
|
||||||
|
result.sections.push({
|
||||||
|
offset: { line: line, column: 0 },
|
||||||
|
map: map,
|
||||||
|
});
|
||||||
|
line += module.code.split('\n').length;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSourceMap(options) {
|
||||||
|
this._assertFinalized();
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
if (this._shouldCombineSourceMaps) {
|
||||||
|
return this._getCombinedSourceMaps(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mappings = this._getMappings();
|
||||||
|
const map = {
|
||||||
|
file: 'bundle.js',
|
||||||
|
sources: _.pluck(this._modules, 'sourcePath'),
|
||||||
|
version: 3,
|
||||||
|
names: [],
|
||||||
|
mappings: mappings,
|
||||||
|
sourcesContent: options.excludeSource
|
||||||
|
? [] : _.pluck(this._modules, 'sourceCode')
|
||||||
|
};
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAssets() {
|
||||||
|
return this._assets;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getMappings() {
|
||||||
|
const modules = this._modules;
|
||||||
|
|
||||||
|
// The first line mapping in our package is basically the base64vlq code for
|
||||||
|
// zeros (A).
|
||||||
|
const firstLine = 'AAAA';
|
||||||
|
|
||||||
|
// Most other lines in our mappings are all zeros (for module, column etc)
|
||||||
|
// except for the lineno mappinp: curLineno - prevLineno = 1; Which is C.
|
||||||
|
const line = 'AACA';
|
||||||
|
|
||||||
|
const moduleLines = Object.create(null);
|
||||||
|
let mappings = '';
|
||||||
|
for (let i = 0; i < modules.length; i++) {
|
||||||
|
const module = modules[i];
|
||||||
|
const code = module.code;
|
||||||
|
let lastCharNewLine = false;
|
||||||
|
moduleLines[module.sourcePath] = 0;
|
||||||
|
for (let t = 0; t < code.length; t++) {
|
||||||
|
if (t === 0 && i === 0) {
|
||||||
|
mappings += firstLine;
|
||||||
|
} else if (t === 0) {
|
||||||
|
mappings += 'AC';
|
||||||
|
|
||||||
|
// This is the only place were we actually don't know the mapping ahead
|
||||||
|
// of time. When it's a new module (and not the first) the lineno
|
||||||
|
// mapping is 0 (current) - number of lines in prev module.
|
||||||
|
mappings += base64VLQ.encode(
|
||||||
|
0 - moduleLines[modules[i - 1].sourcePath]
|
||||||
|
);
|
||||||
|
mappings += 'A';
|
||||||
|
} else if (lastCharNewLine) {
|
||||||
|
moduleLines[module.sourcePath]++;
|
||||||
|
mappings += line;
|
||||||
|
}
|
||||||
|
lastCharNewLine = code[t] === '\n';
|
||||||
|
if (lastCharNewLine) {
|
||||||
|
mappings += ';';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i !== modules.length - 1) {
|
||||||
|
mappings += ';';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
getJSModulePaths() {
|
||||||
|
return this._modules.filter(function(module) {
|
||||||
|
// Filter out non-js files. Like images etc.
|
||||||
|
return !module.virtual;
|
||||||
|
}).map(function(module) {
|
||||||
|
return module.sourcePath;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getDebugInfo() {
|
||||||
|
return [
|
||||||
|
'<div><h3>Main Module:</h3> ' + this._mainModuleId + '</div>',
|
||||||
|
'<style>',
|
||||||
|
'pre.collapsed {',
|
||||||
|
' height: 10px;',
|
||||||
|
' width: 100px;',
|
||||||
|
' display: block;',
|
||||||
|
' text-overflow: ellipsis;',
|
||||||
|
' overflow: hidden;',
|
||||||
|
' cursor: pointer;',
|
||||||
|
'}',
|
||||||
|
'</style>',
|
||||||
|
'<h3> Module paths and transformed code: </h3>',
|
||||||
|
this._modules.map(function(m) {
|
||||||
|
return '<div> <h4> Path: </h4>' + m.sourcePath + '<br/> <h4> Source: </h4>' +
|
||||||
|
'<code><pre class="collapsed" onclick="this.classList.remove(\'collapsed\')">' +
|
||||||
|
_.escape(m.code) + '</pre></code></div>';
|
||||||
|
}).join('\n'),
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
if (!this._finalized) {
|
||||||
|
throw new Error('Cannot serialize bundle unless finalized');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modules: this._modules,
|
||||||
|
assets: this._assets,
|
||||||
|
sourceMapUrl: this._sourceMapUrl,
|
||||||
|
shouldCombineSourceMaps: this._shouldCombineSourceMaps,
|
||||||
|
mainModuleId: this._mainModuleId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJSON(json) {
|
||||||
|
const bundle = new Bundle(json.sourceMapUrl);
|
||||||
|
bundle._mainModuleId = json.mainModuleId;
|
||||||
|
bundle._assets = json.assets;
|
||||||
|
bundle._modules = json.modules;
|
||||||
|
bundle._sourceMapUrl = json.sourceMapUrl;
|
||||||
|
|
||||||
|
Object.freeze(bundle._modules);
|
||||||
|
Object.seal(bundle._modules);
|
||||||
|
Object.freeze(bundle._assets);
|
||||||
|
Object.seal(bundle._assets);
|
||||||
|
bundle._finalized = true;
|
||||||
|
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateSourceMapForVirtualModule(module) {
|
||||||
|
// All lines map 1-to-1
|
||||||
|
let mappings = 'AAAA;';
|
||||||
|
|
||||||
|
for (let i = 1; i < module.code.split('\n').length; i++) {
|
||||||
|
mappings += 'AACA;';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
version: 3,
|
||||||
|
sources: [ module.sourcePath ],
|
||||||
|
names: [],
|
||||||
|
mappings: mappings,
|
||||||
|
file: module.sourcePath,
|
||||||
|
sourcesContent: [ module.sourceCode ],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Bundle;
|
|
@ -12,35 +12,35 @@ jest.autoMockOff();
|
||||||
|
|
||||||
var SourceMapGenerator = require('source-map').SourceMapGenerator;
|
var SourceMapGenerator = require('source-map').SourceMapGenerator;
|
||||||
|
|
||||||
describe('Package', function() {
|
describe('Bundle', function() {
|
||||||
var ModuleTransport;
|
var ModuleTransport;
|
||||||
var Package;
|
var Bundle;
|
||||||
var ppackage;
|
var bundle;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
Package = require('../Package');
|
Bundle = require('../Bundle');
|
||||||
ModuleTransport = require('../../lib/ModuleTransport');
|
ModuleTransport = require('../../lib/ModuleTransport');
|
||||||
ppackage = new Package('test_url');
|
bundle = new Bundle('test_url');
|
||||||
ppackage.getSourceMap = jest.genMockFn().mockImpl(function() {
|
bundle.getSourceMap = jest.genMockFn().mockImpl(function() {
|
||||||
return 'test-source-map';
|
return 'test-source-map';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('source package', function() {
|
describe('source bundle', function() {
|
||||||
it('should create a package and get the source', function() {
|
it('should create a bundle and get the source', function() {
|
||||||
ppackage.addModule(new ModuleTransport({
|
bundle.addModule(new ModuleTransport({
|
||||||
code: 'transformed foo;',
|
code: 'transformed foo;',
|
||||||
sourceCode: 'source foo',
|
sourceCode: 'source foo',
|
||||||
sourcePath: 'foo path',
|
sourcePath: 'foo path',
|
||||||
}));
|
}));
|
||||||
ppackage.addModule(new ModuleTransport({
|
bundle.addModule(new ModuleTransport({
|
||||||
code: 'transformed bar;',
|
code: 'transformed bar;',
|
||||||
sourceCode: 'source bar',
|
sourceCode: 'source bar',
|
||||||
sourcePath: 'bar path',
|
sourcePath: 'bar path',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
ppackage.finalize({});
|
bundle.finalize({});
|
||||||
expect(ppackage.getSource()).toBe([
|
expect(bundle.getSource()).toBe([
|
||||||
'transformed foo;',
|
'transformed foo;',
|
||||||
'transformed bar;',
|
'transformed bar;',
|
||||||
'\/\/@ sourceMappingURL=test_url'
|
'\/\/@ sourceMappingURL=test_url'
|
||||||
|
@ -48,7 +48,7 @@ describe('Package', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be ok to leave out the source map url', function() {
|
it('should be ok to leave out the source map url', function() {
|
||||||
var p = new Package();
|
var p = new Bundle();
|
||||||
p.addModule(new ModuleTransport({
|
p.addModule(new ModuleTransport({
|
||||||
code: 'transformed foo;',
|
code: 'transformed foo;',
|
||||||
sourceCode: 'source foo',
|
sourceCode: 'source foo',
|
||||||
|
@ -67,22 +67,22 @@ describe('Package', function() {
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a package and add run module code', function() {
|
it('should create a bundle and add run module code', function() {
|
||||||
ppackage.addModule(new ModuleTransport({
|
bundle.addModule(new ModuleTransport({
|
||||||
code: 'transformed foo;',
|
code: 'transformed foo;',
|
||||||
sourceCode: 'source foo',
|
sourceCode: 'source foo',
|
||||||
sourcePath: 'foo path'
|
sourcePath: 'foo path'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
ppackage.addModule(new ModuleTransport({
|
bundle.addModule(new ModuleTransport({
|
||||||
code: 'transformed bar;',
|
code: 'transformed bar;',
|
||||||
sourceCode: 'source bar',
|
sourceCode: 'source bar',
|
||||||
sourcePath: 'bar path'
|
sourcePath: 'bar path'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
ppackage.setMainModuleId('foo');
|
bundle.setMainModuleId('foo');
|
||||||
ppackage.finalize({runMainModule: true});
|
bundle.finalize({runMainModule: true});
|
||||||
expect(ppackage.getSource()).toBe([
|
expect(bundle.getSource()).toBe([
|
||||||
'transformed foo;',
|
'transformed foo;',
|
||||||
'transformed bar;',
|
'transformed bar;',
|
||||||
';require("foo");',
|
';require("foo");',
|
||||||
|
@ -100,19 +100,19 @@ describe('Package', function() {
|
||||||
return minified;
|
return minified;
|
||||||
};
|
};
|
||||||
|
|
||||||
ppackage.addModule(new ModuleTransport({
|
bundle.addModule(new ModuleTransport({
|
||||||
code: 'transformed foo;',
|
code: 'transformed foo;',
|
||||||
sourceCode: 'source foo',
|
sourceCode: 'source foo',
|
||||||
sourcePath: 'foo path'
|
sourcePath: 'foo path'
|
||||||
}));
|
}));
|
||||||
ppackage.finalize();
|
bundle.finalize();
|
||||||
expect(ppackage.getMinifiedSourceAndMap()).toBe(minified);
|
expect(bundle.getMinifiedSourceAndMap()).toBe(minified);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('sourcemap package', function() {
|
describe('sourcemap bundle', function() {
|
||||||
it('should create sourcemap', function() {
|
it('should create sourcemap', function() {
|
||||||
var p = new Package('test_url');
|
var p = new Bundle('test_url');
|
||||||
p.addModule(new ModuleTransport({
|
p.addModule(new ModuleTransport({
|
||||||
code: [
|
code: [
|
||||||
'transformed foo',
|
'transformed foo',
|
||||||
|
@ -143,11 +143,11 @@ describe('Package', function() {
|
||||||
p.setMainModuleId('foo');
|
p.setMainModuleId('foo');
|
||||||
p.finalize({runMainModule: true});
|
p.finalize({runMainModule: true});
|
||||||
var s = p.getSourceMap();
|
var s = p.getSourceMap();
|
||||||
expect(s).toEqual(genSourceMap(p._modules));
|
expect(s).toEqual(genSourceMap(p.getModules()));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should combine sourcemaps', function() {
|
it('should combine sourcemaps', function() {
|
||||||
var p = new Package('test_url');
|
var p = new Bundle('test_url');
|
||||||
|
|
||||||
p.addModule(new ModuleTransport({
|
p.addModule(new ModuleTransport({
|
||||||
code: 'transformed foo;\n',
|
code: 'transformed foo;\n',
|
||||||
|
@ -215,7 +215,7 @@ describe('Package', function() {
|
||||||
|
|
||||||
describe('getAssets()', function() {
|
describe('getAssets()', function() {
|
||||||
it('should save and return asset objects', function() {
|
it('should save and return asset objects', function() {
|
||||||
var p = new Package('test_url');
|
var p = new Bundle('test_url');
|
||||||
var asset1 = {};
|
var asset1 = {};
|
||||||
var asset2 = {};
|
var asset2 = {};
|
||||||
p.addAsset(asset1);
|
p.addAsset(asset1);
|
||||||
|
@ -227,7 +227,7 @@ describe('Package', function() {
|
||||||
|
|
||||||
describe('getJSModulePaths()', function() {
|
describe('getJSModulePaths()', function() {
|
||||||
it('should return module paths', function() {
|
it('should return module paths', function() {
|
||||||
var p = new Package('test_url');
|
var p = new Bundle('test_url');
|
||||||
p.addModule(new ModuleTransport({
|
p.addModule(new ModuleTransport({
|
||||||
code: 'transformed foo;\n',
|
code: 'transformed foo;\n',
|
||||||
sourceCode: 'source foo',
|
sourceCode: 'source foo',
|
||||||
|
@ -248,7 +248,7 @@ describe('Package', function() {
|
||||||
|
|
||||||
function genSourceMap(modules) {
|
function genSourceMap(modules) {
|
||||||
var sourceMapGen = new SourceMapGenerator({file: 'bundle.js', version: 3});
|
var sourceMapGen = new SourceMapGenerator({file: 'bundle.js', version: 3});
|
||||||
var packageLineNo = 0;
|
var bundleLineNo = 0;
|
||||||
for (var i = 0; i < modules.length; i++) {
|
for (var i = 0; i < modules.length; i++) {
|
||||||
var module = modules[i];
|
var module = modules[i];
|
||||||
var transformedCode = module.code;
|
var transformedCode = module.code;
|
||||||
|
@ -259,7 +259,7 @@ describe('Package', function() {
|
||||||
for (var t = 0; t < transformedCode.length; t++) {
|
for (var t = 0; t < transformedCode.length; t++) {
|
||||||
if (t === 0 || lastCharNewLine) {
|
if (t === 0 || lastCharNewLine) {
|
||||||
sourceMapGen.addMapping({
|
sourceMapGen.addMapping({
|
||||||
generated: {line: packageLineNo + 1, column: 0},
|
generated: {line: bundleLineNo + 1, column: 0},
|
||||||
original: {line: transformedLineCount + 1, column: 0},
|
original: {line: transformedLineCount + 1, column: 0},
|
||||||
source: sourcePath
|
source: sourcePath
|
||||||
});
|
});
|
||||||
|
@ -267,10 +267,10 @@ describe('Package', function() {
|
||||||
lastCharNewLine = transformedCode[t] === '\n';
|
lastCharNewLine = transformedCode[t] === '\n';
|
||||||
if (lastCharNewLine) {
|
if (lastCharNewLine) {
|
||||||
transformedLineCount++;
|
transformedLineCount++;
|
||||||
packageLineNo++;
|
bundleLineNo++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packageLineNo++;
|
bundleLineNo++;
|
||||||
sourceMapGen.setSourceContent(
|
sourceMapGen.setSourceContent(
|
||||||
sourcePath,
|
sourcePath,
|
||||||
sourceCode
|
sourceCode
|
|
@ -9,7 +9,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
jest
|
jest
|
||||||
.setMock('worker-farm', function() { return function() {};})
|
.setMock('worker-farm', () => () => undefined)
|
||||||
.dontMock('underscore')
|
.dontMock('underscore')
|
||||||
.dontMock('../../lib/ModuleTransport')
|
.dontMock('../../lib/ModuleTransport')
|
||||||
.setMock('uglify-js')
|
.setMock('uglify-js')
|
||||||
|
@ -20,13 +20,14 @@ jest.mock('fs');
|
||||||
|
|
||||||
var Promise = require('promise');
|
var Promise = require('promise');
|
||||||
|
|
||||||
describe('Packager', function() {
|
describe('Bundler', function() {
|
||||||
var getDependencies;
|
var getDependencies;
|
||||||
var wrapModule;
|
var wrapModule;
|
||||||
var Packager;
|
var Bundler;
|
||||||
var packager;
|
var bundler;
|
||||||
var assetServer;
|
var assetServer;
|
||||||
var modules;
|
var modules;
|
||||||
|
var ProgressBar;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
getDependencies = jest.genMockFn();
|
getDependencies = jest.genMockFn();
|
||||||
|
@ -38,7 +39,7 @@ describe('Packager', function() {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
Packager = require('../');
|
Bundler = require('../');
|
||||||
|
|
||||||
require('fs').statSync.mockImpl(function() {
|
require('fs').statSync.mockImpl(function() {
|
||||||
return {
|
return {
|
||||||
|
@ -50,38 +51,61 @@ describe('Packager', function() {
|
||||||
callback(null, '{"json":true}');
|
callback(null, '{"json":true}');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ProgressBar = require('progress');
|
||||||
|
|
||||||
assetServer = {
|
assetServer = {
|
||||||
getAssetData: jest.genMockFn(),
|
getAssetData: jest.genMockFn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
packager = new Packager({
|
bundler = new Bundler({
|
||||||
projectRoots: ['/root'],
|
projectRoots: ['/root'],
|
||||||
assetServer: assetServer,
|
assetServer: assetServer,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function createModule({
|
||||||
|
path,
|
||||||
|
id,
|
||||||
|
dependencies,
|
||||||
|
isAsset,
|
||||||
|
isAsset_DEPRECATED,
|
||||||
|
isJSON,
|
||||||
|
resolution,
|
||||||
|
}) {
|
||||||
|
return {
|
||||||
|
path,
|
||||||
|
resolution,
|
||||||
|
getDependencies() { return Promise.resolve(dependencies); },
|
||||||
|
getName() { return Promise.resolve(id); },
|
||||||
|
isJSON() { return isJSON; },
|
||||||
|
isAsset() { return isAsset; },
|
||||||
|
isAsset_DEPRECATED() { return isAsset_DEPRECATED; },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
modules = [
|
modules = [
|
||||||
{id: 'foo', path: '/root/foo.js', dependencies: []},
|
createModule({id: 'foo', path: '/root/foo.js', dependencies: []}),
|
||||||
{id: 'bar', path: '/root/bar.js', dependencies: []},
|
createModule({id: 'bar', path: '/root/bar.js', dependencies: []}),
|
||||||
{
|
createModule({
|
||||||
id: 'image!img',
|
|
||||||
path: '/root/img/img.png',
|
path: '/root/img/img.png',
|
||||||
|
id: 'image!img',
|
||||||
isAsset_DEPRECATED: true,
|
isAsset_DEPRECATED: true,
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
resolution: 2,
|
resolution: 2,
|
||||||
},
|
}),
|
||||||
{
|
createModule({
|
||||||
id: 'new_image.png',
|
id: 'new_image.png',
|
||||||
path: '/root/img/new_image.png',
|
path: '/root/img/new_image.png',
|
||||||
isAsset: true,
|
isAsset: true,
|
||||||
resolution: 2,
|
resolution: 2,
|
||||||
dependencies: []
|
dependencies: []
|
||||||
},
|
}),
|
||||||
{
|
createModule({
|
||||||
id: 'package/file.json',
|
id: 'package/file.json',
|
||||||
path: '/root/file.json',
|
path: '/root/file.json',
|
||||||
isJSON: true,
|
isJSON: true,
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
},
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
getDependencies.mockImpl(function() {
|
getDependencies.mockImpl(function() {
|
||||||
|
@ -119,8 +143,8 @@ describe('Packager', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('create a package', function() {
|
pit('create a bundle', function() {
|
||||||
return packager.package('/root/foo.js', true, 'source_map_url')
|
return bundler.bundle('/root/foo.js', true, 'source_map_url')
|
||||||
.then(function(p) {
|
.then(function(p) {
|
||||||
expect(p.addModule.mock.calls[0][0]).toEqual({
|
expect(p.addModule.mock.calls[0][0]).toEqual({
|
||||||
code: 'lol transformed /root/foo.js lol',
|
code: 'lol transformed /root/foo.js lol',
|
||||||
|
@ -194,51 +218,24 @@ describe('Packager', function() {
|
||||||
{runMainModule: true}
|
{runMainModule: true}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(p.addAsset.mock.calls[0]).toEqual([
|
expect(p.addAsset.mock.calls).toContain([
|
||||||
imgModule_DEPRECATED
|
imgModule_DEPRECATED
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(p.addAsset.mock.calls[1]).toEqual([
|
expect(p.addAsset.mock.calls).toContain([
|
||||||
imgModule
|
imgModule
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// TODO(amasad) This fails with 0 != 5 in OSS
|
||||||
|
//expect(ProgressBar.prototype.tick.mock.calls.length).toEqual(modules.length);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('gets the list of dependencies', function() {
|
pit('gets the list of dependencies from the resolver', function() {
|
||||||
return packager.getDependencies('/root/foo.js', true)
|
return bundler.getDependencies('/root/foo.js', true)
|
||||||
.then(({dependencies}) => {
|
.then(
|
||||||
expect(dependencies).toEqual([
|
() => expect(getDependencies)
|
||||||
{
|
.toBeCalledWith('/root/foo.js', { dev: true })
|
||||||
dependencies: [],
|
);
|
||||||
id: 'foo',
|
|
||||||
path: '/root/foo.js',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dependencies: [],
|
|
||||||
id: 'bar',
|
|
||||||
path: '/root/bar.js',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dependencies: [],
|
|
||||||
id: 'image!img',
|
|
||||||
isAsset_DEPRECATED: true,
|
|
||||||
path: '/root/img/img.png',
|
|
||||||
resolution: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dependencies: [],
|
|
||||||
id: 'new_image.png',
|
|
||||||
isAsset: true,
|
|
||||||
path: '/root/img/new_image.png',
|
|
||||||
resolution: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dependencies: [],
|
|
||||||
id: 'package/file.json',
|
|
||||||
isJSON: true,
|
|
||||||
path: '/root/file.json',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -0,0 +1,309 @@
|
||||||
|
/**
|
||||||
|
* 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 assert = require('assert');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const Promise = require('promise');
|
||||||
|
const ProgressBar = require('progress');
|
||||||
|
const Cache = require('../Cache');
|
||||||
|
const Transformer = require('../JSTransformer');
|
||||||
|
const DependencyResolver = require('../DependencyResolver');
|
||||||
|
const Bundle = require('./Bundle');
|
||||||
|
const Activity = require('../Activity');
|
||||||
|
const ModuleTransport = require('../lib/ModuleTransport');
|
||||||
|
const declareOpts = require('../lib/declareOpts');
|
||||||
|
const imageSize = require('image-size');
|
||||||
|
|
||||||
|
const sizeOf = Promise.denodeify(imageSize);
|
||||||
|
const readFile = Promise.denodeify(fs.readFile);
|
||||||
|
|
||||||
|
const validateOpts = declareOpts({
|
||||||
|
projectRoots: {
|
||||||
|
type: 'array',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
blacklistRE: {
|
||||||
|
type: 'object', // typeof regex is object
|
||||||
|
},
|
||||||
|
moduleFormat: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'haste',
|
||||||
|
},
|
||||||
|
polyfillModuleNames: {
|
||||||
|
type: 'array',
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
|
cacheVersion: {
|
||||||
|
type: 'string',
|
||||||
|
default: '1.0',
|
||||||
|
},
|
||||||
|
resetCache: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
transformModulePath: {
|
||||||
|
type:'string',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
nonPersistent: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
assetRoots: {
|
||||||
|
type: 'array',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
assetExts: {
|
||||||
|
type: 'array',
|
||||||
|
default: ['png'],
|
||||||
|
},
|
||||||
|
fileWatcher: {
|
||||||
|
type: 'object',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
assetServer: {
|
||||||
|
type: 'object',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
transformTimeoutInterval: {
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
class Bundler {
|
||||||
|
|
||||||
|
constructor(options) {
|
||||||
|
const opts = this._opts = validateOpts(options);
|
||||||
|
|
||||||
|
opts.projectRoots.forEach(verifyRootExists);
|
||||||
|
|
||||||
|
this._cache = new Cache({
|
||||||
|
resetCache: opts.resetCache,
|
||||||
|
cacheVersion: opts.cacheVersion,
|
||||||
|
projectRoots: opts.projectRoots,
|
||||||
|
transformModulePath: opts.transformModulePath,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._resolver = new DependencyResolver({
|
||||||
|
projectRoots: opts.projectRoots,
|
||||||
|
blacklistRE: opts.blacklistRE,
|
||||||
|
polyfillModuleNames: opts.polyfillModuleNames,
|
||||||
|
moduleFormat: opts.moduleFormat,
|
||||||
|
assetRoots: opts.assetRoots,
|
||||||
|
fileWatcher: opts.fileWatcher,
|
||||||
|
assetExts: opts.assetExts,
|
||||||
|
cache: this._cache,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._transformer = new Transformer({
|
||||||
|
projectRoots: opts.projectRoots,
|
||||||
|
blacklistRE: opts.blacklistRE,
|
||||||
|
cache: this._cache,
|
||||||
|
transformModulePath: opts.transformModulePath,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._projectRoots = opts.projectRoots;
|
||||||
|
this._assetServer = opts.assetServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
kill() {
|
||||||
|
this._transformer.kill();
|
||||||
|
return this._cache.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle(main, runModule, sourceMapUrl, isDev, platform) {
|
||||||
|
const bundle = new Bundle(sourceMapUrl);
|
||||||
|
const findEventId = Activity.startEvent('find dependencies');
|
||||||
|
let transformEventId;
|
||||||
|
|
||||||
|
return this.getDependencies(main, isDev, platform).then((result) => {
|
||||||
|
Activity.endEvent(findEventId);
|
||||||
|
transformEventId = Activity.startEvent('transform');
|
||||||
|
|
||||||
|
let bar;
|
||||||
|
if (process.stdout.isTTY) {
|
||||||
|
bar = new ProgressBar('transforming [:bar] :percent :current/:total', {
|
||||||
|
complete: '=',
|
||||||
|
incomplete: ' ',
|
||||||
|
width: 40,
|
||||||
|
total: result.dependencies.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle.setMainModuleId(result.mainModuleId);
|
||||||
|
return Promise.all(
|
||||||
|
result.dependencies.map(
|
||||||
|
module => this._transformModule(bundle, module).then(transformed => {
|
||||||
|
if (bar) {
|
||||||
|
bar.tick();
|
||||||
|
}
|
||||||
|
return transformed;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}).then((transformedModules) => {
|
||||||
|
Activity.endEvent(transformEventId);
|
||||||
|
|
||||||
|
transformedModules.forEach(function(moduleTransport) {
|
||||||
|
bundle.addModule(moduleTransport);
|
||||||
|
});
|
||||||
|
|
||||||
|
bundle.finalize({ runMainModule: runModule });
|
||||||
|
return bundle;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidateFile(filePath) {
|
||||||
|
this._transformer.invalidateFile(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependencies(main, isDev, platform) {
|
||||||
|
return this._resolver.getDependencies(main, { dev: isDev, platform });
|
||||||
|
}
|
||||||
|
|
||||||
|
_transformModule(bundle, module) {
|
||||||
|
let transform;
|
||||||
|
|
||||||
|
if (module.isAsset_DEPRECATED()) {
|
||||||
|
transform = this.generateAssetModule_DEPRECATED(bundle, module);
|
||||||
|
} else if (module.isAsset()) {
|
||||||
|
transform = this.generateAssetModule(bundle, module);
|
||||||
|
} else if (module.isJSON()) {
|
||||||
|
transform = generateJSONModule(module);
|
||||||
|
} else {
|
||||||
|
transform = this._transformer.loadFileAndTransform(
|
||||||
|
path.resolve(module.path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolver = this._resolver;
|
||||||
|
return transform.then(
|
||||||
|
transformed => resolver.wrapModule(module, transformed.code).then(
|
||||||
|
code => new ModuleTransport({
|
||||||
|
code: code,
|
||||||
|
map: transformed.map,
|
||||||
|
sourceCode: transformed.sourceCode,
|
||||||
|
sourcePath: transformed.sourcePath,
|
||||||
|
virtual: transformed.virtual,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getGraphDebugInfo() {
|
||||||
|
return this._resolver.getDebugInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
generateAssetModule_DEPRECATED(bundle, module) {
|
||||||
|
return Promise.all([
|
||||||
|
sizeOf(module.path),
|
||||||
|
module.getName(),
|
||||||
|
]).then(([dimensions, id]) => {
|
||||||
|
const img = {
|
||||||
|
__packager_asset: true,
|
||||||
|
isStatic: true,
|
||||||
|
path: module.path,
|
||||||
|
uri: id.replace(/^[^!]+!/, ''),
|
||||||
|
width: dimensions.width / module.resolution,
|
||||||
|
height: dimensions.height / module.resolution,
|
||||||
|
deprecated: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
bundle.addAsset(img);
|
||||||
|
|
||||||
|
const code = 'module.exports = ' + JSON.stringify(img) + ';';
|
||||||
|
|
||||||
|
return new ModuleTransport({
|
||||||
|
code: code,
|
||||||
|
sourceCode: code,
|
||||||
|
sourcePath: module.path,
|
||||||
|
virtual: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
generateAssetModule(bundle, module) {
|
||||||
|
const relPath = getPathRelativeToRoot(this._projectRoots, module.path);
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
sizeOf(module.path),
|
||||||
|
this._assetServer.getAssetData(relPath),
|
||||||
|
]).then(function(res) {
|
||||||
|
const dimensions = res[0];
|
||||||
|
const assetData = res[1];
|
||||||
|
const img = {
|
||||||
|
__packager_asset: true,
|
||||||
|
fileSystemLocation: path.dirname(module.path),
|
||||||
|
httpServerLocation: path.join('/assets', path.dirname(relPath)),
|
||||||
|
width: dimensions.width / module.resolution,
|
||||||
|
height: dimensions.height / module.resolution,
|
||||||
|
scales: assetData.scales,
|
||||||
|
hash: assetData.hash,
|
||||||
|
name: assetData.name,
|
||||||
|
type: assetData.type,
|
||||||
|
};
|
||||||
|
|
||||||
|
bundle.addAsset(img);
|
||||||
|
|
||||||
|
const ASSET_TEMPLATE = 'module.exports = require("AssetRegistry").registerAsset(%json);';
|
||||||
|
const code = ASSET_TEMPLATE.replace('%json', JSON.stringify(img));
|
||||||
|
|
||||||
|
return new ModuleTransport({
|
||||||
|
code: code,
|
||||||
|
sourceCode: code,
|
||||||
|
sourcePath: module.path,
|
||||||
|
virtual: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateJSONModule(module) {
|
||||||
|
return readFile(module.path).then(function(data) {
|
||||||
|
const code = 'module.exports = ' + data.toString('utf8') + ';';
|
||||||
|
|
||||||
|
return new ModuleTransport({
|
||||||
|
code: code,
|
||||||
|
sourceCode: code,
|
||||||
|
sourcePath: module.path,
|
||||||
|
virtual: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPathRelativeToRoot(roots, absPath) {
|
||||||
|
for (let i = 0; i < roots.length; i++) {
|
||||||
|
const relPath = path.relative(roots[i], absPath);
|
||||||
|
if (relPath[0] !== '.') {
|
||||||
|
return relPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
'Expected root module to be relative to one of the project roots'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyRootExists(root) {
|
||||||
|
// Verify that the root exists.
|
||||||
|
assert(fs.statSync(root).isDirectory(), 'Root has to be a valid directory');
|
||||||
|
}
|
||||||
|
|
||||||
|
class DummyCache {
|
||||||
|
get(filepath, field, loaderCb) {
|
||||||
|
return loaderCb();
|
||||||
|
}
|
||||||
|
|
||||||
|
end(){}
|
||||||
|
invalidate(filepath){}
|
||||||
|
}
|
||||||
|
module.exports = Bundler;
|
|
@ -0,0 +1,256 @@
|
||||||
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
jest
|
||||||
|
.dontMock('../index');
|
||||||
|
|
||||||
|
const Promise = require('promise');
|
||||||
|
|
||||||
|
describe('BundlesLayout', () => {
|
||||||
|
var BundlesLayout;
|
||||||
|
var DependencyResolver;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
BundlesLayout = require('../index');
|
||||||
|
DependencyResolver = require('../../DependencyResolver');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generate', () => {
|
||||||
|
function newBundlesLayout() {
|
||||||
|
return new BundlesLayout({
|
||||||
|
dependencyResolver: new DependencyResolver(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPolyfill() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dep(path) {
|
||||||
|
return {
|
||||||
|
path: path,
|
||||||
|
isPolyfill: isPolyfill,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pit('should bundle sync dependencies', () => {
|
||||||
|
DependencyResolver.prototype.getDependencies.mockImpl((path) => {
|
||||||
|
switch (path) {
|
||||||
|
case '/root/index.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/index.js'), dep('/root/a.js')],
|
||||||
|
asyncDependencies: [],
|
||||||
|
});
|
||||||
|
case '/root/a.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/a.js')],
|
||||||
|
asyncDependencies: [],
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
throw 'Undefined path: ' + path;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
expect(bundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js', '/root/a.js'],
|
||||||
|
children: [],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should separate async dependencies into different bundle', () => {
|
||||||
|
DependencyResolver.prototype.getDependencies.mockImpl((path) => {
|
||||||
|
switch (path) {
|
||||||
|
case '/root/index.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/index.js')],
|
||||||
|
asyncDependencies: [['/root/a.js']],
|
||||||
|
});
|
||||||
|
case '/root/a.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/a.js')],
|
||||||
|
asyncDependencies: [],
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
throw 'Undefined path: ' + path;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
expect(bundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [{
|
||||||
|
id:'bundle.0.1',
|
||||||
|
modules: ['/root/a.js'],
|
||||||
|
children: [],
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('separate async dependencies of async dependencies', () => {
|
||||||
|
DependencyResolver.prototype.getDependencies.mockImpl((path) => {
|
||||||
|
switch (path) {
|
||||||
|
case '/root/index.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/index.js')],
|
||||||
|
asyncDependencies: [['/root/a.js']],
|
||||||
|
});
|
||||||
|
case '/root/a.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/a.js')],
|
||||||
|
asyncDependencies: [['/root/b.js']],
|
||||||
|
});
|
||||||
|
case '/root/b.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/b.js')],
|
||||||
|
asyncDependencies: [],
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
throw 'Undefined path: ' + path;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
expect(bundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/a.js'],
|
||||||
|
children: [{
|
||||||
|
id: 'bundle.0.1.2',
|
||||||
|
modules: ['/root/b.js'],
|
||||||
|
children: [],
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('separate bundle sync dependencies of async ones on same bundle', () => {
|
||||||
|
DependencyResolver.prototype.getDependencies.mockImpl((path) => {
|
||||||
|
switch (path) {
|
||||||
|
case '/root/index.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/index.js')],
|
||||||
|
asyncDependencies: [['/root/a.js']],
|
||||||
|
});
|
||||||
|
case '/root/a.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/a.js'), dep('/root/b.js')],
|
||||||
|
asyncDependencies: [],
|
||||||
|
});
|
||||||
|
case '/root/b.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/b.js')],
|
||||||
|
asyncDependencies: [],
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
throw 'Undefined path: ' + path;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
expect(bundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/a.js', '/root/b.js'],
|
||||||
|
children: [],
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('separate cache in which bundle is each dependency', () => {
|
||||||
|
DependencyResolver.prototype.getDependencies.mockImpl((path) => {
|
||||||
|
switch (path) {
|
||||||
|
case '/root/index.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/index.js'), dep('/root/a.js')],
|
||||||
|
asyncDependencies: [],
|
||||||
|
});
|
||||||
|
case '/root/a.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/a.js')],
|
||||||
|
asyncDependencies: [['/root/b.js']],
|
||||||
|
});
|
||||||
|
case '/root/b.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/b.js')],
|
||||||
|
asyncDependencies: [],
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
throw 'Undefined path: ' + path;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(
|
||||||
|
bundles => expect(bundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js', '/root/a.js'],
|
||||||
|
children: [{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/b.js'],
|
||||||
|
children: [],
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('separate cache in which bundle is each dependency', () => {
|
||||||
|
DependencyResolver.prototype.getDependencies.mockImpl((path) => {
|
||||||
|
switch (path) {
|
||||||
|
case '/root/index.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/index.js'), dep('/root/a.js')],
|
||||||
|
asyncDependencies: [['/root/b.js'], ['/root/c.js']],
|
||||||
|
});
|
||||||
|
case '/root/a.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/a.js')],
|
||||||
|
asyncDependencies: [],
|
||||||
|
});
|
||||||
|
case '/root/b.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/b.js')],
|
||||||
|
asyncDependencies: [['/root/d.js']],
|
||||||
|
});
|
||||||
|
case '/root/c.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/c.js')],
|
||||||
|
asyncDependencies: [],
|
||||||
|
});
|
||||||
|
case '/root/d.js':
|
||||||
|
return Promise.resolve({
|
||||||
|
dependencies: [dep('/root/d.js')],
|
||||||
|
asyncDependencies: [],
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
throw 'Undefined path: ' + path;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var layout = newBundlesLayout();
|
||||||
|
return layout.generateLayout(['/root/index.js']).then(() => {
|
||||||
|
expect(layout.getBundleIDForModule('/root/index.js')).toBe('bundle.0');
|
||||||
|
expect(layout.getBundleIDForModule('/root/a.js')).toBe('bundle.0');
|
||||||
|
expect(layout.getBundleIDForModule('/root/b.js')).toBe('bundle.0.1');
|
||||||
|
expect(layout.getBundleIDForModule('/root/c.js')).toBe('bundle.0.2');
|
||||||
|
expect(layout.getBundleIDForModule('/root/d.js')).toBe('bundle.0.1.3');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
612
react-packager/src/BundlesLayout/__tests__/BundlesLayoutIntegration-test.js
vendored
Normal file
612
react-packager/src/BundlesLayout/__tests__/BundlesLayoutIntegration-test.js
vendored
Normal file
|
@ -0,0 +1,612 @@
|
||||||
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
jest
|
||||||
|
.dontMock('absolute-path')
|
||||||
|
.dontMock('crypto')
|
||||||
|
.dontMock('underscore')
|
||||||
|
.dontMock('path')
|
||||||
|
.dontMock('../index')
|
||||||
|
.dontMock('../../lib/getAssetDataFromName')
|
||||||
|
.dontMock('../../DependencyResolver/crawlers')
|
||||||
|
.dontMock('../../DependencyResolver/crawlers/node')
|
||||||
|
.dontMock('../../DependencyResolver/DependencyGraph/docblock')
|
||||||
|
.dontMock('../../DependencyResolver/fastfs')
|
||||||
|
.dontMock('../../DependencyResolver/replacePatterns')
|
||||||
|
.dontMock('../../DependencyResolver')
|
||||||
|
.dontMock('../../DependencyResolver/DependencyGraph')
|
||||||
|
.dontMock('../../DependencyResolver/AssetModule_DEPRECATED')
|
||||||
|
.dontMock('../../DependencyResolver/AssetModule')
|
||||||
|
.dontMock('../../DependencyResolver/Module')
|
||||||
|
.dontMock('../../DependencyResolver/Package')
|
||||||
|
.dontMock('../../DependencyResolver/Polyfill')
|
||||||
|
.dontMock('../../DependencyResolver/ModuleCache');
|
||||||
|
|
||||||
|
const Promise = require('promise');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
jest.mock('fs');
|
||||||
|
|
||||||
|
describe('BundlesLayout', () => {
|
||||||
|
var BundlesLayout;
|
||||||
|
var Cache;
|
||||||
|
var DependencyResolver;
|
||||||
|
var fileWatcher;
|
||||||
|
var fs;
|
||||||
|
|
||||||
|
const polyfills = [
|
||||||
|
'polyfills/prelude_dev.js',
|
||||||
|
'polyfills/prelude.js',
|
||||||
|
'polyfills/require.js',
|
||||||
|
'polyfills/polyfills.js',
|
||||||
|
'polyfills/console.js',
|
||||||
|
'polyfills/error-guard.js',
|
||||||
|
'polyfills/String.prototype.es6.js',
|
||||||
|
'polyfills/Array.prototype.es6.js',
|
||||||
|
];
|
||||||
|
const baseFs = getBaseFs();
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fs = require('fs');
|
||||||
|
BundlesLayout = require('../index');
|
||||||
|
Cache = require('../../Cache');
|
||||||
|
DependencyResolver = require('../../DependencyResolver');
|
||||||
|
|
||||||
|
fileWatcher = {
|
||||||
|
on: () => this,
|
||||||
|
isWatchman: () => Promise.resolve(false)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generate', () => {
|
||||||
|
function newBundlesLayout() {
|
||||||
|
const resolver = new DependencyResolver({
|
||||||
|
projectRoots: ['/root', '/' + __dirname.split('/')[1]],
|
||||||
|
fileWatcher: fileWatcher,
|
||||||
|
cache: new Cache(),
|
||||||
|
assetExts: ['js', 'png'],
|
||||||
|
assetRoots: ['/root'],
|
||||||
|
});
|
||||||
|
|
||||||
|
return new BundlesLayout({dependencyResolver: resolver});
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripPolyfills(bundle) {
|
||||||
|
return Promise
|
||||||
|
.all(bundle.children.map(childModule => stripPolyfills(childModule)))
|
||||||
|
.then(children => {
|
||||||
|
const modules = bundle.modules
|
||||||
|
.filter(moduleName => { // filter polyfills
|
||||||
|
for (let p of polyfills) {
|
||||||
|
if (moduleName.indexOf(p) !== -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: bundle.id,
|
||||||
|
modules: modules,
|
||||||
|
children: children,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMockFilesystem(mockFs) {
|
||||||
|
fs.__setMockFilesystem(Object.assign(mockFs, baseFs));
|
||||||
|
}
|
||||||
|
|
||||||
|
pit('should bundle single-module app', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should bundle dependant modules', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/
|
||||||
|
require("a");`,
|
||||||
|
'a.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule a
|
||||||
|
*/`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js', '/root/a.js'],
|
||||||
|
children: [],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should split bundles for async dependencies', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/
|
||||||
|
System.import("a");`,
|
||||||
|
'a.js': `
|
||||||
|
/**,
|
||||||
|
* @providesModule a
|
||||||
|
*/`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/a.js'],
|
||||||
|
children: [],
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should split into multiple bundles separate async dependencies', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/
|
||||||
|
System.import("a");
|
||||||
|
System.import("b");`,
|
||||||
|
'a.js': `
|
||||||
|
/**,
|
||||||
|
* @providesModule a
|
||||||
|
*/`,
|
||||||
|
'b.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule b
|
||||||
|
*/`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/a.js'],
|
||||||
|
children: [],
|
||||||
|
}, {
|
||||||
|
id: 'bundle.0.2',
|
||||||
|
modules: ['/root/b.js'],
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should fully traverse sync dependencies', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/
|
||||||
|
require("a");
|
||||||
|
System.import("b");`,
|
||||||
|
'a.js': `
|
||||||
|
/**,
|
||||||
|
* @providesModule a
|
||||||
|
*/`,
|
||||||
|
'b.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule b
|
||||||
|
*/`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js', '/root/a.js'],
|
||||||
|
children: [{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/b.js'],
|
||||||
|
children: [],
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should include sync dependencies async dependencies might have', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/
|
||||||
|
System.import("a");`,
|
||||||
|
'a.js': `
|
||||||
|
/**,
|
||||||
|
* @providesModule a
|
||||||
|
*/,
|
||||||
|
require("b");`,
|
||||||
|
'b.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule b
|
||||||
|
*/
|
||||||
|
require("c");`,
|
||||||
|
'c.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule c
|
||||||
|
*/`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/a.js', '/root/b.js', '/root/c.js'],
|
||||||
|
children: [],
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should allow duplicated dependencies across bundles', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/
|
||||||
|
System.import("a");
|
||||||
|
System.import("b");`,
|
||||||
|
'a.js': `
|
||||||
|
/**,
|
||||||
|
* @providesModule a
|
||||||
|
*/,
|
||||||
|
require("c");`,
|
||||||
|
'b.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule b
|
||||||
|
*/
|
||||||
|
require("c");`,
|
||||||
|
'c.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule c
|
||||||
|
*/`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/a.js', '/root/c.js'],
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'bundle.0.2',
|
||||||
|
modules: ['/root/b.js', '/root/c.js'],
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should put in separate bundles async dependencies of async dependencies', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/
|
||||||
|
System.import("a");`,
|
||||||
|
'a.js': `
|
||||||
|
/**,
|
||||||
|
* @providesModule a
|
||||||
|
*/,
|
||||||
|
System.import("b");`,
|
||||||
|
'b.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule b
|
||||||
|
*/
|
||||||
|
require("c");`,
|
||||||
|
'c.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule c
|
||||||
|
*/`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/a.js'],
|
||||||
|
children: [{
|
||||||
|
id: 'bundle.0.1.2',
|
||||||
|
modules: ['/root/b.js', '/root/c.js'],
|
||||||
|
children: [],
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should put image dependencies into separate bundles', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/
|
||||||
|
System.import("a");`,
|
||||||
|
'a.js':`
|
||||||
|
/**,
|
||||||
|
* @providesModule a
|
||||||
|
*/,
|
||||||
|
require("./img.png");`,
|
||||||
|
'img.png': '',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/a.js', '/root/img.png'],
|
||||||
|
children: [],
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should put image dependencies across bundles', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/
|
||||||
|
System.import("a");
|
||||||
|
System.import("b");`,
|
||||||
|
'a.js':`
|
||||||
|
/**,
|
||||||
|
* @providesModule a
|
||||||
|
*/,
|
||||||
|
require("./img.png");`,
|
||||||
|
'b.js':`
|
||||||
|
/**,
|
||||||
|
* @providesModule b
|
||||||
|
*/,
|
||||||
|
require("./img.png");`,
|
||||||
|
'img.png': '',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/a.js', '/root/img.png'],
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'bundle.0.2',
|
||||||
|
modules: ['/root/b.js', '/root/img.png'],
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('could async require asset', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/
|
||||||
|
System.import("./img.png");`,
|
||||||
|
'img.png': '',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/img.png'],
|
||||||
|
children: [],
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should include deprecated assets into separate bundles', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/
|
||||||
|
System.import("a");`,
|
||||||
|
'a.js':`
|
||||||
|
/**,
|
||||||
|
* @providesModule a
|
||||||
|
*/,
|
||||||
|
require("image!img");`,
|
||||||
|
'img.png': '',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/a.js', '/root/img.png'],
|
||||||
|
children: [],
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('could async require deprecated asset', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/
|
||||||
|
System.import("image!img");`,
|
||||||
|
'img.png': '',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/img.png'],
|
||||||
|
children: [],
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should put packages into bundles', () => {
|
||||||
|
setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': `
|
||||||
|
/**
|
||||||
|
* @providesModule index
|
||||||
|
*/
|
||||||
|
System.import("aPackage");`,
|
||||||
|
'aPackage': {
|
||||||
|
'package.json': JSON.stringify({
|
||||||
|
name: 'aPackage',
|
||||||
|
main: './main.js',
|
||||||
|
browser: {
|
||||||
|
'./main.js': './client.js',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
'main.js': 'some other code',
|
||||||
|
'client.js': 'some code',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
||||||
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
|
expect(resolvedBundles).toEqual({
|
||||||
|
id: 'bundle.0',
|
||||||
|
modules: ['/root/index.js'],
|
||||||
|
children: [{
|
||||||
|
id: 'bundle.0.1',
|
||||||
|
modules: ['/root/aPackage/client.js'],
|
||||||
|
children: [],
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function getBaseFs() {
|
||||||
|
const p = path.join(__dirname, '../../../DependencyResolver/polyfills').substring(1);
|
||||||
|
const root = {};
|
||||||
|
let currentPath = root;
|
||||||
|
|
||||||
|
p.split('/').forEach(part => {
|
||||||
|
const child = {};
|
||||||
|
currentPath[part] = child;
|
||||||
|
currentPath = child;
|
||||||
|
});
|
||||||
|
|
||||||
|
polyfills.forEach(polyfill =>
|
||||||
|
currentPath[polyfill.split('/')[1]] = ''
|
||||||
|
);
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,117 @@
|
||||||
|
/**
|
||||||
|
* 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 _ = require('underscore');
|
||||||
|
const declareOpts = require('../lib/declareOpts');
|
||||||
|
|
||||||
|
const validateOpts = declareOpts({
|
||||||
|
dependencyResolver: {
|
||||||
|
type: 'object',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const BUNDLE_PREFIX = 'bundle';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that takes care of separating the graph of dependencies into
|
||||||
|
* separate bundles
|
||||||
|
*/
|
||||||
|
class BundlesLayout {
|
||||||
|
constructor(options) {
|
||||||
|
const opts = validateOpts(options);
|
||||||
|
this._resolver = opts.dependencyResolver;
|
||||||
|
|
||||||
|
this._moduleToBundle = Object.create(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
generateLayout(entryPaths, isDev) {
|
||||||
|
var currentBundleID = 0;
|
||||||
|
const rootBundle = {
|
||||||
|
id: BUNDLE_PREFIX + '.' + currentBundleID++,
|
||||||
|
modules: [],
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
var pending = [{paths: entryPaths, bundle: rootBundle}];
|
||||||
|
|
||||||
|
return promiseWhile(
|
||||||
|
() => pending.length > 0,
|
||||||
|
() => rootBundle,
|
||||||
|
() => {
|
||||||
|
const {paths, bundle} = pending.shift();
|
||||||
|
|
||||||
|
// pending sync dependencies we still need to explore for the current
|
||||||
|
// pending dependency
|
||||||
|
const pendingSyncDeps = paths;
|
||||||
|
|
||||||
|
// accum variable for sync dependencies of the current pending
|
||||||
|
// dependency we're processing
|
||||||
|
const syncDependencies = Object.create(null);
|
||||||
|
|
||||||
|
return promiseWhile(
|
||||||
|
() => pendingSyncDeps.length > 0,
|
||||||
|
() => {
|
||||||
|
const dependencies = Object.keys(syncDependencies);
|
||||||
|
if (dependencies.length > 0) {
|
||||||
|
bundle.modules = dependencies;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
index => {
|
||||||
|
const pendingSyncDep = pendingSyncDeps.shift();
|
||||||
|
return this._resolver
|
||||||
|
.getDependencies(pendingSyncDep, {dev: isDev})
|
||||||
|
.then(deps => {
|
||||||
|
deps.dependencies.forEach(dep => {
|
||||||
|
if (dep.path !== pendingSyncDep && !dep.isPolyfill()) {
|
||||||
|
pendingSyncDeps.push(dep.path);
|
||||||
|
}
|
||||||
|
syncDependencies[dep.path] = true;
|
||||||
|
this._moduleToBundle[dep.path] = bundle.id;
|
||||||
|
});
|
||||||
|
deps.asyncDependencies.forEach(asyncDeps => {
|
||||||
|
const childBundle = {
|
||||||
|
id: bundle.id + '.' + currentBundleID++,
|
||||||
|
modules: [],
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
bundle.children.push(childBundle);
|
||||||
|
pending.push({paths: asyncDeps, bundle: childBundle});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBundleIDForModule(path) {
|
||||||
|
return this._moduleToBundle[path];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runs the body Promise meanwhile the condition callback is satisfied.
|
||||||
|
// Once it's not satisfied anymore, it returns what the results callback
|
||||||
|
// indicates
|
||||||
|
function promiseWhile(condition, result, body) {
|
||||||
|
return _promiseWhile(condition, result, body, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _promiseWhile(condition, result, body, index) {
|
||||||
|
if (!condition()) {
|
||||||
|
return Promise.resolve(result());
|
||||||
|
}
|
||||||
|
|
||||||
|
return body(index).then(() =>
|
||||||
|
_promiseWhile(condition, result, body, index + 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BundlesLayout;
|
|
@ -8,13 +8,26 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
class Cache {
|
const mockColor = () => {
|
||||||
get(filepath, field, cb) {
|
return {
|
||||||
return cb(filepath);
|
bold: () => { return { }; },
|
||||||
}
|
};
|
||||||
|
};
|
||||||
|
|
||||||
invalidate(filepath) { }
|
mockColor.bold = function() {
|
||||||
end() { }
|
return {};
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = Cache;
|
module.exports = {
|
||||||
|
dim: s => s,
|
||||||
|
magenta: mockColor,
|
||||||
|
white: mockColor,
|
||||||
|
blue: mockColor,
|
||||||
|
yellow: mockColor,
|
||||||
|
green: mockColor,
|
||||||
|
bold: mockColor,
|
||||||
|
red: mockColor,
|
||||||
|
cyan: mockColor,
|
||||||
|
gray: mockColor,
|
||||||
|
black: mockColor,
|
||||||
|
};
|
||||||
|
|
|
@ -215,6 +215,7 @@ class Cache {
|
||||||
hash.update(options.transformModulePath);
|
hash.update(options.transformModulePath);
|
||||||
|
|
||||||
var name = 'react-packager-cache-' + hash.digest('hex');
|
var name = 'react-packager-cache-' + hash.digest('hex');
|
||||||
|
|
||||||
return path.join(tmpdir, name);
|
return path.join(tmpdir, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,13 @@ const Promise = require('promise');
|
||||||
const getAssetDataFromName = require('../lib/getAssetDataFromName');
|
const getAssetDataFromName = require('../lib/getAssetDataFromName');
|
||||||
|
|
||||||
class AssetModule extends Module {
|
class AssetModule extends Module {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
const { resolution, name, type } = getAssetDataFromName(this.path);
|
||||||
|
this.resolution = resolution;
|
||||||
|
this._name = name;
|
||||||
|
this._type = type;
|
||||||
|
}
|
||||||
|
|
||||||
isHaste() {
|
isHaste() {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
|
@ -14,33 +21,31 @@ class AssetModule extends Module {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAsyncDependencies() {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
_read() {
|
_read() {
|
||||||
return Promise.resolve({});
|
return Promise.resolve({});
|
||||||
}
|
}
|
||||||
|
|
||||||
getName() {
|
getName() {
|
||||||
return super.getName().then(id => {
|
return super.getName().then(
|
||||||
const {name, type} = getAssetDataFromName(this.path);
|
id => id.replace(/\/[^\/]+$/, `/${this._name}.${this._type}`)
|
||||||
return id.replace(/\/[^\/]+$/, `/${name}.${type}`);
|
);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getPlainObject() {
|
|
||||||
return this.getName().then(name => this.addReference({
|
|
||||||
path: this.path,
|
|
||||||
isJSON: false,
|
|
||||||
isAsset: true,
|
|
||||||
isAsset_DEPRECATED: false,
|
|
||||||
isPolyfill: false,
|
|
||||||
resolution: getAssetDataFromName(this.path).resolution,
|
|
||||||
id: name,
|
|
||||||
dependencies: [],
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hash() {
|
hash() {
|
||||||
return `AssetModule : ${this.path}`;
|
return `AssetModule : ${this.path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isJSON() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isAsset() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = AssetModule;
|
module.exports = AssetModule;
|
||||||
|
|
|
@ -5,36 +5,45 @@ const Promise = require('promise');
|
||||||
const getAssetDataFromName = require('../lib/getAssetDataFromName');
|
const getAssetDataFromName = require('../lib/getAssetDataFromName');
|
||||||
|
|
||||||
class AssetModule_DEPRECATED extends Module {
|
class AssetModule_DEPRECATED extends Module {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
const {resolution, name} = getAssetDataFromName(this.path);
|
||||||
|
this.resolution = resolution;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
isHaste() {
|
isHaste() {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
getName() {
|
getName() {
|
||||||
return Promise.resolve(this.name);
|
return Promise.resolve(`image!${this.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDependencies() {
|
getDependencies() {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlainObject() {
|
getAsyncDependencies() {
|
||||||
const {name, resolution} = getAssetDataFromName(this.path);
|
return Promise.resolve([]);
|
||||||
|
|
||||||
return Promise.resolve(this.addReference({
|
|
||||||
path: this.path,
|
|
||||||
id: `image!${name}`,
|
|
||||||
resolution,
|
|
||||||
isAsset_DEPRECATED: true,
|
|
||||||
dependencies: [],
|
|
||||||
isJSON: false,
|
|
||||||
isPolyfill: false,
|
|
||||||
isAsset: false,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hash() {
|
hash() {
|
||||||
return `AssetModule_DEPRECATED : ${this.path}`;
|
return `AssetModule_DEPRECATED : ${this.path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isJSON() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isAsset_DEPRECATED() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolution() {
|
||||||
|
return getAssetDataFromName(this.path).resolution;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = AssetModule_DEPRECATED;
|
module.exports = AssetModule_DEPRECATED;
|
||||||
|
|
|
@ -36,6 +36,24 @@ describe('DependencyGraph', function() {
|
||||||
var fileWatcher;
|
var fileWatcher;
|
||||||
var fs;
|
var fs;
|
||||||
|
|
||||||
|
function getOrderedDependenciesAsJSON(dgraph, entry) {
|
||||||
|
return dgraph.getOrderedDependencies(entry).then(
|
||||||
|
deps => Promise.all(deps.map(dep => Promise.all([
|
||||||
|
dep.getName(),
|
||||||
|
dep.getDependencies(),
|
||||||
|
]).then(([name, dependencies]) => ({
|
||||||
|
path: dep.path,
|
||||||
|
isJSON: dep.isJSON(),
|
||||||
|
isAsset: dep.isAsset(),
|
||||||
|
isAsset_DEPRECATED: dep.isAsset_DEPRECATED(),
|
||||||
|
isPolyfill: dep.isPolyfill(),
|
||||||
|
resolution: dep.resolution,
|
||||||
|
id: name,
|
||||||
|
dependencies
|
||||||
|
})))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
fs = require('fs');
|
fs = require('fs');
|
||||||
Cache = require('../../../Cache');
|
Cache = require('../../../Cache');
|
||||||
|
@ -76,7 +94,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -134,7 +152,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -186,7 +204,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -246,7 +264,7 @@ describe('DependencyGraph', function() {
|
||||||
assetRoots_DEPRECATED: ['/root/imgs'],
|
assetRoots_DEPRECATED: ['/root/imgs'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -298,7 +316,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -355,7 +373,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -433,7 +451,7 @@ describe('DependencyGraph', function() {
|
||||||
assetRoots_DEPRECATED: ['/root/imgs'],
|
assetRoots_DEPRECATED: ['/root/imgs'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -495,7 +513,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -548,7 +566,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -601,7 +619,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -662,7 +680,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -719,7 +737,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -770,7 +788,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -820,7 +838,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -867,7 +885,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -918,7 +936,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -967,7 +985,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1021,7 +1039,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/somedir/somefile.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/somedir/somefile.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1079,7 +1097,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1127,7 +1145,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1174,7 +1192,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1233,7 +1251,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1292,7 +1310,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1371,7 +1389,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1428,7 +1446,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1485,7 +1503,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1542,7 +1560,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1614,7 +1632,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{ id: 'index',
|
{ id: 'index',
|
||||||
|
@ -1717,7 +1735,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{ id: 'index',
|
{ id: 'index',
|
||||||
|
@ -1798,7 +1816,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1880,7 +1898,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.ios.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.ios.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1963,7 +1981,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2060,7 +2078,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2146,7 +2164,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2251,7 +2269,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2320,7 +2338,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/react-tools/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/react-tools/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2377,7 +2395,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2422,7 +2440,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2483,7 +2501,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.ios.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.ios.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2539,7 +2557,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.ios.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.ios.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2588,7 +2606,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.ios.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.ios.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2672,11 +2690,11 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').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', root, mockStat);
|
triggerFileChange('change', 'index.js', root, mockStat);
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2737,11 +2755,11 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').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', root, mockStat);
|
triggerFileChange('change', 'index.js', root, mockStat);
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2802,10 +2820,10 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function() {
|
||||||
delete filesystem.root.foo;
|
delete filesystem.root.foo;
|
||||||
triggerFileChange('delete', 'foo.js', root);
|
triggerFileChange('delete', 'foo.js', root);
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2866,7 +2884,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function() {
|
||||||
filesystem.root['bar.js'] = [
|
filesystem.root['bar.js'] = [
|
||||||
'/**',
|
'/**',
|
||||||
' * @providesModule bar',
|
' * @providesModule bar',
|
||||||
|
@ -2878,7 +2896,7 @@ describe('DependencyGraph', function() {
|
||||||
filesystem.root.aPackage['main.js'] = 'require("bar")';
|
filesystem.root.aPackage['main.js'] = 'require("bar")';
|
||||||
triggerFileChange('change', 'aPackage/main.js', root, mockStat);
|
triggerFileChange('change', 'aPackage/main.js', root, mockStat);
|
||||||
|
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2949,7 +2967,7 @@ describe('DependencyGraph', function() {
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
|
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -2968,7 +2986,7 @@ describe('DependencyGraph', function() {
|
||||||
filesystem.root['foo.png'] = '';
|
filesystem.root['foo.png'] = '';
|
||||||
triggerFileChange('add', 'foo.png', root, mockStat);
|
triggerFileChange('add', 'foo.png', root, mockStat);
|
||||||
|
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps2) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps2) {
|
||||||
expect(deps2)
|
expect(deps2)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -3021,7 +3039,7 @@ describe('DependencyGraph', function() {
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
|
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{ id: 'index',
|
{ id: 'index',
|
||||||
|
@ -3039,7 +3057,7 @@ describe('DependencyGraph', function() {
|
||||||
filesystem.root['foo.png'] = '';
|
filesystem.root['foo.png'] = '';
|
||||||
triggerFileChange('add', 'foo.png', root, mockStat);
|
triggerFileChange('add', 'foo.png', root, mockStat);
|
||||||
|
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps2) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps2) {
|
||||||
expect(deps2)
|
expect(deps2)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -3108,7 +3126,7 @@ describe('DependencyGraph', function() {
|
||||||
},
|
},
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function() {
|
||||||
filesystem.root['bar.js'] = [
|
filesystem.root['bar.js'] = [
|
||||||
'/**',
|
'/**',
|
||||||
' * @providesModule bar',
|
' * @providesModule bar',
|
||||||
|
@ -3120,7 +3138,7 @@ describe('DependencyGraph', function() {
|
||||||
filesystem.root.aPackage['main.js'] = 'require("bar")';
|
filesystem.root.aPackage['main.js'] = 'require("bar")';
|
||||||
triggerFileChange('change', 'aPackage/main.js', root, mockStat);
|
triggerFileChange('change', 'aPackage/main.js', root, mockStat);
|
||||||
|
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -3193,11 +3211,11 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function() {
|
||||||
triggerFileChange('change', 'aPackage', '/root', {
|
triggerFileChange('change', 'aPackage', '/root', {
|
||||||
isDirectory: function(){ return true; }
|
isDirectory: function(){ return true; }
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -3264,7 +3282,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function() {
|
||||||
filesystem.root['index.js'] = filesystem.root['index.js'].replace(/aPackage/, 'bPackage');
|
filesystem.root['index.js'] = filesystem.root['index.js'].replace(/aPackage/, 'bPackage');
|
||||||
triggerFileChange('change', 'index.js', root, mockStat);
|
triggerFileChange('change', 'index.js', root, mockStat);
|
||||||
|
|
||||||
|
@ -3274,7 +3292,7 @@ describe('DependencyGraph', function() {
|
||||||
});
|
});
|
||||||
triggerFileChange('change', 'package.json', '/root/aPackage', mockStat);
|
triggerFileChange('change', 'package.json', '/root/aPackage', mockStat);
|
||||||
|
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -3331,7 +3349,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function() {
|
||||||
filesystem.root.aPackage['package.json'] = JSON.stringify({
|
filesystem.root.aPackage['package.json'] = JSON.stringify({
|
||||||
name: 'aPackage',
|
name: 'aPackage',
|
||||||
main: 'main.js',
|
main: 'main.js',
|
||||||
|
@ -3339,7 +3357,7 @@ describe('DependencyGraph', function() {
|
||||||
});
|
});
|
||||||
triggerFileChange('change', 'package.json', '/root/aPackage', mockStat);
|
triggerFileChange('change', 'package.json', '/root/aPackage', mockStat);
|
||||||
|
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -3396,14 +3414,14 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function() {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function() {
|
||||||
filesystem.root.aPackage['package.json'] = JSON.stringify({
|
filesystem.root.aPackage['package.json'] = JSON.stringify({
|
||||||
name: 'bPackage',
|
name: 'bPackage',
|
||||||
main: 'main.js',
|
main: 'main.js',
|
||||||
});
|
});
|
||||||
triggerFileChange('change', 'package.json', '/root/aPackage', mockStat);
|
triggerFileChange('change', 'package.json', '/root/aPackage', mockStat);
|
||||||
|
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -3459,7 +3477,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
expect(deps)
|
expect(deps)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -3500,7 +3518,7 @@ describe('DependencyGraph', function() {
|
||||||
filesystem.root.node_modules.foo['main.js'] = 'lol';
|
filesystem.root.node_modules.foo['main.js'] = 'lol';
|
||||||
triggerFileChange('change', 'main.js', '/root/node_modules/foo', mockStat);
|
triggerFileChange('change', 'main.js', '/root/node_modules/foo', mockStat);
|
||||||
|
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps2) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps2) {
|
||||||
expect(deps2)
|
expect(deps2)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
@ -3559,7 +3577,7 @@ describe('DependencyGraph', function() {
|
||||||
assetExts: ['png', 'jpg'],
|
assetExts: ['png', 'jpg'],
|
||||||
cache: cache,
|
cache: cache,
|
||||||
});
|
});
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
filesystem.root.node_modules.foo['package.json'] = JSON.stringify({
|
filesystem.root.node_modules.foo['package.json'] = JSON.stringify({
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
main: 'main.js',
|
main: 'main.js',
|
||||||
|
@ -3567,7 +3585,7 @@ describe('DependencyGraph', function() {
|
||||||
});
|
});
|
||||||
triggerFileChange('change', 'package.json', '/root/node_modules/foo', mockStat);
|
triggerFileChange('change', 'package.json', '/root/node_modules/foo', mockStat);
|
||||||
|
|
||||||
return dgraph.getOrderedDependencies('/root/index.js').then(function(deps2) {
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps2) {
|
||||||
expect(deps2)
|
expect(deps2)
|
||||||
.toEqual([
|
.toEqual([
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,7 +13,6 @@ const AssetModule_DEPRECATED = require('../AssetModule_DEPRECATED');
|
||||||
const Fastfs = require('../fastfs');
|
const Fastfs = require('../fastfs');
|
||||||
const ModuleCache = require('../ModuleCache');
|
const ModuleCache = require('../ModuleCache');
|
||||||
const Promise = require('promise');
|
const Promise = require('promise');
|
||||||
const _ = require('underscore');
|
|
||||||
const crawl = require('../crawlers');
|
const crawl = require('../crawlers');
|
||||||
const debug = require('debug')('DependencyGraph');
|
const debug = require('debug')('DependencyGraph');
|
||||||
const declareOpts = require('../../lib/declareOpts');
|
const declareOpts = require('../../lib/declareOpts');
|
||||||
|
@ -70,7 +69,7 @@ class DependencyGraph {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this._opts = validateOpts(options);
|
this._opts = validateOpts(options);
|
||||||
this._hasteMap = Object.create(null);
|
this._hasteMap = Object.create(null);
|
||||||
this._immediateResolutionCache = Object.create(null);
|
this._resetResolutionCache();
|
||||||
this._cache = this._opts.cache;
|
this._cache = this._opts.cache;
|
||||||
this.load();
|
this.load();
|
||||||
}
|
}
|
||||||
|
@ -80,7 +79,8 @@ class DependencyGraph {
|
||||||
return this._loading;
|
return this._loading;
|
||||||
}
|
}
|
||||||
|
|
||||||
const crawlActivity = Activity.startEvent('fs crawl');
|
const depGraphActivity = Activity.startEvent('Building Dependency Graph');
|
||||||
|
const crawlActivity = Activity.startEvent('Crawling File System');
|
||||||
const allRoots = this._opts.roots.concat(this._opts.assetRoots_DEPRECATED);
|
const allRoots = this._opts.roots.concat(this._opts.assetRoots_DEPRECATED);
|
||||||
this._crawling = crawl(allRoots, {
|
this._crawling = crawl(allRoots, {
|
||||||
ignore: this._opts.ignoreFilePath,
|
ignore: this._opts.ignoreFilePath,
|
||||||
|
@ -89,10 +89,15 @@ class DependencyGraph {
|
||||||
});
|
});
|
||||||
this._crawling.then((files) => Activity.endEvent(crawlActivity));
|
this._crawling.then((files) => Activity.endEvent(crawlActivity));
|
||||||
|
|
||||||
this._fastfs = new Fastfs(this._opts.roots, this._opts.fileWatcher, {
|
this._fastfs = new Fastfs(
|
||||||
|
'JavaScript',
|
||||||
|
this._opts.roots,
|
||||||
|
this._opts.fileWatcher,
|
||||||
|
{
|
||||||
ignore: this._opts.ignoreFilePath,
|
ignore: this._opts.ignoreFilePath,
|
||||||
crawling: this._crawling,
|
crawling: this._crawling,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
this._fastfs.on('change', this._processFileChange.bind(this));
|
this._fastfs.on('change', this._processFileChange.bind(this));
|
||||||
|
|
||||||
|
@ -102,19 +107,31 @@ class DependencyGraph {
|
||||||
this._fastfs.build()
|
this._fastfs.build()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const hasteActivity = Activity.startEvent('Building Haste Map');
|
const hasteActivity = Activity.startEvent('Building Haste Map');
|
||||||
this._buildHasteMap().then(() => Activity.endEvent(hasteActivity));
|
return this._buildHasteMap().then(() => Activity.endEvent(hasteActivity));
|
||||||
}),
|
}),
|
||||||
this._buildAssetMap_DEPRECATED(),
|
this._buildAssetMap_DEPRECATED(),
|
||||||
]);
|
]).then(() =>
|
||||||
|
Activity.endEvent(depGraphActivity)
|
||||||
|
);
|
||||||
|
|
||||||
return this._loading;
|
return this._loading;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveDependency(fromModule, toModuleName) {
|
setup({ platform }) {
|
||||||
if (fromModule._ref) {
|
if (platform && this._opts.platforms.indexOf(platform) === -1) {
|
||||||
fromModule = fromModule._ref;
|
throw new Error('Unrecognized platform: ' + platform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(amasad): This is a potential race condition. Mutliple requests could
|
||||||
|
// interfere with each other. This needs a refactor to fix -- which will
|
||||||
|
// follow this diff.
|
||||||
|
if (this._platformExt !== platform) {
|
||||||
|
this._resetResolutionCache();
|
||||||
|
}
|
||||||
|
this._platformExt = platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveDependency(fromModule, toModuleName) {
|
||||||
const resHash = resolutionHash(fromModule.path, toModuleName);
|
const resHash = resolutionHash(fromModule.path, toModuleName);
|
||||||
|
|
||||||
if (this._immediateResolutionCache[resHash]) {
|
if (this._immediateResolutionCache[resHash]) {
|
||||||
|
@ -163,33 +180,7 @@ class DependencyGraph {
|
||||||
|
|
||||||
getOrderedDependencies(entryPath) {
|
getOrderedDependencies(entryPath) {
|
||||||
return this.load().then(() => {
|
return this.load().then(() => {
|
||||||
const absPath = this._getAbsolutePath(entryPath);
|
const entry = this._getModuleForEntryPath(entryPath);
|
||||||
|
|
||||||
if (absPath == null) {
|
|
||||||
throw new NotFoundError(
|
|
||||||
'Could not find source file at %s',
|
|
||||||
entryPath
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const absolutePath = path.resolve(absPath);
|
|
||||||
|
|
||||||
if (absolutePath == null) {
|
|
||||||
throw new NotFoundError(
|
|
||||||
'Cannot find entry file %s in any of the roots: %j',
|
|
||||||
entryPath,
|
|
||||||
this._opts.roots
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const platformExt = getPlatformExt(entryPath);
|
|
||||||
if (platformExt && this._opts.platforms.indexOf(platformExt) > -1) {
|
|
||||||
this._platformExt = platformExt;
|
|
||||||
} else {
|
|
||||||
this._platformExt = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const entry = this._moduleCache.getModule(absolutePath);
|
|
||||||
const deps = [];
|
const deps = [];
|
||||||
const visited = Object.create(null);
|
const visited = Object.create(null);
|
||||||
visited[entry.hash()] = true;
|
visited[entry.hash()] = true;
|
||||||
|
@ -226,7 +217,22 @@ class DependencyGraph {
|
||||||
};
|
};
|
||||||
|
|
||||||
return collect(entry)
|
return collect(entry)
|
||||||
.then(() => Promise.all(deps.map(dep => dep.getPlainObject())));
|
.then(() => deps);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getAsyncDependencies(entryPath) {
|
||||||
|
return this.load().then(() => {
|
||||||
|
const mod = this._getModuleForEntryPath(entryPath);
|
||||||
|
return mod.getAsyncDependencies().then(bundles =>
|
||||||
|
Promise
|
||||||
|
.all(bundles.map(bundle =>
|
||||||
|
Promise.all(bundle.map(
|
||||||
|
dep => this.resolveDependency(mod, dep)
|
||||||
|
))
|
||||||
|
))
|
||||||
|
.then(bs => bs.map(bundle => bundle.map(dep => dep.path)))
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,6 +252,39 @@ class DependencyGraph {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getModuleForEntryPath(entryPath) {
|
||||||
|
const absPath = this._getAbsolutePath(entryPath);
|
||||||
|
|
||||||
|
if (absPath == null) {
|
||||||
|
throw new NotFoundError(
|
||||||
|
'Could not find source file at %s',
|
||||||
|
entryPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const absolutePath = path.resolve(absPath);
|
||||||
|
|
||||||
|
if (absolutePath == null) {
|
||||||
|
throw new NotFoundError(
|
||||||
|
'Cannot find entry file %s in any of the roots: %j',
|
||||||
|
entryPath,
|
||||||
|
this._opts.roots
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// `platformExt` could be set in the `setup` method.
|
||||||
|
if (!this._platformExt) {
|
||||||
|
const platformExt = getPlatformExt(entryPath);
|
||||||
|
if (platformExt && this._opts.platforms.indexOf(platformExt) > -1) {
|
||||||
|
this._platformExt = platformExt;
|
||||||
|
} else {
|
||||||
|
this._platformExt = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._moduleCache.getModule(absolutePath);
|
||||||
|
}
|
||||||
|
|
||||||
_resolveHasteDependency(fromModule, toModuleName) {
|
_resolveHasteDependency(fromModule, toModuleName) {
|
||||||
toModuleName = normalizePath(toModuleName);
|
toModuleName = normalizePath(toModuleName);
|
||||||
|
|
||||||
|
@ -510,6 +549,7 @@ class DependencyGraph {
|
||||||
this._assetMap_DEPRECATED = Object.create(null);
|
this._assetMap_DEPRECATED = Object.create(null);
|
||||||
|
|
||||||
const fastfs = new Fastfs(
|
const fastfs = new Fastfs(
|
||||||
|
'Assets',
|
||||||
this._opts.assetRoots_DEPRECATED,
|
this._opts.assetRoots_DEPRECATED,
|
||||||
this._opts.fileWatcher,
|
this._opts.fileWatcher,
|
||||||
{ ignore: this._opts.ignoreFilePath, crawling: this._crawling }
|
{ ignore: this._opts.ignoreFilePath, crawling: this._crawling }
|
||||||
|
@ -549,7 +589,7 @@ class DependencyGraph {
|
||||||
_processFileChange(type, filePath, root, fstat) {
|
_processFileChange(type, filePath, root, fstat) {
|
||||||
// It's really hard to invalidate the right module resolution cache
|
// It's really hard to invalidate the right module resolution cache
|
||||||
// so we just blow it up with every file change.
|
// so we just blow it up with every file change.
|
||||||
this._immediateResolutionCache = Object.create(null);
|
this._resetResolutionCache();
|
||||||
|
|
||||||
const absPath = path.join(root, filePath);
|
const absPath = path.join(root, filePath);
|
||||||
if ((fstat && fstat.isDirectory()) ||
|
if ((fstat && fstat.isDirectory()) ||
|
||||||
|
@ -585,6 +625,10 @@ class DependencyGraph {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_resetResolutionCache() {
|
||||||
|
this._immediateResolutionCache = Object.create(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function assetName(file, ext) {
|
function assetName(file, ext) {
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
|
/**
|
||||||
|
* 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';
|
'use strict';
|
||||||
|
|
||||||
const Promise = require('promise');
|
|
||||||
const docblock = require('./DependencyGraph/docblock');
|
const docblock = require('./DependencyGraph/docblock');
|
||||||
const isAbsolutePath = require('absolute-path');
|
const isAbsolutePath = require('absolute-path');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
@ -69,6 +76,10 @@ class Module {
|
||||||
this._cache.invalidate(this.path);
|
this._cache.invalidate(this.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAsyncDependencies() {
|
||||||
|
return this._read().then(data => data.asyncDependencies);
|
||||||
|
}
|
||||||
|
|
||||||
_read() {
|
_read() {
|
||||||
if (!this._reading) {
|
if (!this._reading) {
|
||||||
this._reading = this._fastfs.readFile(this.path).then(content => {
|
this._reading = this._fastfs.readFile(this.path).then(content => {
|
||||||
|
@ -85,7 +96,9 @@ class Module {
|
||||||
if ('extern' in moduleDocBlock) {
|
if ('extern' in moduleDocBlock) {
|
||||||
data.dependencies = [];
|
data.dependencies = [];
|
||||||
} else {
|
} else {
|
||||||
data.dependencies = extractRequires(content);
|
var dependencies = extractRequires(content);
|
||||||
|
data.dependencies = dependencies.sync;
|
||||||
|
data.asyncDependencies = dependencies.async;
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
@ -95,49 +108,73 @@ class Module {
|
||||||
return this._reading;
|
return this._reading;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlainObject() {
|
|
||||||
return Promise.all([
|
|
||||||
this.getName(),
|
|
||||||
this.getDependencies(),
|
|
||||||
]).then(([name, dependencies]) => this.addReference({
|
|
||||||
path: this.path,
|
|
||||||
isJSON: path.extname(this.path) === '.json',
|
|
||||||
isAsset: false,
|
|
||||||
isAsset_DEPRECATED: false,
|
|
||||||
isPolyfill: false,
|
|
||||||
resolution: undefined,
|
|
||||||
id: name,
|
|
||||||
dependencies
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
hash() {
|
hash() {
|
||||||
return `Module : ${this.path}`;
|
return `Module : ${this.path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
addReference(obj) {
|
isJSON() {
|
||||||
Object.defineProperty(obj, '_ref', { value: this });
|
return path.extname(this.path) === '.json';
|
||||||
return obj;
|
}
|
||||||
|
|
||||||
|
isAsset() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isPolyfill() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isAsset_DEPRECATED() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
hash: this.hash(),
|
||||||
|
isJSON: this.isJSON(),
|
||||||
|
isAsset: this.isAsset(),
|
||||||
|
isAsset_DEPRECATED: this.isAsset_DEPRECATED(),
|
||||||
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract all required modules from a `code` string.
|
* Extract all required modules from a `code` string.
|
||||||
*/
|
*/
|
||||||
var blockCommentRe = /\/\*(.|\n)*?\*\//g;
|
const blockCommentRe = /\/\*(.|\n)*?\*\//g;
|
||||||
var lineCommentRe = /\/\/.+(\n|$)/g;
|
const lineCommentRe = /\/\/.+(\n|$)/g;
|
||||||
function extractRequires(code /*: string*/) /*: Array<string>*/ {
|
function extractRequires(code /*: string*/) /*: Array<string>*/ {
|
||||||
var deps = [];
|
var deps = {
|
||||||
|
sync: [],
|
||||||
|
async: [],
|
||||||
|
};
|
||||||
|
|
||||||
code
|
code
|
||||||
.replace(blockCommentRe, '')
|
.replace(blockCommentRe, '')
|
||||||
.replace(lineCommentRe, '')
|
.replace(lineCommentRe, '')
|
||||||
|
// Parse sync dependencies. See comment below for further detils.
|
||||||
.replace(replacePatterns.IMPORT_RE, (match, pre, quot, dep, post) => {
|
.replace(replacePatterns.IMPORT_RE, (match, pre, quot, dep, post) => {
|
||||||
deps.push(dep);
|
deps.sync.push(dep);
|
||||||
return match;
|
return match;
|
||||||
})
|
})
|
||||||
.replace(replacePatterns.REQUIRE_RE, function(match, pre, quot, dep, post) {
|
// Parse the sync dependencies this module has. When the module is
|
||||||
deps.push(dep);
|
// required, all it's sync dependencies will be loaded into memory.
|
||||||
|
// Sync dependencies can be defined either using `require` or the ES6
|
||||||
|
// `import` syntax:
|
||||||
|
// var dep1 = require('dep1');
|
||||||
|
.replace(replacePatterns.REQUIRE_RE, (match, pre, quot, dep, post) => {
|
||||||
|
deps.sync.push(dep);
|
||||||
|
})
|
||||||
|
// Parse async dependencies this module has. As opposed to what happens
|
||||||
|
// with sync dependencies, when the module is required, it's async
|
||||||
|
// dependencies won't be loaded into memory. This is deferred till the
|
||||||
|
// code path gets to the import statement:
|
||||||
|
// System.import('dep1')
|
||||||
|
.replace(replacePatterns.SYSTEM_IMPORT_RE, (match, pre, quot, dep, post) => {
|
||||||
|
deps.async.push([dep]);
|
||||||
|
return match;
|
||||||
});
|
});
|
||||||
|
|
||||||
return deps;
|
return deps;
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Promise = require('promise');
|
||||||
|
const Module = require('./Module');
|
||||||
|
|
||||||
|
class Polyfill extends Module {
|
||||||
|
constructor({ path, id, dependencies }) {
|
||||||
|
super(path);
|
||||||
|
this._id = id;
|
||||||
|
this._dependencies = dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
isHaste() {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return Promise.resolve(this._id);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPackage() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependencies() {
|
||||||
|
return Promise.resolve(this._dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
isJSON() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isPolyfill() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Polyfill;
|
|
@ -9,23 +9,24 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
jest.dontMock('../')
|
jest.dontMock('../')
|
||||||
.dontMock('q')
|
.dontMock('underscore')
|
||||||
.dontMock('../replacePatterns')
|
.dontMock('../replacePatterns');
|
||||||
.setMock('chalk', { dim: function(s) { return s; } });
|
|
||||||
|
|
||||||
jest.mock('path');
|
jest.mock('path');
|
||||||
|
|
||||||
var Promise = require('promise');
|
var Promise = require('promise');
|
||||||
|
var _ = require('underscore');
|
||||||
|
|
||||||
describe('HasteDependencyResolver', function() {
|
describe('HasteDependencyResolver', function() {
|
||||||
var HasteDependencyResolver;
|
var HasteDependencyResolver;
|
||||||
|
var Module;
|
||||||
function createModule(o) {
|
var Polyfill;
|
||||||
o.getPlainObject = () => Promise.resolve(o);
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
Module = require('../Module');
|
||||||
|
Polyfill = require('../Polyfill');
|
||||||
|
Polyfill.mockClear();
|
||||||
|
|
||||||
// For the polyfillDeps
|
// For the polyfillDeps
|
||||||
require('path').join.mockImpl(function(a, b) {
|
require('path').join.mockImpl(function(a, b) {
|
||||||
return b;
|
return b;
|
||||||
|
@ -33,12 +34,16 @@ describe('HasteDependencyResolver', function() {
|
||||||
HasteDependencyResolver = require('../');
|
HasteDependencyResolver = require('../');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function createModule(id, dependencies) {
|
||||||
|
var module = new Module();
|
||||||
|
module.getName.mockImpl(() => Promise.resolve(id));
|
||||||
|
module.getDependencies.mockImpl(() => Promise.resolve(dependencies));
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
describe('getDependencies', function() {
|
describe('getDependencies', function() {
|
||||||
pit('should get dependencies with polyfills', function() {
|
pit('should get dependencies with polyfills', function() {
|
||||||
var module = createModule({
|
var module = createModule('index');
|
||||||
id: 'index',
|
|
||||||
path: '/root/index.js', dependencies: ['a']
|
|
||||||
});
|
|
||||||
var deps = [module];
|
var deps = [module];
|
||||||
|
|
||||||
var depResolver = new HasteDependencyResolver({
|
var depResolver = new HasteDependencyResolver({
|
||||||
|
@ -57,7 +62,8 @@ describe('HasteDependencyResolver', function() {
|
||||||
return depResolver.getDependencies('/root/index.js', { dev: false })
|
return depResolver.getDependencies('/root/index.js', { dev: false })
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
expect(result.mainModuleId).toEqual('index');
|
expect(result.mainModuleId).toEqual('index');
|
||||||
expect(result.dependencies).toEqual([
|
expect(result.dependencies[result.dependencies.length - 1]).toBe(module);
|
||||||
|
expect(_.pluck(Polyfill.mock.calls, 0)).toEqual([
|
||||||
{ path: 'polyfills/prelude.js',
|
{ path: 'polyfills/prelude.js',
|
||||||
id: 'polyfills/prelude.js',
|
id: 'polyfills/prelude.js',
|
||||||
isPolyfill: true,
|
isPolyfill: true,
|
||||||
|
@ -115,18 +121,12 @@ describe('HasteDependencyResolver', function() {
|
||||||
'polyfills/String.prototype.es6.js',
|
'polyfills/String.prototype.es6.js',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
module
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('should get dependencies with polyfills', function() {
|
pit('should get dependencies with polyfills', function() {
|
||||||
var module = createModule({
|
var module = createModule('index');
|
||||||
id: 'index',
|
|
||||||
path: '/root/index.js',
|
|
||||||
dependencies: ['a'],
|
|
||||||
});
|
|
||||||
|
|
||||||
var deps = [module];
|
var deps = [module];
|
||||||
|
|
||||||
var depResolver = new HasteDependencyResolver({
|
var depResolver = new HasteDependencyResolver({
|
||||||
|
@ -145,75 +145,15 @@ describe('HasteDependencyResolver', function() {
|
||||||
return depResolver.getDependencies('/root/index.js', { dev: true })
|
return depResolver.getDependencies('/root/index.js', { dev: true })
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
expect(result.mainModuleId).toEqual('index');
|
expect(result.mainModuleId).toEqual('index');
|
||||||
expect(result.dependencies).toEqual([
|
expect(depGraph.getOrderedDependencies).toBeCalledWith('/root/index.js');
|
||||||
{ path: 'polyfills/prelude_dev.js',
|
expect(result.dependencies[0]).toBe(Polyfill.mock.instances[0]);
|
||||||
id: 'polyfills/prelude_dev.js',
|
expect(result.dependencies[result.dependencies.length - 1])
|
||||||
isPolyfill: true,
|
.toBe(module);
|
||||||
dependencies: []
|
|
||||||
},
|
|
||||||
{ path: 'polyfills/require.js',
|
|
||||||
id: 'polyfills/require.js',
|
|
||||||
isPolyfill: true,
|
|
||||||
dependencies: ['polyfills/prelude_dev.js']
|
|
||||||
},
|
|
||||||
{ path: 'polyfills/polyfills.js',
|
|
||||||
id: 'polyfills/polyfills.js',
|
|
||||||
isPolyfill: true,
|
|
||||||
dependencies: ['polyfills/prelude_dev.js', 'polyfills/require.js']
|
|
||||||
},
|
|
||||||
{ id: 'polyfills/console.js',
|
|
||||||
isPolyfill: true,
|
|
||||||
path: 'polyfills/console.js',
|
|
||||||
dependencies: [
|
|
||||||
'polyfills/prelude_dev.js',
|
|
||||||
'polyfills/require.js',
|
|
||||||
'polyfills/polyfills.js'
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ id: 'polyfills/error-guard.js',
|
|
||||||
isPolyfill: true,
|
|
||||||
path: 'polyfills/error-guard.js',
|
|
||||||
dependencies: [
|
|
||||||
'polyfills/prelude_dev.js',
|
|
||||||
'polyfills/require.js',
|
|
||||||
'polyfills/polyfills.js',
|
|
||||||
'polyfills/console.js'
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ id: 'polyfills/String.prototype.es6.js',
|
|
||||||
isPolyfill: true,
|
|
||||||
path: 'polyfills/String.prototype.es6.js',
|
|
||||||
dependencies: [
|
|
||||||
'polyfills/prelude_dev.js',
|
|
||||||
'polyfills/require.js',
|
|
||||||
'polyfills/polyfills.js',
|
|
||||||
'polyfills/console.js',
|
|
||||||
'polyfills/error-guard.js'
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ id: 'polyfills/Array.prototype.es6.js',
|
|
||||||
isPolyfill: true,
|
|
||||||
path: 'polyfills/Array.prototype.es6.js',
|
|
||||||
dependencies: [
|
|
||||||
'polyfills/prelude_dev.js',
|
|
||||||
'polyfills/require.js',
|
|
||||||
'polyfills/polyfills.js',
|
|
||||||
'polyfills/console.js',
|
|
||||||
'polyfills/error-guard.js',
|
|
||||||
'polyfills/String.prototype.es6.js'
|
|
||||||
],
|
|
||||||
},
|
|
||||||
module
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('should pass in more polyfills', function() {
|
pit('should pass in more polyfills', function() {
|
||||||
var module = createModule({
|
var module = createModule('index');
|
||||||
id: 'index',
|
|
||||||
path: '/root/index.js',
|
|
||||||
dependencies: ['a']
|
|
||||||
});
|
|
||||||
var deps = [module];
|
var deps = [module];
|
||||||
|
|
||||||
var depResolver = new HasteDependencyResolver({
|
var depResolver = new HasteDependencyResolver({
|
||||||
|
@ -231,66 +171,9 @@ describe('HasteDependencyResolver', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
return depResolver.getDependencies('/root/index.js', { dev: false })
|
return depResolver.getDependencies('/root/index.js', { dev: false })
|
||||||
.then(function(result) {
|
.then((result) => {
|
||||||
expect(result.mainModuleId).toEqual('index');
|
expect(result.mainModuleId).toEqual('index');
|
||||||
expect(result.dependencies).toEqual([
|
expect(Polyfill.mock.calls[result.dependencies.length - 2]).toEqual([
|
||||||
{ path: 'polyfills/prelude.js',
|
|
||||||
id: 'polyfills/prelude.js',
|
|
||||||
isPolyfill: true,
|
|
||||||
dependencies: []
|
|
||||||
},
|
|
||||||
{ path: 'polyfills/require.js',
|
|
||||||
id: 'polyfills/require.js',
|
|
||||||
isPolyfill: true,
|
|
||||||
dependencies: ['polyfills/prelude.js']
|
|
||||||
},
|
|
||||||
{ path: 'polyfills/polyfills.js',
|
|
||||||
id: 'polyfills/polyfills.js',
|
|
||||||
isPolyfill: true,
|
|
||||||
dependencies: ['polyfills/prelude.js', 'polyfills/require.js']
|
|
||||||
},
|
|
||||||
{ id: 'polyfills/console.js',
|
|
||||||
isPolyfill: true,
|
|
||||||
path: 'polyfills/console.js',
|
|
||||||
dependencies: [
|
|
||||||
'polyfills/prelude.js',
|
|
||||||
'polyfills/require.js',
|
|
||||||
'polyfills/polyfills.js'
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ id: 'polyfills/error-guard.js',
|
|
||||||
isPolyfill: true,
|
|
||||||
path: 'polyfills/error-guard.js',
|
|
||||||
dependencies: [
|
|
||||||
'polyfills/prelude.js',
|
|
||||||
'polyfills/require.js',
|
|
||||||
'polyfills/polyfills.js',
|
|
||||||
'polyfills/console.js'
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ id: 'polyfills/String.prototype.es6.js',
|
|
||||||
isPolyfill: true,
|
|
||||||
path: 'polyfills/String.prototype.es6.js',
|
|
||||||
dependencies: [
|
|
||||||
'polyfills/prelude.js',
|
|
||||||
'polyfills/require.js',
|
|
||||||
'polyfills/polyfills.js',
|
|
||||||
'polyfills/console.js',
|
|
||||||
'polyfills/error-guard.js'
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ id: 'polyfills/Array.prototype.es6.js',
|
|
||||||
isPolyfill: true,
|
|
||||||
path: 'polyfills/Array.prototype.es6.js',
|
|
||||||
dependencies: [
|
|
||||||
'polyfills/prelude.js',
|
|
||||||
'polyfills/require.js',
|
|
||||||
'polyfills/polyfills.js',
|
|
||||||
'polyfills/console.js',
|
|
||||||
'polyfills/error-guard.js',
|
|
||||||
'polyfills/String.prototype.es6.js',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ path: 'some module',
|
{ path: 'some module',
|
||||||
id: 'some module',
|
id: 'some module',
|
||||||
isPolyfill: true,
|
isPolyfill: true,
|
||||||
|
@ -304,7 +187,6 @@ describe('HasteDependencyResolver', function() {
|
||||||
'polyfills/Array.prototype.es6.js'
|
'polyfills/Array.prototype.es6.js'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
module
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -463,25 +345,21 @@ describe('HasteDependencyResolver', function() {
|
||||||
|
|
||||||
depGraph.resolveDependency.mockImpl(function(fromModule, toModuleName) {
|
depGraph.resolveDependency.mockImpl(function(fromModule, toModuleName) {
|
||||||
if (toModuleName === 'x') {
|
if (toModuleName === 'x') {
|
||||||
return Promise.resolve(createModule({
|
return Promise.resolve(createModule('changed'));
|
||||||
id: 'changed'
|
|
||||||
}));
|
|
||||||
} else if (toModuleName === 'y') {
|
} else if (toModuleName === 'y') {
|
||||||
return Promise.resolve(createModule({ id: 'Y' }));
|
return Promise.resolve(createModule('Y'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
return depResolver.wrapModule({
|
return depResolver.wrapModule(
|
||||||
id: 'test module',
|
createModule('test module', ['x', 'y']),
|
||||||
path: '/root/test.js',
|
code
|
||||||
dependencies: dependencies
|
).then(processedCode => {
|
||||||
}, code).then(processedCode => {
|
|
||||||
|
|
||||||
expect(processedCode).toEqual([
|
expect(processedCode).toEqual([
|
||||||
'__d(\'test module\',["changed","Y"],function(global,' +
|
'__d(\'test module\',["changed","Y"],function(global, require,' +
|
||||||
' require, requireDynamic, requireLazy, module, exports) { ' +
|
' module, exports) { ' +
|
||||||
"import'x';",
|
"import'x';",
|
||||||
"import 'changed';",
|
"import 'changed';",
|
||||||
"import 'changed' ;",
|
"import 'changed' ;",
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
jest
|
||||||
|
.dontMock('absolute-path')
|
||||||
|
.dontMock('../fastfs')
|
||||||
|
.dontMock('../replacePatterns')
|
||||||
|
.dontMock('../DependencyGraph/docblock')
|
||||||
|
.dontMock('../../FileWatcher')
|
||||||
|
.dontMock('../Module');
|
||||||
|
|
||||||
|
jest
|
||||||
|
.mock('fs');
|
||||||
|
|
||||||
|
describe('Module', () => {
|
||||||
|
var Fastfs;
|
||||||
|
var Module;
|
||||||
|
var ModuleCache;
|
||||||
|
var Promise;
|
||||||
|
var fs;
|
||||||
|
|
||||||
|
const FileWatcher = require('../../FileWatcher');
|
||||||
|
const fileWatcher = new FileWatcher(['/root']);
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
Fastfs = require('../fastfs');
|
||||||
|
Module = require('../Module');
|
||||||
|
ModuleCache = require('../ModuleCache');
|
||||||
|
Promise = require('promise');
|
||||||
|
fs = require('fs');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Async Dependencies', () => {
|
||||||
|
function expectAsyncDependenciesToEqual(expected) {
|
||||||
|
var fastfs = new Fastfs(
|
||||||
|
'test',
|
||||||
|
['/root'],
|
||||||
|
fileWatcher,
|
||||||
|
{crawling: Promise.resolve(['/root/index.js']), ignore: []},
|
||||||
|
);
|
||||||
|
|
||||||
|
return fastfs.build().then(() => {
|
||||||
|
var module = new Module('/root/index.js', fastfs, new ModuleCache(fastfs));
|
||||||
|
|
||||||
|
return module.getAsyncDependencies().then(actual =>
|
||||||
|
expect(actual).toEqual(expected)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pit('should recognize single dependency', () => {
|
||||||
|
fs.__setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': 'System.import("dep1")',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return expectAsyncDependenciesToEqual([['dep1']]);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should parse single quoted dependencies', () => {
|
||||||
|
fs.__setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': 'System.import(\'dep1\')',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return expectAsyncDependenciesToEqual([['dep1']]);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should parse multiple async dependencies on the same module', () => {
|
||||||
|
fs.__setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': [
|
||||||
|
'System.import("dep1")',
|
||||||
|
'System.import("dep2")',
|
||||||
|
].join('\n'),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return expectAsyncDependenciesToEqual([
|
||||||
|
['dep1'],
|
||||||
|
['dep2'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('parse fine new lines', () => {
|
||||||
|
fs.__setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': 'System.import(\n"dep1"\n)',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return expectAsyncDependenciesToEqual([['dep1']]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -14,8 +14,9 @@ const stat = Promise.denodeify(fs.stat);
|
||||||
const hasOwn = Object.prototype.hasOwnProperty;
|
const hasOwn = Object.prototype.hasOwnProperty;
|
||||||
|
|
||||||
class Fastfs extends EventEmitter {
|
class Fastfs extends EventEmitter {
|
||||||
constructor(roots, fileWatcher, {ignore, crawling}) {
|
constructor(name, roots, fileWatcher, {ignore, crawling}) {
|
||||||
super();
|
super();
|
||||||
|
this._name = name;
|
||||||
this._fileWatcher = fileWatcher;
|
this._fileWatcher = fileWatcher;
|
||||||
this._ignore = ignore;
|
this._ignore = ignore;
|
||||||
this._roots = roots.map(root => new File(root, { isDir: true }));
|
this._roots = roots.map(root => new File(root, { isDir: true }));
|
||||||
|
@ -29,7 +30,7 @@ class Fastfs extends EventEmitter {
|
||||||
);
|
);
|
||||||
|
|
||||||
return this._crawling.then(files => {
|
return this._crawling.then(files => {
|
||||||
const fastfsActivity = Activity.startEvent('Building in-memory fs');
|
const fastfsActivity = Activity.startEvent('Building in-memory fs for ' + this._name);
|
||||||
files.forEach(filePath => {
|
files.forEach(filePath => {
|
||||||
if (filePath.match(rootsPattern)) {
|
if (filePath.match(rootsPattern)) {
|
||||||
const newFile = new File(filePath, { isDir: false });
|
const newFile = new File(filePath, { isDir: false });
|
||||||
|
@ -155,7 +156,6 @@ class Fastfs extends EventEmitter {
|
||||||
this._getAndAssertRoot(file.path).addChild(file);
|
this._getAndAssertRoot(file.path).addChild(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_processFileChange(type, filePath, root, fstat) {
|
_processFileChange(type, filePath, root, fstat) {
|
||||||
const absPath = path.join(root, filePath);
|
const absPath = path.join(root, filePath);
|
||||||
if (this._ignore(absPath) || (fstat && fstat.isDirectory())) {
|
if (this._ignore(absPath) || (fstat && fstat.isDirectory())) {
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var DependencyGraph = require('./DependencyGraph');
|
var DependencyGraph = require('./DependencyGraph');
|
||||||
var replacePatterns = require('./replacePatterns');
|
var replacePatterns = require('./replacePatterns');
|
||||||
|
var Polyfill = require('./Polyfill');
|
||||||
var declareOpts = require('../lib/declareOpts');
|
var declareOpts = require('../lib/declareOpts');
|
||||||
var Promise = require('promise');
|
var Promise = require('promise');
|
||||||
|
|
||||||
|
@ -26,10 +27,6 @@ var validateOpts = declareOpts({
|
||||||
type: 'array',
|
type: 'array',
|
||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
nonPersistent: {
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
moduleFormat: {
|
moduleFormat: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: 'haste',
|
default: 'haste',
|
||||||
|
@ -76,6 +73,10 @@ var getDependenciesValidateOpts = declareOpts({
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
platform: {
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
HasteDependencyResolver.prototype.getDependencies = function(main, options) {
|
HasteDependencyResolver.prototype.getDependencies = function(main, options) {
|
||||||
|
@ -83,18 +84,24 @@ HasteDependencyResolver.prototype.getDependencies = function(main, options) {
|
||||||
|
|
||||||
var depGraph = this._depGraph;
|
var depGraph = this._depGraph;
|
||||||
var self = this;
|
var self = this;
|
||||||
return depGraph.load().then(
|
|
||||||
() => depGraph.getOrderedDependencies(main).then(
|
depGraph.setup({ platform: opts.platform });
|
||||||
dependencies => {
|
|
||||||
const mainModuleId = dependencies[0].id;
|
return Promise.all([
|
||||||
|
depGraph.getOrderedDependencies(main),
|
||||||
|
depGraph.getAsyncDependencies(main),
|
||||||
|
]).then(
|
||||||
|
([dependencies, asyncDependencies]) => dependencies[0].getName().then(
|
||||||
|
mainModuleId => {
|
||||||
self._prependPolyfillDependencies(
|
self._prependPolyfillDependencies(
|
||||||
dependencies,
|
dependencies,
|
||||||
opts.dev
|
opts.dev,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mainModuleId: mainModuleId,
|
mainModuleId,
|
||||||
dependencies: dependencies
|
dependencies,
|
||||||
|
asyncDependencies,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -118,7 +125,7 @@ HasteDependencyResolver.prototype._prependPolyfillDependencies = function(
|
||||||
].concat(this._polyfillModuleNames);
|
].concat(this._polyfillModuleNames);
|
||||||
|
|
||||||
var polyfillModules = polyfillModuleNames.map(
|
var polyfillModules = polyfillModuleNames.map(
|
||||||
(polyfillModuleName, idx) => ({
|
(polyfillModuleName, idx) => new Polyfill({
|
||||||
path: polyfillModuleName,
|
path: polyfillModuleName,
|
||||||
id: polyfillModuleName,
|
id: polyfillModuleName,
|
||||||
dependencies: polyfillModuleNames.slice(0, idx),
|
dependencies: polyfillModuleNames.slice(0, idx),
|
||||||
|
@ -130,23 +137,26 @@ HasteDependencyResolver.prototype._prependPolyfillDependencies = function(
|
||||||
};
|
};
|
||||||
|
|
||||||
HasteDependencyResolver.prototype.wrapModule = function(module, code) {
|
HasteDependencyResolver.prototype.wrapModule = function(module, code) {
|
||||||
if (module.isPolyfill) {
|
if (module.isPolyfill()) {
|
||||||
return Promise.resolve(code);
|
return Promise.resolve(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolvedDeps = Object.create(null);
|
const resolvedDeps = Object.create(null);
|
||||||
const resolvedDepsArr = [];
|
const resolvedDepsArr = [];
|
||||||
|
|
||||||
return Promise.all(
|
return module.getDependencies().then(
|
||||||
module.dependencies.map(depName => {
|
dependencies => Promise.all(dependencies.map(
|
||||||
return this._depGraph.resolveDependency(module, depName)
|
depName => this._depGraph.resolveDependency(module, depName)
|
||||||
.then((dep) => dep && dep.getPlainObject().then(mod => {
|
.then(depModule => {
|
||||||
if (mod) {
|
if (depModule) {
|
||||||
resolvedDeps[depName] = mod.id;
|
return depModule.getName().then(name => {
|
||||||
resolvedDepsArr.push(mod.id);
|
resolvedDeps[depName] = name;
|
||||||
|
resolvedDepsArr.push(name);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}));
|
|
||||||
})
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
).then(() => {
|
).then(() => {
|
||||||
const relativizeCode = (codeMatch, pre, quot, depName, post) => {
|
const relativizeCode = (codeMatch, pre, quot, depName, post) => {
|
||||||
const depId = resolvedDeps[depName];
|
const depId = resolvedDeps[depName];
|
||||||
|
@ -157,13 +167,15 @@ HasteDependencyResolver.prototype.wrapModule = function(module, code) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return defineModuleCode({
|
return module.getName().then(
|
||||||
|
name => defineModuleCode({
|
||||||
code: code
|
code: code
|
||||||
.replace(replacePatterns.IMPORT_RE, relativizeCode)
|
.replace(replacePatterns.IMPORT_RE, relativizeCode)
|
||||||
.replace(replacePatterns.REQUIRE_RE, relativizeCode),
|
.replace(replacePatterns.REQUIRE_RE, relativizeCode),
|
||||||
deps: JSON.stringify(resolvedDepsArr),
|
deps: JSON.stringify(resolvedDepsArr),
|
||||||
moduleName: module.id,
|
moduleName: name,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -176,8 +188,7 @@ function defineModuleCode({moduleName, code, deps}) {
|
||||||
`__d(`,
|
`__d(`,
|
||||||
`'${moduleName}',`,
|
`'${moduleName}',`,
|
||||||
`${deps},`,
|
`${deps},`,
|
||||||
'function(global, require, ',
|
'function(global, require, module, exports) {',
|
||||||
'requireDynamic, requireLazy, module, exports) {',
|
|
||||||
` ${code}`,
|
` ${code}`,
|
||||||
'\n});',
|
'\n});',
|
||||||
].join('');
|
].join('');
|
||||||
|
|
|
@ -376,6 +376,12 @@
|
||||||
var str = Array.prototype.map.call(arguments, function(arg) {
|
var str = Array.prototype.map.call(arguments, function(arg) {
|
||||||
return inspect(arg, {depth: 10});
|
return inspect(arg, {depth: 10});
|
||||||
}).join(', ');
|
}).join(', ');
|
||||||
|
if (str.slice(0, 10) === "'Warning: " && level >= LOG_LEVELS.error) {
|
||||||
|
// React warnings use console.error so that a stack trace is shown,
|
||||||
|
// but we don't (currently) want these to show a redbox
|
||||||
|
// (Note: Logic duplicated in ExceptionsManager.js.)
|
||||||
|
level = LOG_LEVELS.warn;
|
||||||
|
}
|
||||||
global.nativeLoggingHook(str, level);
|
global.nativeLoggingHook(str, level);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -303,6 +303,18 @@
|
||||||
return _totalFactories;
|
return _totalFactories;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously loads any missing dependency and executes the provided
|
||||||
|
* callback once all of them are satisfied.
|
||||||
|
*
|
||||||
|
* Note that the dependencies on the provided array must be string literals
|
||||||
|
* as the packager uses this information to figure out how the modules are
|
||||||
|
* packaged into different bundles.
|
||||||
|
*/
|
||||||
|
require.ensure = function(dependencies, callback) {
|
||||||
|
throw '`require.ensure` is still not supported';
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The define function conforming to CommonJS proposal:
|
* The define function conforming to CommonJS proposal:
|
||||||
* http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition
|
* http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition
|
||||||
|
@ -464,56 +476,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Special version of define that executes the factory as soon as all
|
|
||||||
* dependencies are met.
|
|
||||||
*
|
|
||||||
* define() does just that, defines a module. Module's factory will not be
|
|
||||||
* called until required by other module. This makes sense for most of our
|
|
||||||
* library modules: we do not want to execute the factory unless it's being
|
|
||||||
* used by someone.
|
|
||||||
*
|
|
||||||
* On the other hand there are modules, that you can call "entrance points".
|
|
||||||
* You want to run the "factory" method for them as soon as all dependencies
|
|
||||||
* are met.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* define('BaseClass', [], function() { return ... });
|
|
||||||
* // ^^ factory for BaseClass was just stored in modulesMap
|
|
||||||
*
|
|
||||||
* define('SubClass', ['BaseClass'], function() { ... });
|
|
||||||
* // SubClass module is marked as ready (waiting == 0), factory is just
|
|
||||||
* // stored
|
|
||||||
*
|
|
||||||
* define('OtherClass, ['BaseClass'], function() { ... });
|
|
||||||
* // OtherClass module is marked as ready (waiting == 0), factory is just
|
|
||||||
* // stored
|
|
||||||
*
|
|
||||||
* requireLazy(['SubClass', 'ChatConfig'],
|
|
||||||
* function() { ... });
|
|
||||||
* // ChatRunner is waiting for ChatConfig to come
|
|
||||||
*
|
|
||||||
* define('ChatConfig', [], { foo: 'bar' });
|
|
||||||
* // at this point ChatRunner is marked as ready, and its factory
|
|
||||||
* // executed + all dependent factories are executed too: BaseClass,
|
|
||||||
* // SubClass, ChatConfig notice that OtherClass's factory won't be
|
|
||||||
* // executed unless explicitly required by someone
|
|
||||||
*
|
|
||||||
* @param {Array} dependencies
|
|
||||||
* @param {Object|Function} factory
|
|
||||||
*/
|
|
||||||
function requireLazy(dependencies, factory, context) {
|
|
||||||
return define(
|
|
||||||
dependencies,
|
|
||||||
factory,
|
|
||||||
undefined,
|
|
||||||
REQUIRE_WHEN_READY,
|
|
||||||
context,
|
|
||||||
1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _uid() {
|
function _uid() {
|
||||||
return '__mod__' + _counter++;
|
return '__mod__' + _counter++;
|
||||||
}
|
}
|
||||||
|
@ -595,12 +557,8 @@
|
||||||
|
|
||||||
_register('global', global);
|
_register('global', global);
|
||||||
_register('require', require);
|
_register('require', require);
|
||||||
_register('requireDynamic', require);
|
|
||||||
_register('requireLazy', requireLazy);
|
|
||||||
|
|
||||||
global.require = require;
|
global.require = require;
|
||||||
global.requireDynamic = require;
|
|
||||||
global.requireLazy = requireLazy;
|
|
||||||
|
|
||||||
require.__debug = {
|
require.__debug = {
|
||||||
modules: modulesMap,
|
modules: modulesMap,
|
||||||
|
@ -621,8 +579,7 @@
|
||||||
* out for every module which would be a lot of extra bytes.
|
* out for every module which would be a lot of extra bytes.
|
||||||
*/
|
*/
|
||||||
global.__d = function(id, deps, factory, _special, _inlineRequires) {
|
global.__d = function(id, deps, factory, _special, _inlineRequires) {
|
||||||
var defaultDeps = ['global', 'require', 'requireDynamic', 'requireLazy',
|
var defaultDeps = ['global', 'require', 'module', 'exports'];
|
||||||
'module', 'exports'];
|
|
||||||
define(id, defaultDeps.concat(deps), factory, _special || USED_AS_TRANSPORT,
|
define(id, defaultDeps.concat(deps), factory, _special || USED_AS_TRANSPORT,
|
||||||
null, null, _inlineRequires);
|
null, null, _inlineRequires);
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,3 +11,5 @@
|
||||||
|
|
||||||
exports.IMPORT_RE = /(\bimport\s+?(?:.+\s+?from\s+?)?)(['"])([^'"]+)(\2)/g;
|
exports.IMPORT_RE = /(\bimport\s+?(?:.+\s+?from\s+?)?)(['"])([^'"]+)(\2)/g;
|
||||||
exports.REQUIRE_RE = /(\brequire\s*?\(\s*?)(['"])([^'"]+)(\2\s*?\))/g;
|
exports.REQUIRE_RE = /(\brequire\s*?\(\s*?)(['"])([^'"]+)(\2\s*?\))/g;
|
||||||
|
exports.SYSTEM_IMPORT_RE = /(\bSystem\.import\s*?\(\s*?)(['"])([^'"]+)(\2\s*?\))/g;
|
||||||
|
|
||||||
|
|
|
@ -8,19 +8,24 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var fs = require('fs');
|
const ModuleTransport = require('../lib/ModuleTransport');
|
||||||
var Promise = require('promise');
|
const Promise = require('promise');
|
||||||
var workerFarm = require('worker-farm');
|
const declareOpts = require('../lib/declareOpts');
|
||||||
var declareOpts = require('../lib/declareOpts');
|
const fs = require('fs');
|
||||||
var util = require('util');
|
const util = require('util');
|
||||||
var ModuleTransport = require('../lib/ModuleTransport');
|
const workerFarm = require('worker-farm');
|
||||||
|
|
||||||
var readFile = Promise.denodeify(fs.readFile);
|
const readFile = Promise.denodeify(fs.readFile);
|
||||||
|
|
||||||
module.exports = Transformer;
|
// Avoid memory leaks caused in workers. This number seems to be a good enough number
|
||||||
Transformer.TransformError = TransformError;
|
// to avoid any memory leak while not slowing down initial builds.
|
||||||
|
// TODO(amasad): Once we get bundle splitting, we can drive this down a bit more.
|
||||||
|
const MAX_CALLS_PER_WORKER = 600;
|
||||||
|
|
||||||
var validateOpts = declareOpts({
|
// Worker will timeout if one of the callers timeout.
|
||||||
|
const DEFAULT_MAX_CALL_TIME = 30000;
|
||||||
|
|
||||||
|
const validateOpts = declareOpts({
|
||||||
projectRoots: {
|
projectRoots: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -40,51 +45,58 @@ var validateOpts = declareOpts({
|
||||||
type: 'object',
|
type: 'object',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
transformTimeoutInterval: {
|
||||||
|
type: 'number',
|
||||||
|
default: DEFAULT_MAX_CALL_TIME,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function Transformer(options) {
|
class Transformer {
|
||||||
var opts = validateOpts(options);
|
constructor(options) {
|
||||||
|
const opts = this._opts = validateOpts(options);
|
||||||
|
|
||||||
this._cache = opts.cache;
|
this._cache = opts.cache;
|
||||||
|
|
||||||
if (options.transformModulePath != null) {
|
if (opts.transformModulePath != null) {
|
||||||
this._workers = workerFarm(
|
this._workers = workerFarm({
|
||||||
{autoStart: true, maxConcurrentCallsPerWorker: 1},
|
autoStart: true,
|
||||||
options.transformModulePath
|
maxConcurrentCallsPerWorker: 1,
|
||||||
);
|
maxCallsPerWorker: MAX_CALLS_PER_WORKER,
|
||||||
|
maxCallTime: opts.transformTimeoutInterval,
|
||||||
|
}, opts.transformModulePath);
|
||||||
|
|
||||||
this._transform = Promise.denodeify(this._workers);
|
this._transform = Promise.denodeify(this._workers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Transformer.prototype.kill = function() {
|
kill() {
|
||||||
this._workers && workerFarm.end(this._workers);
|
this._workers && workerFarm.end(this._workers);
|
||||||
};
|
}
|
||||||
|
|
||||||
Transformer.prototype.invalidateFile = function(filePath) {
|
invalidateFile(filePath) {
|
||||||
this._cache.invalidate(filePath);
|
this._cache.invalidate(filePath);
|
||||||
};
|
}
|
||||||
|
|
||||||
Transformer.prototype.loadFileAndTransform = function(filePath) {
|
loadFileAndTransform(filePath) {
|
||||||
if (this._transform == null) {
|
if (this._transform == null) {
|
||||||
return Promise.reject(new Error('No transfrom module'));
|
return Promise.reject(new Error('No transfrom module'));
|
||||||
}
|
}
|
||||||
|
|
||||||
var transform = this._transform;
|
return this._cache.get(
|
||||||
return this._cache.get(filePath, 'transformedSource', function() {
|
filePath,
|
||||||
|
'transformedSource',
|
||||||
// TODO: use fastfs to avoid reading file from disk again
|
// TODO: use fastfs to avoid reading file from disk again
|
||||||
return readFile(filePath)
|
() => readFile(filePath).then(
|
||||||
.then(function(buffer) {
|
buffer => {
|
||||||
var sourceCode = buffer.toString();
|
const sourceCode = buffer.toString('utf8');
|
||||||
|
|
||||||
return transform({
|
return this._transform({
|
||||||
sourceCode: sourceCode,
|
sourceCode,
|
||||||
filename: filePath,
|
filename: filePath,
|
||||||
}).then(
|
}).then(res => {
|
||||||
function(res) {
|
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
console.warn(
|
console.warn(
|
||||||
'Error property on the result value form the transformer',
|
'Error property on the result value from the transformer',
|
||||||
'module is deprecated and will be removed in future versions.',
|
'module is deprecated and will be removed in future versions.',
|
||||||
'Please pass an error object as the first argument to the callback'
|
'Please pass an error object as the first argument to the callback'
|
||||||
);
|
);
|
||||||
|
@ -97,13 +109,28 @@ Transformer.prototype.loadFileAndTransform = function(filePath) {
|
||||||
sourcePath: filePath,
|
sourcePath: filePath,
|
||||||
sourceCode: sourceCode,
|
sourceCode: sourceCode,
|
||||||
});
|
});
|
||||||
}
|
}).catch(err => {
|
||||||
|
if (err.type === 'TimeoutError') {
|
||||||
|
const timeoutErr = new Error(
|
||||||
|
`TimeoutError: transforming ${filePath} took longer than ` +
|
||||||
|
`${this._opts.transformTimeoutInterval / 1000} seconds.\n` +
|
||||||
|
`You can adjust timeout via the 'transformTimeoutInterval' option`
|
||||||
);
|
);
|
||||||
}).catch(function(err) {
|
timeoutErr.type = 'TimeoutError';
|
||||||
|
throw timeoutErr;
|
||||||
|
}
|
||||||
|
|
||||||
throw formatError(err, filePath);
|
throw formatError(err, filePath);
|
||||||
});
|
});
|
||||||
});
|
})
|
||||||
};
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = Transformer;
|
||||||
|
|
||||||
|
Transformer.TransformError = TransformError;
|
||||||
|
|
||||||
function TransformError() {
|
function TransformError() {
|
||||||
Error.captureStackTrace && Error.captureStackTrace(this, TransformError);
|
Error.captureStackTrace && Error.captureStackTrace(this, TransformError);
|
||||||
|
|
|
@ -1,300 +0,0 @@
|
||||||
/**
|
|
||||||
* 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';
|
|
||||||
|
|
||||||
var _ = require('underscore');
|
|
||||||
var base64VLQ = require('./base64-vlq');
|
|
||||||
var UglifyJS = require('uglify-js');
|
|
||||||
var ModuleTransport = require('../lib/ModuleTransport');
|
|
||||||
|
|
||||||
module.exports = Package;
|
|
||||||
|
|
||||||
var SOURCEMAPPING_URL = '\n\/\/@ sourceMappingURL=';
|
|
||||||
|
|
||||||
function Package(sourceMapUrl) {
|
|
||||||
this._finalized = false;
|
|
||||||
this._modules = [];
|
|
||||||
this._assets = [];
|
|
||||||
this._sourceMapUrl = sourceMapUrl;
|
|
||||||
this._shouldCombineSourceMaps = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Package.prototype.setMainModuleId = function(moduleId) {
|
|
||||||
this._mainModuleId = moduleId;
|
|
||||||
};
|
|
||||||
|
|
||||||
Package.prototype.addModule = function(module) {
|
|
||||||
if (!(module instanceof ModuleTransport)) {
|
|
||||||
throw new Error('Expeceted a ModuleTransport object');
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we get a map from the transformer we'll switch to a mode
|
|
||||||
// were we're combining the source maps as opposed to
|
|
||||||
if (!this._shouldCombineSourceMaps && module.map != null) {
|
|
||||||
this._shouldCombineSourceMaps = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._modules.push(module);
|
|
||||||
};
|
|
||||||
|
|
||||||
Package.prototype.addAsset = function(asset) {
|
|
||||||
this._assets.push(asset);
|
|
||||||
};
|
|
||||||
|
|
||||||
Package.prototype.finalize = function(options) {
|
|
||||||
options = options || {};
|
|
||||||
if (options.runMainModule) {
|
|
||||||
var runCode = ';require("' + this._mainModuleId + '");';
|
|
||||||
this.addModule(new ModuleTransport({
|
|
||||||
code: runCode,
|
|
||||||
virtual: true,
|
|
||||||
sourceCode: runCode,
|
|
||||||
sourcePath: 'RunMainModule.js'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.freeze(this._modules);
|
|
||||||
Object.seal(this._modules);
|
|
||||||
Object.freeze(this._assets);
|
|
||||||
Object.seal(this._assets);
|
|
||||||
this._finalized = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
Package.prototype._assertFinalized = function() {
|
|
||||||
if (!this._finalized) {
|
|
||||||
throw new Error('Package need to be finalized before getting any source');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Package.prototype._getSource = function() {
|
|
||||||
if (this._source == null) {
|
|
||||||
this._source = _.pluck(this._modules, 'code').join('\n');
|
|
||||||
}
|
|
||||||
return this._source;
|
|
||||||
};
|
|
||||||
|
|
||||||
Package.prototype._getInlineSourceMap = function() {
|
|
||||||
if (this._inlineSourceMap == null) {
|
|
||||||
var sourceMap = this.getSourceMap({excludeSource: true});
|
|
||||||
var encoded = new Buffer(JSON.stringify(sourceMap)).toString('base64');
|
|
||||||
this._inlineSourceMap = 'data:application/json;base64,' + encoded;
|
|
||||||
}
|
|
||||||
return this._inlineSourceMap;
|
|
||||||
};
|
|
||||||
|
|
||||||
Package.prototype.getSource = function(options) {
|
|
||||||
this._assertFinalized();
|
|
||||||
|
|
||||||
options = options || {};
|
|
||||||
|
|
||||||
if (options.minify) {
|
|
||||||
return this.getMinifiedSourceAndMap().code;
|
|
||||||
}
|
|
||||||
|
|
||||||
var source = this._getSource();
|
|
||||||
|
|
||||||
if (options.inlineSourceMap) {
|
|
||||||
source += SOURCEMAPPING_URL + this._getInlineSourceMap();
|
|
||||||
} else if (this._sourceMapUrl) {
|
|
||||||
source += SOURCEMAPPING_URL + this._sourceMapUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return source;
|
|
||||||
};
|
|
||||||
|
|
||||||
Package.prototype.getMinifiedSourceAndMap = function() {
|
|
||||||
this._assertFinalized();
|
|
||||||
|
|
||||||
var source = this._getSource();
|
|
||||||
try {
|
|
||||||
return UglifyJS.minify(source, {
|
|
||||||
fromString: true,
|
|
||||||
outSourceMap: 'bundle.js',
|
|
||||||
inSourceMap: this.getSourceMap(),
|
|
||||||
});
|
|
||||||
} catch(e) {
|
|
||||||
// Sometimes, when somebody is using a new syntax feature that we
|
|
||||||
// don't yet have transform for, the untransformed line is sent to
|
|
||||||
// uglify, and it chokes on it. This code tries to print the line
|
|
||||||
// and the module for easier debugging
|
|
||||||
var errorMessage = 'Error while minifying JS\n';
|
|
||||||
if (e.line) {
|
|
||||||
errorMessage += 'Transformed code line: "' +
|
|
||||||
source.split('\n')[e.line - 1] + '"\n';
|
|
||||||
}
|
|
||||||
if (e.pos) {
|
|
||||||
var fromIndex = source.lastIndexOf('__d(\'', e.pos);
|
|
||||||
if (fromIndex > -1) {
|
|
||||||
fromIndex += '__d(\''.length;
|
|
||||||
var toIndex = source.indexOf('\'', fromIndex);
|
|
||||||
errorMessage += 'Module name (best guess): ' +
|
|
||||||
source.substring(fromIndex, toIndex) + '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
errorMessage += e.toString();
|
|
||||||
throw new Error(errorMessage);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* I found a neat trick in the sourcemap spec that makes it easy
|
|
||||||
* to concat sourcemaps. The `sections` field allows us to combine
|
|
||||||
* the sourcemap easily by adding an offset. Tested on chrome.
|
|
||||||
* Seems like it's not yet in Firefox but that should be fine for
|
|
||||||
* now.
|
|
||||||
*/
|
|
||||||
Package.prototype._getCombinedSourceMaps = function(options) {
|
|
||||||
var result = {
|
|
||||||
version: 3,
|
|
||||||
file: 'bundle.js',
|
|
||||||
sections: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
var line = 0;
|
|
||||||
this._modules.forEach(function(module) {
|
|
||||||
var map = module.map;
|
|
||||||
if (module.virtual) {
|
|
||||||
map = generateSourceMapForVirtualModule(module);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.excludeSource) {
|
|
||||||
map = _.extend({}, map, {sourcesContent: []});
|
|
||||||
}
|
|
||||||
|
|
||||||
result.sections.push({
|
|
||||||
offset: { line: line, column: 0 },
|
|
||||||
map: map,
|
|
||||||
});
|
|
||||||
line += module.code.split('\n').length;
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
Package.prototype.getSourceMap = function(options) {
|
|
||||||
this._assertFinalized();
|
|
||||||
|
|
||||||
options = options || {};
|
|
||||||
|
|
||||||
if (this._shouldCombineSourceMaps) {
|
|
||||||
return this._getCombinedSourceMaps(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
var mappings = this._getMappings();
|
|
||||||
var map = {
|
|
||||||
file: 'bundle.js',
|
|
||||||
sources: _.pluck(this._modules, 'sourcePath'),
|
|
||||||
version: 3,
|
|
||||||
names: [],
|
|
||||||
mappings: mappings,
|
|
||||||
sourcesContent: options.excludeSource
|
|
||||||
? [] : _.pluck(this._modules, 'sourceCode')
|
|
||||||
};
|
|
||||||
return map;
|
|
||||||
};
|
|
||||||
|
|
||||||
Package.prototype.getAssets = function() {
|
|
||||||
return this._assets;
|
|
||||||
};
|
|
||||||
|
|
||||||
Package.prototype._getMappings = function() {
|
|
||||||
var modules = this._modules;
|
|
||||||
|
|
||||||
// The first line mapping in our package is basically the base64vlq code for
|
|
||||||
// zeros (A).
|
|
||||||
var firstLine = 'AAAA';
|
|
||||||
|
|
||||||
// Most other lines in our mappings are all zeros (for module, column etc)
|
|
||||||
// except for the lineno mappinp: curLineno - prevLineno = 1; Which is C.
|
|
||||||
var line = 'AACA';
|
|
||||||
|
|
||||||
var moduleLines = Object.create(null);
|
|
||||||
var mappings = '';
|
|
||||||
for (var i = 0; i < modules.length; i++) {
|
|
||||||
var module = modules[i];
|
|
||||||
var code = module.code;
|
|
||||||
var lastCharNewLine = false;
|
|
||||||
moduleLines[module.sourcePath] = 0;
|
|
||||||
for (var t = 0; t < code.length; t++) {
|
|
||||||
if (t === 0 && i === 0) {
|
|
||||||
mappings += firstLine;
|
|
||||||
} else if (t === 0) {
|
|
||||||
mappings += 'AC';
|
|
||||||
|
|
||||||
// This is the only place were we actually don't know the mapping ahead
|
|
||||||
// of time. When it's a new module (and not the first) the lineno
|
|
||||||
// mapping is 0 (current) - number of lines in prev module.
|
|
||||||
mappings += base64VLQ.encode(
|
|
||||||
0 - moduleLines[modules[i - 1].sourcePath]
|
|
||||||
);
|
|
||||||
mappings += 'A';
|
|
||||||
} else if (lastCharNewLine) {
|
|
||||||
moduleLines[module.sourcePath]++;
|
|
||||||
mappings += line;
|
|
||||||
}
|
|
||||||
lastCharNewLine = code[t] === '\n';
|
|
||||||
if (lastCharNewLine) {
|
|
||||||
mappings += ';';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i !== modules.length - 1) {
|
|
||||||
mappings += ';';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mappings;
|
|
||||||
};
|
|
||||||
|
|
||||||
Package.prototype.getJSModulePaths = function() {
|
|
||||||
return this._modules.filter(function(module) {
|
|
||||||
// Filter out non-js files. Like images etc.
|
|
||||||
return !module.virtual;
|
|
||||||
}).map(function(module) {
|
|
||||||
return module.sourcePath;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Package.prototype.getDebugInfo = function() {
|
|
||||||
return [
|
|
||||||
'<div><h3>Main Module:</h3> ' + this._mainModuleId + '</div>',
|
|
||||||
'<style>',
|
|
||||||
'pre.collapsed {',
|
|
||||||
' height: 10px;',
|
|
||||||
' width: 100px;',
|
|
||||||
' display: block;',
|
|
||||||
' text-overflow: ellipsis;',
|
|
||||||
' overflow: hidden;',
|
|
||||||
' cursor: pointer;',
|
|
||||||
'}',
|
|
||||||
'</style>',
|
|
||||||
'<h3> Module paths and transformed code: </h3>',
|
|
||||||
this._modules.map(function(m) {
|
|
||||||
return '<div> <h4> Path: </h4>' + m.sourcePath + '<br/> <h4> Source: </h4>' +
|
|
||||||
'<code><pre class="collapsed" onclick="this.classList.remove(\'collapsed\')">' +
|
|
||||||
_.escape(m.code) + '</pre></code></div>';
|
|
||||||
}).join('\n'),
|
|
||||||
].join('\n');
|
|
||||||
};
|
|
||||||
|
|
||||||
function generateSourceMapForVirtualModule(module) {
|
|
||||||
// All lines map 1-to-1
|
|
||||||
var mappings = 'AAAA;';
|
|
||||||
|
|
||||||
for (var i = 1; i < module.code.split('\n').length; i++) {
|
|
||||||
mappings += 'AACA;';
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
version: 3,
|
|
||||||
sources: [ module.sourcePath ],
|
|
||||||
names: [],
|
|
||||||
mappings: mappings,
|
|
||||||
file: module.sourcePath,
|
|
||||||
sourcesContent: [ module.sourceCode ],
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,289 +0,0 @@
|
||||||
/**
|
|
||||||
* 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';
|
|
||||||
|
|
||||||
var assert = require('assert');
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
var Promise = require('promise');
|
|
||||||
var Cache = require('../Cache');
|
|
||||||
var Transformer = require('../JSTransformer');
|
|
||||||
var DependencyResolver = require('../DependencyResolver');
|
|
||||||
var Package = require('./Package');
|
|
||||||
var Activity = require('../Activity');
|
|
||||||
var ModuleTransport = require('../lib/ModuleTransport');
|
|
||||||
var declareOpts = require('../lib/declareOpts');
|
|
||||||
var imageSize = require('image-size');
|
|
||||||
|
|
||||||
var sizeOf = Promise.denodeify(imageSize);
|
|
||||||
var readFile = Promise.denodeify(fs.readFile);
|
|
||||||
|
|
||||||
var validateOpts = declareOpts({
|
|
||||||
projectRoots: {
|
|
||||||
type: 'array',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
blacklistRE: {
|
|
||||||
type: 'object', // typeof regex is object
|
|
||||||
},
|
|
||||||
moduleFormat: {
|
|
||||||
type: 'string',
|
|
||||||
default: 'haste',
|
|
||||||
},
|
|
||||||
polyfillModuleNames: {
|
|
||||||
type: 'array',
|
|
||||||
default: [],
|
|
||||||
},
|
|
||||||
cacheVersion: {
|
|
||||||
type: 'string',
|
|
||||||
default: '1.0',
|
|
||||||
},
|
|
||||||
resetCache: {
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
transformModulePath: {
|
|
||||||
type:'string',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
nonPersistent: {
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
assetRoots: {
|
|
||||||
type: 'array',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
assetExts: {
|
|
||||||
type: 'array',
|
|
||||||
default: ['png'],
|
|
||||||
},
|
|
||||||
fileWatcher: {
|
|
||||||
type: 'object',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
assetServer: {
|
|
||||||
type: 'object',
|
|
||||||
required: true,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function Packager(options) {
|
|
||||||
var opts = this._opts = validateOpts(options);
|
|
||||||
|
|
||||||
opts.projectRoots.forEach(verifyRootExists);
|
|
||||||
|
|
||||||
this._cache = opts.nonPersistent
|
|
||||||
? new DummyCache()
|
|
||||||
: new Cache({
|
|
||||||
resetCache: opts.resetCache,
|
|
||||||
cacheVersion: opts.cacheVersion,
|
|
||||||
projectRoots: opts.projectRoots,
|
|
||||||
transformModulePath: opts.transformModulePath,
|
|
||||||
});
|
|
||||||
|
|
||||||
this._resolver = new DependencyResolver({
|
|
||||||
projectRoots: opts.projectRoots,
|
|
||||||
blacklistRE: opts.blacklistRE,
|
|
||||||
polyfillModuleNames: opts.polyfillModuleNames,
|
|
||||||
nonPersistent: opts.nonPersistent,
|
|
||||||
moduleFormat: opts.moduleFormat,
|
|
||||||
assetRoots: opts.assetRoots,
|
|
||||||
fileWatcher: opts.fileWatcher,
|
|
||||||
assetExts: opts.assetExts,
|
|
||||||
cache: this._cache,
|
|
||||||
});
|
|
||||||
|
|
||||||
this._transformer = new Transformer({
|
|
||||||
projectRoots: opts.projectRoots,
|
|
||||||
blacklistRE: opts.blacklistRE,
|
|
||||||
cache: this._cache,
|
|
||||||
transformModulePath: opts.transformModulePath,
|
|
||||||
});
|
|
||||||
|
|
||||||
this._projectRoots = opts.projectRoots;
|
|
||||||
this._assetServer = opts.assetServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
Packager.prototype.kill = function() {
|
|
||||||
this._transformer.kill();
|
|
||||||
return this._cache.end();
|
|
||||||
};
|
|
||||||
|
|
||||||
Packager.prototype.package = function(main, runModule, sourceMapUrl, isDev) {
|
|
||||||
var ppackage = new Package(sourceMapUrl);
|
|
||||||
|
|
||||||
var transformModule = this._transformModule.bind(this, ppackage);
|
|
||||||
var findEventId = Activity.startEvent('find dependencies');
|
|
||||||
var transformEventId;
|
|
||||||
|
|
||||||
return this.getDependencies(main, isDev)
|
|
||||||
.then(function(result) {
|
|
||||||
Activity.endEvent(findEventId);
|
|
||||||
transformEventId = Activity.startEvent('transform');
|
|
||||||
|
|
||||||
ppackage.setMainModuleId(result.mainModuleId);
|
|
||||||
return Promise.all(
|
|
||||||
result.dependencies.map(transformModule)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.then(function(transformedModules) {
|
|
||||||
Activity.endEvent(transformEventId);
|
|
||||||
|
|
||||||
transformedModules.forEach(function(moduleTransport) {
|
|
||||||
ppackage.addModule(moduleTransport);
|
|
||||||
});
|
|
||||||
|
|
||||||
ppackage.finalize({ runMainModule: runModule });
|
|
||||||
return ppackage;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Packager.prototype.invalidateFile = function(filePath) {
|
|
||||||
this._transformer.invalidateFile(filePath);
|
|
||||||
};
|
|
||||||
|
|
||||||
Packager.prototype.getDependencies = function(main, isDev) {
|
|
||||||
return this._resolver.getDependencies(main, { dev: isDev });
|
|
||||||
};
|
|
||||||
|
|
||||||
Packager.prototype._transformModule = function(ppackage, module) {
|
|
||||||
var transform;
|
|
||||||
|
|
||||||
if (module.isAsset_DEPRECATED) {
|
|
||||||
transform = this.generateAssetModule_DEPRECATED(ppackage, module);
|
|
||||||
} else if (module.isAsset) {
|
|
||||||
transform = this.generateAssetModule(ppackage, module);
|
|
||||||
} else if (module.isJSON) {
|
|
||||||
transform = generateJSONModule(module);
|
|
||||||
} else {
|
|
||||||
transform = this._transformer.loadFileAndTransform(
|
|
||||||
path.resolve(module.path)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
var resolver = this._resolver;
|
|
||||||
return transform.then(
|
|
||||||
transformed => resolver.wrapModule(module, transformed.code).then(
|
|
||||||
code => new ModuleTransport({
|
|
||||||
code: code,
|
|
||||||
map: transformed.map,
|
|
||||||
sourceCode: transformed.sourceCode,
|
|
||||||
sourcePath: transformed.sourcePath,
|
|
||||||
virtual: transformed.virtual,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Packager.prototype.getGraphDebugInfo = function() {
|
|
||||||
return this._resolver.getDebugInfo();
|
|
||||||
};
|
|
||||||
|
|
||||||
Packager.prototype.generateAssetModule_DEPRECATED = function(ppackage, module) {
|
|
||||||
return sizeOf(module.path).then(function(dimensions) {
|
|
||||||
var img = {
|
|
||||||
__packager_asset: true,
|
|
||||||
isStatic: true,
|
|
||||||
path: module.path,
|
|
||||||
uri: module.id.replace(/^[^!]+!/, ''),
|
|
||||||
width: dimensions.width / module.resolution,
|
|
||||||
height: dimensions.height / module.resolution,
|
|
||||||
deprecated: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
ppackage.addAsset(img);
|
|
||||||
|
|
||||||
var code = 'module.exports = ' + JSON.stringify(img) + ';';
|
|
||||||
|
|
||||||
return new ModuleTransport({
|
|
||||||
code: code,
|
|
||||||
sourceCode: code,
|
|
||||||
sourcePath: module.path,
|
|
||||||
virtual: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Packager.prototype.generateAssetModule = function(ppackage, module) {
|
|
||||||
var relPath = getPathRelativeToRoot(this._projectRoots, module.path);
|
|
||||||
|
|
||||||
return Promise.all([
|
|
||||||
sizeOf(module.path),
|
|
||||||
this._assetServer.getAssetData(relPath),
|
|
||||||
]).then(function(res) {
|
|
||||||
var dimensions = res[0];
|
|
||||||
var assetData = res[1];
|
|
||||||
var img = {
|
|
||||||
__packager_asset: true,
|
|
||||||
fileSystemLocation: path.dirname(module.path),
|
|
||||||
httpServerLocation: path.join('/assets', path.dirname(relPath)),
|
|
||||||
width: dimensions.width / module.resolution,
|
|
||||||
height: dimensions.height / module.resolution,
|
|
||||||
scales: assetData.scales,
|
|
||||||
hash: assetData.hash,
|
|
||||||
name: assetData.name,
|
|
||||||
type: assetData.type,
|
|
||||||
};
|
|
||||||
|
|
||||||
ppackage.addAsset(img);
|
|
||||||
|
|
||||||
var ASSET_TEMPLATE = 'module.exports = require("AssetRegistry").registerAsset(%json);';
|
|
||||||
var code = ASSET_TEMPLATE.replace('%json', JSON.stringify(img));
|
|
||||||
|
|
||||||
return new ModuleTransport({
|
|
||||||
code: code,
|
|
||||||
sourceCode: code,
|
|
||||||
sourcePath: module.path,
|
|
||||||
virtual: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function generateJSONModule(module) {
|
|
||||||
return readFile(module.path).then(function(data) {
|
|
||||||
var code = 'module.exports = ' + data.toString('utf8') + ';';
|
|
||||||
|
|
||||||
return new ModuleTransport({
|
|
||||||
code: code,
|
|
||||||
sourceCode: code,
|
|
||||||
sourcePath: module.path,
|
|
||||||
virtual: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPathRelativeToRoot(roots, absPath) {
|
|
||||||
for (var i = 0; i < roots.length; i++) {
|
|
||||||
var relPath = path.relative(roots[i], absPath);
|
|
||||||
if (relPath[0] !== '.') {
|
|
||||||
return relPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
'Expected root module to be relative to one of the project roots'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function verifyRootExists(root) {
|
|
||||||
// Verify that the root exists.
|
|
||||||
assert(fs.statSync(root).isDirectory(), 'Root has to be a valid directory');
|
|
||||||
}
|
|
||||||
|
|
||||||
class DummyCache {
|
|
||||||
get(filepath, field, loaderCb) {
|
|
||||||
return loaderCb();
|
|
||||||
}
|
|
||||||
|
|
||||||
end(){}
|
|
||||||
invalidate(filepath){}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packager;
|
|
|
@ -8,70 +8,55 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
jest.setMock('worker-farm', function() { return function() {}; })
|
jest.setMock('worker-farm', function() { return () => {}; })
|
||||||
.dontMock('os')
|
.dontMock('os')
|
||||||
.dontMock('path')
|
.dontMock('path')
|
||||||
.dontMock('url')
|
.dontMock('url')
|
||||||
.setMock('timers', {
|
.setMock('timers', { setImmediate: (fn) => setTimeout(fn, 0) })
|
||||||
setImmediate: function(fn) {
|
|
||||||
return setTimeout(fn, 0);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setMock('uglify-js')
|
.setMock('uglify-js')
|
||||||
.dontMock('../')
|
.dontMock('../')
|
||||||
.setMock('chalk', { dim: function(s) { return s; } });
|
.setMock('chalk', { dim: function(s) { return s; } });
|
||||||
|
|
||||||
var Promise = require('promise');
|
const Promise = require('promise');
|
||||||
|
|
||||||
describe('processRequest', function() {
|
describe('processRequest', () => {
|
||||||
var server;
|
var server;
|
||||||
var Packager;
|
var Bundler;
|
||||||
var FileWatcher;
|
var FileWatcher;
|
||||||
|
|
||||||
var options = {
|
const options = {
|
||||||
projectRoots: ['root'],
|
projectRoots: ['root'],
|
||||||
blacklistRE: null,
|
blacklistRE: null,
|
||||||
cacheVersion: null,
|
cacheVersion: null,
|
||||||
polyfillModuleNames: null
|
polyfillModuleNames: null
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeRequest = function(requestHandler, requrl) {
|
const makeRequest = (reqHandler, requrl) => new Promise(resolve =>
|
||||||
return new Promise(function(resolve) {
|
reqHandler(
|
||||||
requestHandler(
|
|
||||||
{ url: requrl },
|
{ url: requrl },
|
||||||
{
|
{
|
||||||
setHeader: jest.genMockFunction(),
|
setHeader: jest.genMockFunction(),
|
||||||
end: function(res) {
|
end: res => resolve(res),
|
||||||
resolve(res);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{ next: () => {} },
|
||||||
next: function() {}
|
)
|
||||||
}
|
|
||||||
);
|
);
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var invalidatorFunc = jest.genMockFunction();
|
const invalidatorFunc = jest.genMockFunction();
|
||||||
var watcherFunc = jest.genMockFunction();
|
const watcherFunc = jest.genMockFunction();
|
||||||
var requestHandler;
|
var requestHandler;
|
||||||
var triggerFileChange;
|
var triggerFileChange;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
Packager = require('../../Packager');
|
Bundler = require('../../Bundler');
|
||||||
FileWatcher = require('../../FileWatcher');
|
FileWatcher = require('../../FileWatcher');
|
||||||
|
|
||||||
Packager.prototype.package = jest.genMockFunction().mockImpl(function() {
|
Bundler.prototype.bundle = jest.genMockFunction().mockImpl(() =>
|
||||||
return Promise.resolve({
|
Promise.resolve({
|
||||||
getSource: function() {
|
getSource: () => 'this is the source',
|
||||||
return 'this is the source';
|
getSourceMap: () => 'this is the source map',
|
||||||
},
|
})
|
||||||
getSourceMap: function() {
|
);
|
||||||
return 'this is the source map';
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
FileWatcher.prototype.on = function(eventType, callback) {
|
FileWatcher.prototype.on = function(eventType, callback) {
|
||||||
if (eventType !== 'all') {
|
if (eventType !== 'all') {
|
||||||
|
@ -82,130 +67,141 @@ describe('processRequest', function() {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
Packager.prototype.invalidateFile = invalidatorFunc;
|
Bundler.prototype.invalidateFile = invalidatorFunc;
|
||||||
|
|
||||||
var Server = require('../');
|
const Server = require('../');
|
||||||
server = new Server(options);
|
server = new Server(options);
|
||||||
requestHandler = server.processRequest.bind(server);
|
requestHandler = server.processRequest.bind(server);
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('returns JS bundle source on request of *.bundle',function() {
|
pit('returns JS bundle source on request of *.bundle', () => {
|
||||||
return makeRequest(
|
return makeRequest(
|
||||||
requestHandler,
|
requestHandler,
|
||||||
'mybundle.bundle?runModule=true'
|
'mybundle.bundle?runModule=true'
|
||||||
).then(function(response) {
|
).then(response =>
|
||||||
expect(response).toEqual('this is the source');
|
expect(response).toEqual('this is the source')
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('returns JS bundle source on request of *.bundle (compat)',function() {
|
pit('returns JS bundle source on request of *.bundle (compat)', () => {
|
||||||
return makeRequest(
|
return makeRequest(
|
||||||
requestHandler,
|
requestHandler,
|
||||||
'mybundle.runModule.bundle'
|
'mybundle.runModule.bundle'
|
||||||
).then(function(response) {
|
).then(response =>
|
||||||
expect(response).toEqual('this is the source');
|
expect(response).toEqual('this is the source')
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('returns sourcemap on request of *.map', function() {
|
pit('returns sourcemap on request of *.map', () => {
|
||||||
return makeRequest(
|
return makeRequest(
|
||||||
requestHandler,
|
requestHandler,
|
||||||
'mybundle.map?runModule=true'
|
'mybundle.map?runModule=true'
|
||||||
).then(function(response) {
|
).then(response =>
|
||||||
expect(response).toEqual('"this is the source map"');
|
expect(response).toEqual('"this is the source map"')
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('works with .ios.js extension', function() {
|
pit('works with .ios.js extension', () => {
|
||||||
return makeRequest(
|
return makeRequest(
|
||||||
requestHandler,
|
requestHandler,
|
||||||
'index.ios.includeRequire.bundle'
|
'index.ios.includeRequire.bundle'
|
||||||
).then(function(response) {
|
).then(response => {
|
||||||
expect(response).toEqual('this is the source');
|
expect(response).toEqual('this is the source');
|
||||||
expect(Packager.prototype.package).toBeCalledWith(
|
expect(Bundler.prototype.bundle).toBeCalledWith(
|
||||||
'index.ios.js',
|
'index.ios.js',
|
||||||
true,
|
true,
|
||||||
'index.ios.includeRequire.map',
|
'index.ios.includeRequire.map',
|
||||||
true
|
true,
|
||||||
|
undefined
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('watches all files in projectRoot', function() {
|
pit('passes in the platform param', function() {
|
||||||
|
return makeRequest(
|
||||||
|
requestHandler,
|
||||||
|
'index.bundle?platform=ios'
|
||||||
|
).then(function(response) {
|
||||||
|
expect(response).toEqual('this is the source');
|
||||||
|
expect(Bundler.prototype.bundle).toBeCalledWith(
|
||||||
|
'index.js',
|
||||||
|
true,
|
||||||
|
'index.map',
|
||||||
|
true,
|
||||||
|
'ios',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('watches all files in projectRoot', () => {
|
||||||
return makeRequest(
|
return makeRequest(
|
||||||
requestHandler,
|
requestHandler,
|
||||||
'mybundle.bundle?runModule=true'
|
'mybundle.bundle?runModule=true'
|
||||||
).then(function() {
|
).then(() => {
|
||||||
expect(watcherFunc.mock.calls[0][0]).toEqual('all');
|
expect(watcherFunc.mock.calls[0][0]).toEqual('all');
|
||||||
expect(watcherFunc.mock.calls[0][1]).not.toBe(null);
|
expect(watcherFunc.mock.calls[0][1]).not.toBe(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('file changes', () => {
|
||||||
describe('file changes', function() {
|
pit('invalides files in bundle when file is updated', () => {
|
||||||
pit('invalides files in package when file is updated', function() {
|
|
||||||
return makeRequest(
|
return makeRequest(
|
||||||
requestHandler,
|
requestHandler,
|
||||||
'mybundle.bundle?runModule=true'
|
'mybundle.bundle?runModule=true'
|
||||||
).then(function() {
|
).then(() => {
|
||||||
var onFileChange = watcherFunc.mock.calls[0][1];
|
const onFileChange = watcherFunc.mock.calls[0][1];
|
||||||
onFileChange('all','path/file.js', options.projectRoots[0]);
|
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');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('rebuilds the packages that contain a file when that file is changed', function() {
|
pit('rebuilds the bundles that contain a file when that file is changed', () => {
|
||||||
var packageFunc = jest.genMockFunction();
|
const bundleFunc = jest.genMockFunction();
|
||||||
packageFunc
|
bundleFunc
|
||||||
.mockReturnValueOnce(
|
.mockReturnValueOnce(
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
getSource: function() {
|
getSource: () => 'this is the first source',
|
||||||
return 'this is the first source';
|
getSourceMap: () => {},
|
||||||
},
|
|
||||||
getSourceMap: function() {},
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mockReturnValue(
|
.mockReturnValue(
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
getSource: function() {
|
getSource: () => 'this is the rebuilt source',
|
||||||
return 'this is the rebuilt source';
|
getSourceMap: () => {},
|
||||||
},
|
|
||||||
getSourceMap: function() {},
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
Packager.prototype.package = packageFunc;
|
Bundler.prototype.bundle = bundleFunc;
|
||||||
|
|
||||||
var Server = require('../../Server');
|
const Server = require('../../Server');
|
||||||
server = new Server(options);
|
server = new Server(options);
|
||||||
|
|
||||||
requestHandler = server.processRequest.bind(server);
|
requestHandler = server.processRequest.bind(server);
|
||||||
|
|
||||||
|
|
||||||
return makeRequest(requestHandler, 'mybundle.bundle?runModule=true')
|
return makeRequest(requestHandler, 'mybundle.bundle?runModule=true')
|
||||||
.then(function(response) {
|
.then(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(bundleFunc.mock.calls.length).toBe(1);
|
||||||
triggerFileChange('all','path/file.js', options.projectRoots[0]);
|
triggerFileChange('all','path/file.js', options.projectRoots[0]);
|
||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(() => {
|
||||||
expect(packageFunc.mock.calls.length).toBe(2);
|
expect(bundleFunc.mock.calls.length).toBe(2);
|
||||||
return makeRequest(requestHandler, 'mybundle.bundle?runModule=true')
|
return makeRequest(requestHandler, 'mybundle.bundle?runModule=true')
|
||||||
.then(function(response) {
|
.then(response =>
|
||||||
expect(response).toEqual('this is the rebuilt source');
|
expect(response).toEqual('this is the rebuilt source')
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('/onchange endpoint', function() {
|
describe('/onchange endpoint', () => {
|
||||||
var EventEmitter;
|
var EventEmitter;
|
||||||
var req;
|
var req;
|
||||||
var res;
|
var res;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
EventEmitter = require.requireActual('events').EventEmitter;
|
EventEmitter = require.requireActual('events').EventEmitter;
|
||||||
req = new EventEmitter();
|
req = new EventEmitter();
|
||||||
req.url = '/onchange';
|
req.url = '/onchange';
|
||||||
|
@ -215,14 +211,14 @@ describe('processRequest', function() {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hold on to request and inform on change', function() {
|
it('should hold on to request and inform on change', () => {
|
||||||
server.processRequest(req, res);
|
server.processRequest(req, res);
|
||||||
triggerFileChange('all', 'path/file.js', options.projectRoots[0]);
|
triggerFileChange('all', 'path/file.js', options.projectRoots[0]);
|
||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
expect(res.end).toBeCalledWith(JSON.stringify({changed: true}));
|
expect(res.end).toBeCalledWith(JSON.stringify({changed: true}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not inform changes on disconnected clients', function() {
|
it('should not inform changes on disconnected clients', () => {
|
||||||
server.processRequest(req, res);
|
server.processRequest(req, res);
|
||||||
req.emit('close');
|
req.emit('close');
|
||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
|
@ -232,56 +228,52 @@ describe('processRequest', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('/assets endpoint', function() {
|
describe('/assets endpoint', () => {
|
||||||
var AssetServer;
|
var AssetServer;
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
AssetServer = require('../../AssetServer');
|
AssetServer = require('../../AssetServer');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should serve simple case', function() {
|
it('should serve simple case', () => {
|
||||||
var req = {
|
const req = {url: '/assets/imgs/a.png'};
|
||||||
url: '/assets/imgs/a.png',
|
const res = {end: jest.genMockFn()};
|
||||||
};
|
|
||||||
var res = {
|
|
||||||
end: jest.genMockFn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
AssetServer.prototype.get.mockImpl(function() {
|
AssetServer.prototype.get.mockImpl(() => Promise.resolve('i am image'));
|
||||||
return Promise.resolve('i am image');
|
|
||||||
});
|
|
||||||
|
|
||||||
server.processRequest(req, res);
|
server.processRequest(req, res);
|
||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
expect(res.end).toBeCalledWith('i am image');
|
expect(res.end).toBeCalledWith('i am image');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return 404', function() {
|
it('should return 404', () => {
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('buildPackage(options)', function() {
|
describe('buildbundle(options)', () => {
|
||||||
it('Calls the packager with the correct args', function() {
|
it('Calls the bundler with the correct args', () => {
|
||||||
server.buildPackage({
|
server.buildBundle({
|
||||||
entryFile: 'foo file'
|
entryFile: 'foo file'
|
||||||
});
|
});
|
||||||
expect(Packager.prototype.package).toBeCalledWith(
|
expect(Bundler.prototype.bundle).toBeCalledWith(
|
||||||
'foo file',
|
'foo file',
|
||||||
true,
|
true,
|
||||||
undefined,
|
undefined,
|
||||||
true
|
true,
|
||||||
|
undefined
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('buildPackageFromUrl(options)', function() {
|
describe('buildBundleFromUrl(options)', () => {
|
||||||
it('Calls the packager with the correct args', function() {
|
it('Calls the bundler with the correct args', () => {
|
||||||
server.buildPackageFromUrl('/path/to/foo.bundle?dev=false&runModule=false');
|
server.buildBundleFromUrl('/path/to/foo.bundle?dev=false&runModule=false');
|
||||||
expect(Packager.prototype.package).toBeCalledWith(
|
expect(Bundler.prototype.bundle).toBeCalledWith(
|
||||||
'path/to/foo.js',
|
'path/to/foo.js',
|
||||||
false,
|
false,
|
||||||
'/path/to/foo.map',
|
'/path/to/foo.map',
|
||||||
false
|
false,
|
||||||
|
undefined
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,21 +8,20 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var url = require('url');
|
const Activity = require('../Activity');
|
||||||
var path = require('path');
|
const AssetServer = require('../AssetServer');
|
||||||
var declareOpts = require('../lib/declareOpts');
|
const FileWatcher = require('../FileWatcher');
|
||||||
var FileWatcher = require('../FileWatcher');
|
const Bundler = require('../Bundler');
|
||||||
var Packager = require('../Packager');
|
const Promise = require('promise');
|
||||||
var Activity = require('../Activity');
|
|
||||||
var AssetServer = require('../AssetServer');
|
|
||||||
var Promise = require('promise');
|
|
||||||
var _ = require('underscore');
|
|
||||||
var exec = require('child_process').exec;
|
|
||||||
var fs = require('fs');
|
|
||||||
|
|
||||||
module.exports = Server;
|
const _ = require('underscore');
|
||||||
|
const declareOpts = require('../lib/declareOpts');
|
||||||
|
const exec = require('child_process').exec;
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const url = require('url');
|
||||||
|
|
||||||
var validateOpts = declareOpts({
|
const validateOpts = declareOpts({
|
||||||
projectRoots: {
|
projectRoots: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -62,117 +61,13 @@ var validateOpts = declareOpts({
|
||||||
type: 'array',
|
type: 'array',
|
||||||
default: ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp'],
|
default: ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp'],
|
||||||
},
|
},
|
||||||
|
transformTimeoutInterval: {
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function Server(options) {
|
const bundleOpts = declareOpts({
|
||||||
var opts = validateOpts(options);
|
|
||||||
|
|
||||||
this._projectRoots = opts.projectRoots;
|
|
||||||
this._packages = Object.create(null);
|
|
||||||
this._changeWatchers = [];
|
|
||||||
|
|
||||||
var assetGlobs = opts.assetExts.map(function(ext) {
|
|
||||||
return '**/*.' + ext;
|
|
||||||
});
|
|
||||||
|
|
||||||
var watchRootConfigs = opts.projectRoots.map(function(dir) {
|
|
||||||
return {
|
|
||||||
dir: dir,
|
|
||||||
globs: [
|
|
||||||
'**/*.js',
|
|
||||||
'**/*.json',
|
|
||||||
].concat(assetGlobs),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
if (opts.assetRoots != null) {
|
|
||||||
watchRootConfigs = watchRootConfigs.concat(
|
|
||||||
opts.assetRoots.map(function(dir) {
|
|
||||||
return {
|
|
||||||
dir: dir,
|
|
||||||
globs: assetGlobs,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._fileWatcher = options.nonPersistent
|
|
||||||
? FileWatcher.createDummyWatcher()
|
|
||||||
: new FileWatcher(watchRootConfigs);
|
|
||||||
|
|
||||||
this._assetServer = new AssetServer({
|
|
||||||
projectRoots: opts.projectRoots,
|
|
||||||
assetExts: opts.assetExts,
|
|
||||||
});
|
|
||||||
|
|
||||||
var packagerOpts = Object.create(opts);
|
|
||||||
packagerOpts.fileWatcher = this._fileWatcher;
|
|
||||||
packagerOpts.assetServer = this._assetServer;
|
|
||||||
this._packager = new Packager(packagerOpts);
|
|
||||||
|
|
||||||
var onFileChange = this._onFileChange.bind(this);
|
|
||||||
this._fileWatcher.on('all', onFileChange);
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
this._debouncedFileChangeHandler = _.debounce(function(filePath) {
|
|
||||||
self._rebuildPackages(filePath);
|
|
||||||
self._informChangeWatchers();
|
|
||||||
}, 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
Server.prototype._onFileChange = function(type, filepath, root) {
|
|
||||||
var absPath = path.join(root, filepath);
|
|
||||||
this._packager.invalidateFile(absPath);
|
|
||||||
// Make sure the file watcher event runs through the system before
|
|
||||||
// we rebuild the packages.
|
|
||||||
this._debouncedFileChangeHandler(absPath);
|
|
||||||
};
|
|
||||||
|
|
||||||
Server.prototype._rebuildPackages = function() {
|
|
||||||
var buildPackage = this.buildPackage.bind(this);
|
|
||||||
var packages = this._packages;
|
|
||||||
|
|
||||||
Object.keys(packages).forEach(function(optionsJson) {
|
|
||||||
var options = JSON.parse(optionsJson);
|
|
||||||
// Wait for a previous build (if exists) to finish.
|
|
||||||
packages[optionsJson] = (packages[optionsJson] || Promise.resolve()).finally(function() {
|
|
||||||
// With finally promise callback we can't change the state of the promise
|
|
||||||
// so we need to reassign the promise.
|
|
||||||
packages[optionsJson] = buildPackage(options).then(function(p) {
|
|
||||||
// Make a throwaway call to getSource to cache the source string.
|
|
||||||
p.getSource({
|
|
||||||
inlineSourceMap: options.inlineSourceMap,
|
|
||||||
minify: options.minify,
|
|
||||||
});
|
|
||||||
return p;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return packages[optionsJson];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Server.prototype._informChangeWatchers = function() {
|
|
||||||
var watchers = this._changeWatchers;
|
|
||||||
var headers = {
|
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
|
||||||
};
|
|
||||||
|
|
||||||
watchers.forEach(function(w) {
|
|
||||||
w.res.writeHead(205, headers);
|
|
||||||
w.res.end(JSON.stringify({ changed: true }));
|
|
||||||
});
|
|
||||||
|
|
||||||
this._changeWatchers = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
Server.prototype.end = function() {
|
|
||||||
Promise.all([
|
|
||||||
this._fileWatcher.end(),
|
|
||||||
this._packager.kill(),
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
var packageOpts = declareOpts({
|
|
||||||
sourceMapUrl: {
|
sourceMapUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -197,46 +92,155 @@ var packageOpts = declareOpts({
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
platform: {
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Server.prototype.buildPackage = function(options) {
|
class Server {
|
||||||
var opts = packageOpts(options);
|
constructor(options) {
|
||||||
|
const opts = validateOpts(options);
|
||||||
|
|
||||||
return this._packager.package(
|
this._projectRoots = opts.projectRoots;
|
||||||
|
this._bundles = Object.create(null);
|
||||||
|
this._changeWatchers = [];
|
||||||
|
|
||||||
|
const assetGlobs = opts.assetExts.map(ext => '**/*.' + ext);
|
||||||
|
|
||||||
|
var watchRootConfigs = opts.projectRoots.map(dir => {
|
||||||
|
return {
|
||||||
|
dir: dir,
|
||||||
|
globs: [
|
||||||
|
'**/*.js',
|
||||||
|
'**/*.json',
|
||||||
|
].concat(assetGlobs),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (opts.assetRoots != null) {
|
||||||
|
watchRootConfigs = watchRootConfigs.concat(
|
||||||
|
opts.assetRoots.map(dir => {
|
||||||
|
return {
|
||||||
|
dir: dir,
|
||||||
|
globs: assetGlobs,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._fileWatcher = options.nonPersistent
|
||||||
|
? FileWatcher.createDummyWatcher()
|
||||||
|
: new FileWatcher(watchRootConfigs);
|
||||||
|
|
||||||
|
this._assetServer = new AssetServer({
|
||||||
|
projectRoots: opts.projectRoots,
|
||||||
|
assetExts: opts.assetExts,
|
||||||
|
});
|
||||||
|
|
||||||
|
const bundlerOpts = Object.create(opts);
|
||||||
|
bundlerOpts.fileWatcher = this._fileWatcher;
|
||||||
|
bundlerOpts.assetServer = this._assetServer;
|
||||||
|
this._bundler = new Bundler(bundlerOpts);
|
||||||
|
|
||||||
|
this._fileWatcher.on('all', this._onFileChange.bind(this));
|
||||||
|
|
||||||
|
this._debouncedFileChangeHandler = _.debounce(filePath => {
|
||||||
|
this._rebuildBundles(filePath);
|
||||||
|
this._informChangeWatchers();
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
end() {
|
||||||
|
Promise.all([
|
||||||
|
this._fileWatcher.end(),
|
||||||
|
this._bundler.kill(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
buildBundle(options) {
|
||||||
|
const opts = bundleOpts(options);
|
||||||
|
return this._bundler.bundle(
|
||||||
opts.entryFile,
|
opts.entryFile,
|
||||||
opts.runModule,
|
opts.runModule,
|
||||||
opts.sourceMapUrl,
|
opts.sourceMapUrl,
|
||||||
opts.dev
|
opts.dev,
|
||||||
|
opts.platform
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
Server.prototype.buildPackageFromUrl = function(reqUrl) {
|
buildBundleFromUrl(reqUrl) {
|
||||||
var options = getOptionsFromUrl(reqUrl);
|
const options = this._getOptionsFromUrl(reqUrl);
|
||||||
return this.buildPackage(options);
|
return this.buildBundle(options);
|
||||||
};
|
}
|
||||||
|
|
||||||
Server.prototype.getDependencies = function(main) {
|
getDependencies(main) {
|
||||||
return this._packager.getDependencies(main);
|
return this._bundler.getDependencies(main);
|
||||||
};
|
}
|
||||||
|
|
||||||
Server.prototype._processDebugRequest = function(reqUrl, res) {
|
_onFileChange(type, filepath, root) {
|
||||||
|
const absPath = path.join(root, filepath);
|
||||||
|
this._bundler.invalidateFile(absPath);
|
||||||
|
// Make sure the file watcher event runs through the system before
|
||||||
|
// we rebuild the bundles.
|
||||||
|
this._debouncedFileChangeHandler(absPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
_rebuildBundles() {
|
||||||
|
const buildBundle = this.buildBundle.bind(this);
|
||||||
|
const bundles = this._bundles;
|
||||||
|
|
||||||
|
Object.keys(bundles).forEach(function(optionsJson) {
|
||||||
|
const options = JSON.parse(optionsJson);
|
||||||
|
// Wait for a previous build (if exists) to finish.
|
||||||
|
bundles[optionsJson] = (bundles[optionsJson] || Promise.resolve()).finally(function() {
|
||||||
|
// With finally promise callback we can't change the state of the promise
|
||||||
|
// so we need to reassign the promise.
|
||||||
|
bundles[optionsJson] = buildBundle(options).then(function(p) {
|
||||||
|
// Make a throwaway call to getSource to cache the source string.
|
||||||
|
p.getSource({
|
||||||
|
inlineSourceMap: options.inlineSourceMap,
|
||||||
|
minify: options.minify,
|
||||||
|
});
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return bundles[optionsJson];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_informChangeWatchers() {
|
||||||
|
const watchers = this._changeWatchers;
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
};
|
||||||
|
|
||||||
|
watchers.forEach(function(w) {
|
||||||
|
w.res.writeHead(205, headers);
|
||||||
|
w.res.end(JSON.stringify({ changed: true }));
|
||||||
|
});
|
||||||
|
|
||||||
|
this._changeWatchers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
_processDebugRequest(reqUrl, res) {
|
||||||
var ret = '<!doctype html>';
|
var ret = '<!doctype html>';
|
||||||
var pathname = url.parse(reqUrl).pathname;
|
const pathname = url.parse(reqUrl).pathname;
|
||||||
var parts = pathname.split('/').filter(Boolean);
|
const parts = pathname.split('/').filter(Boolean);
|
||||||
if (parts.length === 1) {
|
if (parts.length === 1) {
|
||||||
ret += '<div><a href="/debug/packages">Cached Packages</a></div>';
|
ret += '<div><a href="/debug/bundles">Cached Bundles</a></div>';
|
||||||
ret += '<div><a href="/debug/graph">Dependency Graph</a></div>';
|
ret += '<div><a href="/debug/graph">Dependency Graph</a></div>';
|
||||||
res.end(ret);
|
res.end(ret);
|
||||||
} else if (parts[1] === 'packages') {
|
} else if (parts[1] === 'bundles') {
|
||||||
ret += '<h1> Cached Packages </h1>';
|
ret += '<h1> Cached Bundles </h1>';
|
||||||
Promise.all(Object.keys(this._packages).map(function(optionsJson) {
|
Promise.all(Object.keys(this._bundles).map(optionsJson =>
|
||||||
return this._packages[optionsJson].then(function(p) {
|
this._bundles[optionsJson].then(p => {
|
||||||
ret += '<div><h2>' + optionsJson + '</h2>';
|
ret += '<div><h2>' + optionsJson + '</h2>';
|
||||||
ret += p.getDebugInfo();
|
ret += p.getDebugInfo();
|
||||||
});
|
})
|
||||||
}, this)).then(
|
)).then(
|
||||||
function() { res.end(ret); },
|
() => res.end(ret),
|
||||||
function(e) {
|
e => {
|
||||||
res.writeHead(500);
|
res.writeHead(500);
|
||||||
res.end('Internal Error');
|
res.end('Internal Error');
|
||||||
console.log(e.stack);
|
console.log(e.stack);
|
||||||
|
@ -244,56 +248,54 @@ Server.prototype._processDebugRequest = function(reqUrl, res) {
|
||||||
);
|
);
|
||||||
} else if (parts[1] === 'graph'){
|
} else if (parts[1] === 'graph'){
|
||||||
ret += '<h1> Dependency Graph </h2>';
|
ret += '<h1> Dependency Graph </h2>';
|
||||||
ret += this._packager.getGraphDebugInfo();
|
ret += this._bundler.getGraphDebugInfo();
|
||||||
res.end(ret);
|
res.end(ret);
|
||||||
} else {
|
} else {
|
||||||
res.writeHead('404');
|
res.writeHead('404');
|
||||||
res.end('Invalid debug request');
|
res.end('Invalid debug request');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
Server.prototype._processOnChangeRequest = function(req, res) {
|
_processOnChangeRequest(req, res) {
|
||||||
var watchers = this._changeWatchers;
|
const watchers = this._changeWatchers;
|
||||||
|
|
||||||
watchers.push({
|
watchers.push({
|
||||||
req: req,
|
req: req,
|
||||||
res: res,
|
res: res,
|
||||||
});
|
});
|
||||||
|
|
||||||
req.on('close', function() {
|
req.on('close', () => {
|
||||||
for (var i = 0; i < watchers.length; i++) {
|
for (let i = 0; i < watchers.length; i++) {
|
||||||
if (watchers[i] && watchers[i].req === req) {
|
if (watchers[i] && watchers[i].req === req) {
|
||||||
watchers.splice(i, 1);
|
watchers.splice(i, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
Server.prototype._processAssetsRequest = function(req, res) {
|
_processAssetsRequest(req, res) {
|
||||||
var urlObj = url.parse(req.url, true);
|
const urlObj = url.parse(req.url, true);
|
||||||
var assetPath = urlObj.pathname.match(/^\/assets\/(.+)$/);
|
const assetPath = urlObj.pathname.match(/^\/assets\/(.+)$/);
|
||||||
this._assetServer.get(assetPath[1])
|
this._assetServer.get(assetPath[1])
|
||||||
.then(
|
.then(
|
||||||
function(data) {
|
data => res.end(data),
|
||||||
res.end(data);
|
error => {
|
||||||
},
|
|
||||||
function(error) {
|
|
||||||
console.error(error.stack);
|
console.error(error.stack);
|
||||||
res.writeHead('404');
|
res.writeHead('404');
|
||||||
res.end('Asset not found');
|
res.end('Asset not found');
|
||||||
}
|
}
|
||||||
).done();
|
).done();
|
||||||
};
|
}
|
||||||
|
|
||||||
Server.prototype._processProfile = function(req, res) {
|
_processProfile(req, res) {
|
||||||
console.log('Dumping profile information...');
|
console.log('Dumping profile information...');
|
||||||
var dumpName = '/tmp/dump_' + Date.now() + '.json';
|
const dumpName = '/tmp/dump_' + Date.now() + '.json';
|
||||||
var prefix = process.env.TRACE_VIEWER_PATH || '';
|
const prefix = process.env.TRACE_VIEWER_PATH || '';
|
||||||
var cmd = path.join(prefix, 'trace2html') + ' ' + dumpName;
|
const cmd = path.join(prefix, 'trace2html') + ' ' + dumpName;
|
||||||
fs.writeFileSync(dumpName, req.rawBody);
|
fs.writeFileSync(dumpName, req.rawBody);
|
||||||
exec(cmd, function (error) {
|
exec(cmd, error => {
|
||||||
if (error) {
|
if (error) {
|
||||||
if (error.code === 127) {
|
if (error.code === 127) {
|
||||||
console.error(
|
console.error(
|
||||||
|
@ -313,18 +315,18 @@ Server.prototype._processProfile = function(req, res) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
exec('rm ' + dumpName);
|
exec('rm ' + dumpName);
|
||||||
exec('open ' + dumpName.replace(/json$/, 'html'), function (error) {
|
exec('open ' + dumpName.replace(/json$/, 'html'), err => {
|
||||||
if (error) {
|
if (err) {
|
||||||
console.error(error);
|
console.error(err);
|
||||||
}
|
}
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
Server.prototype.processRequest = function(req, res, next) {
|
processRequest(req, res, next) {
|
||||||
var urlObj = url.parse(req.url, true);
|
const urlObj = url.parse(req.url, true);
|
||||||
var pathname = urlObj.pathname;
|
var pathname = urlObj.pathname;
|
||||||
|
|
||||||
var requestType;
|
var requestType;
|
||||||
|
@ -332,6 +334,8 @@ Server.prototype.processRequest = function(req, res, next) {
|
||||||
requestType = 'bundle';
|
requestType = 'bundle';
|
||||||
} else if (pathname.match(/\.map$/)) {
|
} else if (pathname.match(/\.map$/)) {
|
||||||
requestType = 'map';
|
requestType = 'map';
|
||||||
|
} else if (pathname.match(/\.assets$/)) {
|
||||||
|
requestType = 'assets';
|
||||||
} else if (pathname.match(/^\/debug/)) {
|
} else if (pathname.match(/^\/debug/)) {
|
||||||
this._processDebugRequest(req.url, res);
|
this._processDebugRequest(req.url, res);
|
||||||
return;
|
return;
|
||||||
|
@ -349,14 +353,14 @@ Server.prototype.processRequest = function(req, res, next) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var startReqEventId = Activity.startEvent('request:' + req.url);
|
const startReqEventId = Activity.startEvent('request:' + req.url);
|
||||||
var options = getOptionsFromUrl(req.url);
|
const options = this._getOptionsFromUrl(req.url);
|
||||||
var optionsJson = JSON.stringify(options);
|
const optionsJson = JSON.stringify(options);
|
||||||
var building = this._packages[optionsJson] || this.buildPackage(options);
|
const building = this._bundles[optionsJson] || this.buildBundle(options);
|
||||||
|
|
||||||
this._packages[optionsJson] = building;
|
this._bundles[optionsJson] = building;
|
||||||
building.then(
|
building.then(
|
||||||
function(p) {
|
p => {
|
||||||
if (requestType === 'bundle') {
|
if (requestType === 'bundle') {
|
||||||
var bundleSource = p.getSource({
|
var bundleSource = p.getSource({
|
||||||
inlineSourceMap: options.inlineSourceMap,
|
inlineSourceMap: options.inlineSourceMap,
|
||||||
|
@ -370,13 +374,18 @@ Server.prototype.processRequest = function(req, res, next) {
|
||||||
res.setHeader('Content-Type', 'application/json');
|
res.setHeader('Content-Type', 'application/json');
|
||||||
res.end(sourceMap);
|
res.end(sourceMap);
|
||||||
Activity.endEvent(startReqEventId);
|
Activity.endEvent(startReqEventId);
|
||||||
|
} else if (requestType === 'assets') {
|
||||||
|
var assetsList = JSON.stringify(p.getAssets());
|
||||||
|
res.setHeader('Content-Type', 'application/json');
|
||||||
|
res.end(assetsList);
|
||||||
|
Activity.endEvent(startReqEventId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
this._handleError.bind(this, res, optionsJson)
|
this._handleError.bind(this, res, optionsJson)
|
||||||
).done();
|
).done();
|
||||||
};
|
}
|
||||||
|
|
||||||
Server.prototype._handleError = function(res, packageID, error) {
|
_handleError(res, bundleID, error) {
|
||||||
res.writeHead(error.status || 500, {
|
res.writeHead(error.status || 500, {
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
});
|
});
|
||||||
|
@ -390,7 +399,7 @@ Server.prototype._handleError = function(res, packageID, error) {
|
||||||
res.end(JSON.stringify(error));
|
res.end(JSON.stringify(error));
|
||||||
|
|
||||||
if (error.type === 'NotFoundError') {
|
if (error.type === 'NotFoundError') {
|
||||||
delete this._packages[packageID];
|
delete this._bundles[bundleID];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error(error.stack || error);
|
console.error(error.stack || error);
|
||||||
|
@ -400,21 +409,21 @@ Server.prototype._handleError = function(res, packageID, error) {
|
||||||
'please check your terminal error output for more details',
|
'please check your terminal error output for more details',
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function getOptionsFromUrl(reqUrl) {
|
_getOptionsFromUrl(reqUrl) {
|
||||||
// `true` to parse the query param as an object.
|
// `true` to parse the query param as an object.
|
||||||
var urlObj = url.parse(reqUrl, true);
|
const urlObj = url.parse(reqUrl, true);
|
||||||
// node v0.11.14 bug see https://github.com/facebook/react-native/issues/218
|
// node v0.11.14 bug see https://github.com/facebook/react-native/issues/218
|
||||||
urlObj.query = urlObj.query || {};
|
urlObj.query = urlObj.query || {};
|
||||||
|
|
||||||
var pathname = decodeURIComponent(urlObj.pathname);
|
const pathname = decodeURIComponent(urlObj.pathname);
|
||||||
|
|
||||||
// Backwards compatibility. Options used to be as added as '.' to the
|
// Backwards compatibility. Options used to be as added as '.' to the
|
||||||
// entry module name. We can safely remove these options.
|
// entry module name. We can safely remove these options.
|
||||||
var entryFile = pathname.replace(/^\//, '').split('.').filter(function(part) {
|
const entryFile = pathname.replace(/^\//, '').split('.').filter(part => {
|
||||||
if (part === 'includeRequire' || part === 'runModule' ||
|
if (part === 'includeRequire' || part === 'runModule' ||
|
||||||
part === 'bundle' || part === 'map') {
|
part === 'bundle' || part === 'map' || part === 'assets') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -423,21 +432,25 @@ function getOptionsFromUrl(reqUrl) {
|
||||||
return {
|
return {
|
||||||
sourceMapUrl: pathname.replace(/\.bundle$/, '.map'),
|
sourceMapUrl: pathname.replace(/\.bundle$/, '.map'),
|
||||||
entryFile: entryFile,
|
entryFile: entryFile,
|
||||||
dev: getBoolOptionFromQuery(urlObj.query, 'dev', true),
|
dev: this._getBoolOptionFromQuery(urlObj.query, 'dev', true),
|
||||||
minify: getBoolOptionFromQuery(urlObj.query, 'minify'),
|
minify: this._getBoolOptionFromQuery(urlObj.query, 'minify'),
|
||||||
runModule: getBoolOptionFromQuery(urlObj.query, 'runModule', true),
|
runModule: this._getBoolOptionFromQuery(urlObj.query, 'runModule', true),
|
||||||
inlineSourceMap: getBoolOptionFromQuery(
|
inlineSourceMap: this._getBoolOptionFromQuery(
|
||||||
urlObj.query,
|
urlObj.query,
|
||||||
'inlineSourceMap',
|
'inlineSourceMap',
|
||||||
false
|
false
|
||||||
),
|
),
|
||||||
|
platform: urlObj.query.platform,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBoolOptionFromQuery(query, opt, defaultVal) {
|
_getBoolOptionFromQuery(query, opt, defaultVal) {
|
||||||
if (query[opt] == null && defaultVal != null) {
|
if (query[opt] == null && defaultVal != null) {
|
||||||
return defaultVal;
|
return defaultVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
return query[opt] === 'true' || query[opt] === '1';
|
return query[opt] === 'true' || query[opt] === '1';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = Server;
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
/**
|
||||||
|
* 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 Bundle = require('../Bundler/Bundle');
|
||||||
|
const Promise = require('promise');
|
||||||
|
const bser = require('bser');
|
||||||
|
const debug = require('debug')('ReactPackager:SocketClient');
|
||||||
|
const net = require('net');
|
||||||
|
|
||||||
|
class SocketClient {
|
||||||
|
static create(sockPath) {
|
||||||
|
return new SocketClient(sockPath).onReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(sockPath) {
|
||||||
|
debug('connecting to', sockPath);
|
||||||
|
|
||||||
|
this._sock = net.connect(sockPath);
|
||||||
|
this._ready = new Promise((resolve, reject) => {
|
||||||
|
this._sock.on('connect', () => resolve(this));
|
||||||
|
this._sock.on('error', (e) => reject(e));
|
||||||
|
});
|
||||||
|
|
||||||
|
this._resolvers = Object.create(null);
|
||||||
|
const bunser = new bser.BunserBuf();
|
||||||
|
this._sock.on('data', (buf) => bunser.append(buf));
|
||||||
|
|
||||||
|
bunser.on('value', (message) => this._handleMessage(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
onReady() {
|
||||||
|
return this._ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependencies(main) {
|
||||||
|
return this._send({
|
||||||
|
type: 'getDependencies',
|
||||||
|
data: main,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
buildBundle(options) {
|
||||||
|
return this._send({
|
||||||
|
type: 'buildBundle',
|
||||||
|
data: options,
|
||||||
|
}).then(json => Bundle.fromJSON(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
_send(message) {
|
||||||
|
message.id = uid();
|
||||||
|
this._sock.write(bser.dumpToBuffer(message));
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this._resolvers[message.id] = {resolve, reject};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleMessage(message) {
|
||||||
|
if (!(message && message.id && message.type)) {
|
||||||
|
throw new Error(
|
||||||
|
'Malformed message from server ' + JSON.stringify(message)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('got message with type', message.type);
|
||||||
|
|
||||||
|
const resolver = this._resolvers[message.id];
|
||||||
|
if (!resolver) {
|
||||||
|
throw new Error(
|
||||||
|
'Unrecognized message id (message already resolved or never existed'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this._resolvers[message.id];
|
||||||
|
|
||||||
|
if (message.type === 'error') {
|
||||||
|
// TODO convert to an error
|
||||||
|
resolver.reject(message.data);
|
||||||
|
} else {
|
||||||
|
resolver.resolve(message.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
debug('closing connection');
|
||||||
|
this._sock.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = SocketClient;
|
||||||
|
|
||||||
|
function uid(len) {
|
||||||
|
len = len || 7;
|
||||||
|
return Math.random().toString(35).substr(2, len);
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
/**
|
||||||
|
* 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 Promise = require('promise');
|
||||||
|
const Server = require('../Server');
|
||||||
|
const bser = require('bser');
|
||||||
|
const debug = require('debug')('ReactPackager:SocketServer');
|
||||||
|
const fs = require('fs');
|
||||||
|
const net = require('net');
|
||||||
|
|
||||||
|
const MAX_IDLE_TIME = 10 * 60 * 1000;
|
||||||
|
|
||||||
|
class SocketServer {
|
||||||
|
constructor(sockPath, options) {
|
||||||
|
this._server = net.createServer();
|
||||||
|
this._server.listen(sockPath);
|
||||||
|
this._ready = new Promise((resolve, reject) => {
|
||||||
|
this._server.on('error', (e) => reject(e));
|
||||||
|
this._server.on('listening', () => {
|
||||||
|
debug(
|
||||||
|
'Process %d listening on socket path %s ' +
|
||||||
|
'for server with options %j',
|
||||||
|
process.pid,
|
||||||
|
sockPath,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
resolve(this);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this._server.on('connection', (sock) => this._handleConnection(sock));
|
||||||
|
|
||||||
|
// Disable the file watcher.
|
||||||
|
options.nonPersistent = true;
|
||||||
|
this._packagerServer = new Server(options);
|
||||||
|
this._jobs = 0;
|
||||||
|
this._dieEventually();
|
||||||
|
|
||||||
|
process.on('exit', () => fs.unlinkSync(sockPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
onReady() {
|
||||||
|
return this._ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleConnection(sock) {
|
||||||
|
debug('connection to server', process.pid);
|
||||||
|
|
||||||
|
const bunser = new bser.BunserBuf();
|
||||||
|
sock.on('data', (buf) => bunser.append(buf));
|
||||||
|
|
||||||
|
bunser.on('value', (m) => this._handleMessage(sock, m));
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleMessage(sock, m) {
|
||||||
|
if (!m || !m.id || !m.data) {
|
||||||
|
console.error('SocketServer recieved a malformed message: %j', m);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('got request', m);
|
||||||
|
|
||||||
|
// Debounce the kill timer.
|
||||||
|
this._dieEventually();
|
||||||
|
|
||||||
|
const handleError = (error) => {
|
||||||
|
debug('request error', error);
|
||||||
|
this._jobs--;
|
||||||
|
this._reply(sock, m.id, 'error', error.stack);
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (m.type) {
|
||||||
|
case 'getDependencies':
|
||||||
|
this._jobs++;
|
||||||
|
this._packagerServer.getDependencies(m.data).then(
|
||||||
|
({ dependencies }) => this._reply(sock, m.id, 'result', dependencies),
|
||||||
|
handleError,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'buildBundle':
|
||||||
|
this._jobs++;
|
||||||
|
this._packagerServer.buildBundle(m.data).then(
|
||||||
|
(result) => this._reply(sock, m.id, 'result', result),
|
||||||
|
handleError,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this._reply(sock, m.id, 'error', 'Unknown message type: ' + m.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_reply(sock, id, type, data) {
|
||||||
|
debug('request finished', type);
|
||||||
|
|
||||||
|
this._jobs--;
|
||||||
|
data = toJSON(data);
|
||||||
|
|
||||||
|
sock.write(bser.dumpToBuffer({
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
data,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
_dieEventually() {
|
||||||
|
clearTimeout(this._deathTimer);
|
||||||
|
this._deathTimer = setTimeout(() => {
|
||||||
|
if (this._jobs <= 0) {
|
||||||
|
debug('server dying', process.pid);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
this._dieEventually();
|
||||||
|
}, MAX_IDLE_TIME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = SocketServer;
|
||||||
|
|
||||||
|
// TODO move this to bser code.
|
||||||
|
function toJSON(object) {
|
||||||
|
if (!(object && typeof object === 'object')) {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object.toJSON) {
|
||||||
|
return object.toJSON();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var p in object) {
|
||||||
|
object[p] = toJSON(object[p]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
jest.setMock('worker-farm', function() { return () => {}; })
|
||||||
|
.setMock('uglify-js')
|
||||||
|
.mock('net')
|
||||||
|
.dontMock('../SocketClient');
|
||||||
|
|
||||||
|
describe('SocketClient', () => {
|
||||||
|
let SocketClient;
|
||||||
|
let sock;
|
||||||
|
let bunser;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
SocketClient = require('../SocketClient');
|
||||||
|
|
||||||
|
const {EventEmitter} = require.requireActual('events');
|
||||||
|
sock = new EventEmitter();
|
||||||
|
sock.write = jest.genMockFn();
|
||||||
|
|
||||||
|
require('net').connect.mockImpl(() => sock);
|
||||||
|
|
||||||
|
const bser = require('bser');
|
||||||
|
bunser = new EventEmitter();
|
||||||
|
require('bser').BunserBuf.mockImpl(() => bunser);
|
||||||
|
bser.dumpToBuffer.mockImpl((a) => a);
|
||||||
|
|
||||||
|
require('../../Bundler/Bundle').fromJSON.mockImpl((a) => a);
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('create a connection', () => {
|
||||||
|
const client = new SocketClient('/sock');
|
||||||
|
sock.emit('connect');
|
||||||
|
return client.onReady().then(c => {
|
||||||
|
expect(c).toBe(client);
|
||||||
|
expect(require('net').connect).toBeCalledWith('/sock');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('buildBundle', () => {
|
||||||
|
const client = new SocketClient('/sock');
|
||||||
|
sock.emit('connect');
|
||||||
|
const options = { entryFile: '/main' };
|
||||||
|
|
||||||
|
const promise = client.buildBundle(options);
|
||||||
|
|
||||||
|
expect(sock.write).toBeCalled();
|
||||||
|
const message = sock.write.mock.calls[0][0];
|
||||||
|
expect(message.type).toBe('buildBundle');
|
||||||
|
expect(message.data).toEqual(options);
|
||||||
|
expect(typeof message.id).toBe('string');
|
||||||
|
|
||||||
|
bunser.emit('value', {
|
||||||
|
id: message.id,
|
||||||
|
type: 'result',
|
||||||
|
data: { bundle: 'foo' },
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise.then(bundle => expect(bundle).toEqual({ bundle: 'foo' }));
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('getDependencies', () => {
|
||||||
|
const client = new SocketClient('/sock');
|
||||||
|
sock.emit('connect');
|
||||||
|
const main = '/main';
|
||||||
|
|
||||||
|
const promise = client.getDependencies(main);
|
||||||
|
|
||||||
|
expect(sock.write).toBeCalled();
|
||||||
|
const message = sock.write.mock.calls[0][0];
|
||||||
|
expect(message.type).toBe('getDependencies');
|
||||||
|
expect(message.data).toEqual(main);
|
||||||
|
expect(typeof message.id).toBe('string');
|
||||||
|
|
||||||
|
bunser.emit('value', {
|
||||||
|
id: message.id,
|
||||||
|
type: 'result',
|
||||||
|
data: ['a', 'b', 'c'],
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise.then(result => expect(result).toEqual(['a', 'b', 'c']));
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('handle errors', () => {
|
||||||
|
const client = new SocketClient('/sock');
|
||||||
|
sock.emit('connect');
|
||||||
|
const main = '/main';
|
||||||
|
|
||||||
|
const promise = client.getDependencies(main);
|
||||||
|
|
||||||
|
expect(sock.write).toBeCalled();
|
||||||
|
const message = sock.write.mock.calls[0][0];
|
||||||
|
expect(message.type).toBe('getDependencies');
|
||||||
|
expect(message.data).toEqual(main);
|
||||||
|
expect(typeof message.id).toBe('string');
|
||||||
|
|
||||||
|
bunser.emit('value', {
|
||||||
|
id: message.id,
|
||||||
|
type: 'error',
|
||||||
|
data: 'some error'
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise.catch(m => expect(m).toBe('some error'));
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,87 @@
|
||||||
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
jest.setMock('worker-farm', function() { return () => {}; })
|
||||||
|
.setMock('uglify-js')
|
||||||
|
.mock('child_process')
|
||||||
|
.dontMock('../');
|
||||||
|
|
||||||
|
describe('SocketInterface', () => {
|
||||||
|
let SocketInterface;
|
||||||
|
let SocketClient;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
SocketInterface = require('../');
|
||||||
|
SocketClient = require('../SocketClient');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getOrCreateSocketFor', () => {
|
||||||
|
pit('creates socket path by hashing options', () => {
|
||||||
|
const fs = require('fs');
|
||||||
|
fs.existsSync = jest.genMockFn().mockImpl(() => true);
|
||||||
|
|
||||||
|
// Check that given two equivelant server options, we end up with the same
|
||||||
|
// socket path.
|
||||||
|
const options1 = { projectRoots: ['/root'], transformModulePath: '/root/foo' };
|
||||||
|
const options2 = { transformModulePath: '/root/foo', projectRoots: ['/root'] };
|
||||||
|
const options3 = { projectRoots: ['/root', '/root2'] };
|
||||||
|
|
||||||
|
return SocketInterface.getOrCreateSocketFor(options1).then(() => {
|
||||||
|
expect(SocketClient.create).toBeCalled();
|
||||||
|
return SocketInterface.getOrCreateSocketFor(options2).then(() => {
|
||||||
|
expect(SocketClient.create.mock.calls.length).toBe(2);
|
||||||
|
expect(SocketClient.create.mock.calls[0]).toEqual(SocketClient.create.mock.calls[1]);
|
||||||
|
return SocketInterface.getOrCreateSocketFor(options3).then(() => {
|
||||||
|
expect(SocketClient.create.mock.calls.length).toBe(3);
|
||||||
|
expect(SocketClient.create.mock.calls[1]).not.toEqual(SocketClient.create.mock.calls[2]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('should fork a server', () => {
|
||||||
|
const fs = require('fs');
|
||||||
|
fs.existsSync = jest.genMockFn().mockImpl(() => false);
|
||||||
|
let sockPath;
|
||||||
|
let callback;
|
||||||
|
|
||||||
|
require('child_process').spawn.mockImpl(() => ({
|
||||||
|
on: (event, cb) => callback = cb,
|
||||||
|
send: (message) => {
|
||||||
|
expect(message.type).toBe('createSocketServer');
|
||||||
|
expect(message.data.options).toEqual({ projectRoots: ['/root'] });
|
||||||
|
expect(message.data.sockPath).toContain('react-packager');
|
||||||
|
sockPath = message.data.sockPath;
|
||||||
|
|
||||||
|
setImmediate(() => callback({ type: 'createdServer' }));
|
||||||
|
},
|
||||||
|
unref: () => undefined,
|
||||||
|
disconnect: () => undefined,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return SocketInterface.getOrCreateSocketFor({ projectRoots: ['/root'] })
|
||||||
|
.then(() => {
|
||||||
|
expect(SocketClient.create).toBeCalledWith(sockPath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createSocketServer', () => {
|
||||||
|
pit('creates a server', () => {
|
||||||
|
require('../SocketServer').mockImpl((sockPath, options) => {
|
||||||
|
expect(sockPath).toBe('/socket');
|
||||||
|
expect(options).toEqual({ projectRoots: ['/root'] });
|
||||||
|
return { onReady: () => Promise.resolve() };
|
||||||
|
});
|
||||||
|
|
||||||
|
return SocketInterface.createSocketServer('/socket', { projectRoots: ['/root'] });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,94 @@
|
||||||
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
jest.setMock('worker-farm', function() { return () => {}; })
|
||||||
|
.setMock('uglify-js')
|
||||||
|
.mock('net')
|
||||||
|
.mock('fs')
|
||||||
|
.dontMock('../SocketServer');
|
||||||
|
|
||||||
|
describe('SocketServer', () => {
|
||||||
|
let PackagerServer;
|
||||||
|
let SocketServer;
|
||||||
|
let netServer;
|
||||||
|
let bunser;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
SocketServer = require('../SocketServer');
|
||||||
|
|
||||||
|
const {EventEmitter} = require.requireActual('events');
|
||||||
|
netServer = new EventEmitter();
|
||||||
|
netServer.listen = jest.genMockFn();
|
||||||
|
require('net').createServer.mockImpl(() => netServer);
|
||||||
|
|
||||||
|
const bser = require('bser');
|
||||||
|
bunser = new EventEmitter();
|
||||||
|
bser.BunserBuf.mockImpl(() => bunser);
|
||||||
|
bser.dumpToBuffer.mockImpl((a) => a);
|
||||||
|
|
||||||
|
PackagerServer = require('../../Server');
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('create a server', () => {
|
||||||
|
const server = new SocketServer('/sock', { projectRoots: ['/root'] });
|
||||||
|
netServer.emit('listening');
|
||||||
|
return server.onReady().then(s => {
|
||||||
|
expect(s).toBe(server);
|
||||||
|
expect(netServer.listen).toBeCalledWith('/sock');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('handles getDependencies message', () => {
|
||||||
|
const server = new SocketServer('/sock', { projectRoots: ['/root'] });
|
||||||
|
netServer.emit('listening');
|
||||||
|
return server.onReady().then(() => {
|
||||||
|
const sock = { on: jest.genMockFn(), write: jest.genMockFn() };
|
||||||
|
netServer.emit('connection', sock);
|
||||||
|
PackagerServer.prototype.getDependencies.mockImpl(
|
||||||
|
() => Promise.resolve({ dependencies: ['a', 'b', 'c'] })
|
||||||
|
);
|
||||||
|
bunser.emit('value', { type: 'getDependencies', id: 1, data: '/main' });
|
||||||
|
expect(PackagerServer.prototype.getDependencies).toBeCalledWith('/main');
|
||||||
|
|
||||||
|
// Run pending promises.
|
||||||
|
return Promise.resolve().then(() => {
|
||||||
|
expect(sock.write).toBeCalledWith(
|
||||||
|
{ id: 1, type: 'result', data: ['a', 'b', 'c']}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('handles buildBundle message', () => {
|
||||||
|
const server = new SocketServer('/sock', { projectRoots: ['/root'] });
|
||||||
|
netServer.emit('listening');
|
||||||
|
return server.onReady().then(() => {
|
||||||
|
const sock = { on: jest.genMockFn(), write: jest.genMockFn() };
|
||||||
|
netServer.emit('connection', sock);
|
||||||
|
PackagerServer.prototype.buildBundle.mockImpl(
|
||||||
|
() => Promise.resolve({ bundle: 'foo' })
|
||||||
|
);
|
||||||
|
bunser.emit(
|
||||||
|
'value',
|
||||||
|
{ type: 'buildBundle', id: 1, data: { options: 'bar' } }
|
||||||
|
);
|
||||||
|
expect(PackagerServer.prototype.buildBundle).toBeCalledWith(
|
||||||
|
{ options: 'bar' }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Run pending promises.
|
||||||
|
return Promise.resolve().then(() => {
|
||||||
|
expect(sock.write).toBeCalledWith(
|
||||||
|
{ id: 1, type: 'result', data: { bundle: 'foo' }}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,102 @@
|
||||||
|
/**
|
||||||
|
* 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 Promise = require('promise');
|
||||||
|
const SocketClient = require('./SocketClient');
|
||||||
|
const SocketServer = require('./SocketServer');
|
||||||
|
const _ = require('underscore');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const tmpdir = require('os').tmpdir();
|
||||||
|
const {spawn} = require('child_process');
|
||||||
|
|
||||||
|
const CREATE_SERVER_TIMEOUT = 10000;
|
||||||
|
|
||||||
|
const SocketInterface = {
|
||||||
|
getOrCreateSocketFor(options) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const hash = crypto.createHash('md5');
|
||||||
|
Object.keys(options).sort().forEach(key => {
|
||||||
|
if (options[key] && typeof options[key] !== 'string') {
|
||||||
|
hash.update(JSON.stringify(options[key]));
|
||||||
|
} else {
|
||||||
|
hash.update(options[key]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const sockPath = path.join(
|
||||||
|
tmpdir,
|
||||||
|
'react-packager-' + hash.digest('hex')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fs.existsSync(sockPath)) {
|
||||||
|
resolve(SocketClient.create(sockPath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const logPath = path.join(tmpdir, 'react-packager.log');
|
||||||
|
|
||||||
|
const timeout = setTimeout(
|
||||||
|
() => reject(
|
||||||
|
new Error(
|
||||||
|
'Took too long to start server. Server logs: \n' +
|
||||||
|
fs.readFileSync(logPath, 'utf8')
|
||||||
|
)
|
||||||
|
),
|
||||||
|
CREATE_SERVER_TIMEOUT,
|
||||||
|
);
|
||||||
|
|
||||||
|
const log = fs.openSync(logPath, 'a');
|
||||||
|
|
||||||
|
// Enable server debugging by default since it's going to a log file.
|
||||||
|
const env = _.clone(process.env);
|
||||||
|
env.DEBUG = 'ReactPackager:SocketServer';
|
||||||
|
|
||||||
|
// We have to go through the main entry point to make sure
|
||||||
|
// we go through the babel require hook.
|
||||||
|
const child = spawn(
|
||||||
|
process.execPath,
|
||||||
|
[path.join(__dirname, '..', '..', 'index.js')],
|
||||||
|
{
|
||||||
|
detached: true,
|
||||||
|
env: env,
|
||||||
|
stdio: ['ipc', log, log]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
child.unref();
|
||||||
|
|
||||||
|
child.on('message', m => {
|
||||||
|
if (m && m.type && m.type === 'createdServer') {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
child.disconnect();
|
||||||
|
resolve(SocketClient.create(sockPath));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (options.blacklistRE) {
|
||||||
|
options.blacklistRE = { source: options.blacklistRE.source };
|
||||||
|
}
|
||||||
|
|
||||||
|
child.send({
|
||||||
|
type: 'createSocketServer',
|
||||||
|
data: { sockPath, options }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
createSocketServer(sockPath, options) {
|
||||||
|
return new SocketServer(sockPath, options).onReady();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = SocketInterface;
|
|
@ -10,7 +10,14 @@
|
||||||
|
|
||||||
var fs = jest.genMockFromModule('fs');
|
var fs = jest.genMockFromModule('fs');
|
||||||
|
|
||||||
|
function asyncCallback(callback) {
|
||||||
|
return function() {
|
||||||
|
setImmediate(() => callback.apply(this, arguments));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fs.realpath.mockImpl(function(filepath, callback) {
|
fs.realpath.mockImpl(function(filepath, callback) {
|
||||||
|
callback = asyncCallback(callback);
|
||||||
var node;
|
var node;
|
||||||
try {
|
try {
|
||||||
node = getToNode(filepath);
|
node = getToNode(filepath);
|
||||||
|
@ -24,6 +31,7 @@ fs.realpath.mockImpl(function(filepath, callback) {
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.readdir.mockImpl(function(filepath, callback) {
|
fs.readdir.mockImpl(function(filepath, callback) {
|
||||||
|
callback = asyncCallback(callback);
|
||||||
var node;
|
var node;
|
||||||
try {
|
try {
|
||||||
node = getToNode(filepath);
|
node = getToNode(filepath);
|
||||||
|
@ -42,6 +50,7 @@ fs.readdir.mockImpl(function(filepath, callback) {
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.readFile.mockImpl(function(filepath, encoding, callback) {
|
fs.readFile.mockImpl(function(filepath, encoding, callback) {
|
||||||
|
callback = asyncCallback(callback);
|
||||||
if (arguments.length === 2) {
|
if (arguments.length === 2) {
|
||||||
callback = encoding;
|
callback = encoding;
|
||||||
encoding = null;
|
encoding = null;
|
||||||
|
@ -60,6 +69,7 @@ fs.readFile.mockImpl(function(filepath, encoding, callback) {
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.stat.mockImpl(function(filepath, callback) {
|
fs.stat.mockImpl(function(filepath, callback) {
|
||||||
|
callback = asyncCallback(callback);
|
||||||
var node;
|
var node;
|
||||||
try {
|
try {
|
||||||
node = getToNode(filepath);
|
node = getToNode(filepath);
|
||||||
|
|
|
@ -13,6 +13,12 @@
|
||||||
var babel = require('babel-core');
|
var babel = require('babel-core');
|
||||||
|
|
||||||
function transform(srcTxt, filename, options) {
|
function transform(srcTxt, filename, options) {
|
||||||
|
var plugins = [];
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
plugins = plugins.concat(['node-env-inline', 'dunderscore-dev-inline']);
|
||||||
|
}
|
||||||
|
|
||||||
var result = babel.transform(srcTxt, {
|
var result = babel.transform(srcTxt, {
|
||||||
retainLines: true,
|
retainLines: true,
|
||||||
compact: true,
|
compact: true,
|
||||||
|
@ -35,6 +41,7 @@ function transform(srcTxt, filename, options) {
|
||||||
'react',
|
'react',
|
||||||
'regenerator',
|
'regenerator',
|
||||||
],
|
],
|
||||||
|
plugins: plugins,
|
||||||
sourceFileName: filename,
|
sourceFileName: filename,
|
||||||
sourceMaps: false,
|
sourceMaps: false,
|
||||||
extra: options || {},
|
extra: options || {},
|
||||||
|
|
Loading…
Reference in New Issue