mirror of https://github.com/status-im/metro.git
[react-packager] Rename 'Package' to 'Bundle'
Summary: The word Package is overloaded, it may mean npm package, or may mean a collection of bundles. Neither is what we mean. We mean `bundle`. This renames it and modernize some of the Bundler code.
This commit is contained in:
parent
d1772eebe6
commit
3f1619b158
|
@ -22,18 +22,23 @@ exports.middleware = function(options) {
|
||||||
return server.processRequest.bind(server);
|
return server.processRequest.bind(server);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.buildPackage = function(options, packageOptions) {
|
|
||||||
|
// 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;
|
||||||
|
|
|
@ -0,0 +1,307 @@
|
||||||
|
/**
|
||||||
|
* 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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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')
|
||||||
|
@ -19,11 +19,11 @@ 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;
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ describe('Packager', function() {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
Packager = require('../');
|
Bundler = require('../');
|
||||||
|
|
||||||
require('fs').statSync.mockImpl(function() {
|
require('fs').statSync.mockImpl(function() {
|
||||||
return {
|
return {
|
||||||
|
@ -53,7 +53,7 @@ describe('Packager', function() {
|
||||||
getAssetData: jest.genMockFn(),
|
getAssetData: jest.genMockFn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
packager = new Packager({
|
bundler = new Bundler({
|
||||||
projectRoots: ['/root'],
|
projectRoots: ['/root'],
|
||||||
assetServer: assetServer,
|
assetServer: assetServer,
|
||||||
});
|
});
|
||||||
|
@ -118,8 +118,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',
|
||||||
|
@ -204,7 +204,7 @@ describe('Packager', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('gets the list of dependencies', function() {
|
pit('gets the list of dependencies', function() {
|
||||||
return packager.getDependencies('/root/foo.js', true)
|
return bundler.getDependencies('/root/foo.js', true)
|
||||||
.then(({dependencies}) => {
|
.then(({dependencies}) => {
|
||||||
expect(dependencies).toEqual([
|
expect(dependencies).toEqual([
|
||||||
{
|
{
|
|
@ -0,0 +1,291 @@
|
||||||
|
/**
|
||||||
|
* 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 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,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
class Bundler {
|
||||||
|
|
||||||
|
constructor(options) {
|
||||||
|
const 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
kill() {
|
||||||
|
this._transformer.kill();
|
||||||
|
return this._cache.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle(main, runModule, sourceMapUrl, isDev) {
|
||||||
|
const bundle = new Bundle(sourceMapUrl);
|
||||||
|
|
||||||
|
const transformModule = this._transformModule.bind(this, bundle);
|
||||||
|
const findEventId = Activity.startEvent('find dependencies');
|
||||||
|
let transformEventId;
|
||||||
|
|
||||||
|
return this.getDependencies(main, isDev)
|
||||||
|
.then(function(result) {
|
||||||
|
Activity.endEvent(findEventId);
|
||||||
|
transformEventId = Activity.startEvent('transform');
|
||||||
|
|
||||||
|
bundle.setMainModuleId(result.mainModuleId);
|
||||||
|
return Promise.all(
|
||||||
|
result.dependencies.map(transformModule)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(function(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) {
|
||||||
|
return this._resolver.getDependencies(main, { dev: isDev });
|
||||||
|
}
|
||||||
|
|
||||||
|
_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 sizeOf(module.path).then(function(dimensions) {
|
||||||
|
const 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
|
@ -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');
|
||||||
|
|
|
@ -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;
|
|
|
@ -24,7 +24,7 @@ var Promise = require('promise');
|
||||||
|
|
||||||
describe('processRequest', function() {
|
describe('processRequest', function() {
|
||||||
var server;
|
var server;
|
||||||
var Packager;
|
var Bundler;
|
||||||
var FileWatcher;
|
var FileWatcher;
|
||||||
|
|
||||||
var options = {
|
var options = {
|
||||||
|
@ -57,10 +57,10 @@ describe('processRequest', function() {
|
||||||
var triggerFileChange;
|
var triggerFileChange;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
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(function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
getSource: function() {
|
getSource: function() {
|
||||||
return 'this is the source';
|
return 'this is the source';
|
||||||
|
@ -81,7 +81,7 @@ describe('processRequest', function() {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
Packager.prototype.invalidateFile = invalidatorFunc;
|
Bundler.prototype.invalidateFile = invalidatorFunc;
|
||||||
|
|
||||||
var Server = require('../');
|
var Server = require('../');
|
||||||
server = new Server(options);
|
server = new Server(options);
|
||||||
|
@ -121,7 +121,7 @@ describe('processRequest', function() {
|
||||||
'index.ios.includeRequire.bundle'
|
'index.ios.includeRequire.bundle'
|
||||||
).then(function(response) {
|
).then(function(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',
|
||||||
|
@ -142,7 +142,7 @@ describe('processRequest', function() {
|
||||||
|
|
||||||
|
|
||||||
describe('file changes', function() {
|
describe('file changes', function() {
|
||||||
pit('invalides files in package when file is updated', function() {
|
pit('invalides files in bundle when file is updated', function() {
|
||||||
return makeRequest(
|
return makeRequest(
|
||||||
requestHandler,
|
requestHandler,
|
||||||
'mybundle.bundle?runModule=true'
|
'mybundle.bundle?runModule=true'
|
||||||
|
@ -153,9 +153,9 @@ describe('processRequest', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
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', function() {
|
||||||
var packageFunc = jest.genMockFunction();
|
var bundleFunc = jest.genMockFunction();
|
||||||
packageFunc
|
bundleFunc
|
||||||
.mockReturnValueOnce(
|
.mockReturnValueOnce(
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
getSource: function() {
|
getSource: function() {
|
||||||
|
@ -173,7 +173,7 @@ describe('processRequest', function() {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
Packager.prototype.package = packageFunc;
|
Bundler.prototype.bundle = bundleFunc;
|
||||||
|
|
||||||
var Server = require('../../Server');
|
var Server = require('../../Server');
|
||||||
server = new Server(options);
|
server = new Server(options);
|
||||||
|
@ -184,13 +184,13 @@ describe('processRequest', function() {
|
||||||
return makeRequest(requestHandler, 'mybundle.bundle?runModule=true')
|
return makeRequest(requestHandler, 'mybundle.bundle?runModule=true')
|
||||||
.then(function(response) {
|
.then(function(response) {
|
||||||
expect(response).toEqual('this is the first source');
|
expect(response).toEqual('this is the first source');
|
||||||
expect(packageFunc.mock.calls.length).toBe(1);
|
expect(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(function() {
|
||||||
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(function(response) {
|
||||||
expect(response).toEqual('this is the rebuilt source');
|
expect(response).toEqual('this is the rebuilt source');
|
||||||
|
@ -259,12 +259,12 @@ describe('processRequest', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('buildPackage(options)', function() {
|
describe('buildBundle(options)', function() {
|
||||||
it('Calls the packager with the correct args', function() {
|
it('Calls the bundler with the correct args', function() {
|
||||||
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,
|
||||||
|
@ -273,10 +273,10 @@ describe('processRequest', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('buildPackageFromUrl(options)', function() {
|
describe('buildBundleFromUrl(options)', function() {
|
||||||
it('Calls the packager with the correct args', function() {
|
it('Calls the bundler with the correct args', function() {
|
||||||
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',
|
||||||
|
|
|
@ -12,7 +12,7 @@ var url = require('url');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var declareOpts = require('../lib/declareOpts');
|
var declareOpts = require('../lib/declareOpts');
|
||||||
var FileWatcher = require('../FileWatcher');
|
var FileWatcher = require('../FileWatcher');
|
||||||
var Packager = require('../Packager');
|
var Bundler = require('../Bundler');
|
||||||
var Activity = require('../Activity');
|
var Activity = require('../Activity');
|
||||||
var AssetServer = require('../AssetServer');
|
var AssetServer = require('../AssetServer');
|
||||||
var Promise = require('promise');
|
var Promise = require('promise');
|
||||||
|
@ -68,7 +68,7 @@ function Server(options) {
|
||||||
var opts = validateOpts(options);
|
var opts = validateOpts(options);
|
||||||
|
|
||||||
this._projectRoots = opts.projectRoots;
|
this._projectRoots = opts.projectRoots;
|
||||||
this._packages = Object.create(null);
|
this._bundles = Object.create(null);
|
||||||
this._changeWatchers = [];
|
this._changeWatchers = [];
|
||||||
|
|
||||||
var assetGlobs = opts.assetExts.map(function(ext) {
|
var assetGlobs = opts.assetExts.map(function(ext) {
|
||||||
|
@ -105,40 +105,40 @@ function Server(options) {
|
||||||
assetExts: opts.assetExts,
|
assetExts: opts.assetExts,
|
||||||
});
|
});
|
||||||
|
|
||||||
var packagerOpts = Object.create(opts);
|
var bundlerOpts = Object.create(opts);
|
||||||
packagerOpts.fileWatcher = this._fileWatcher;
|
bundlerOpts.fileWatcher = this._fileWatcher;
|
||||||
packagerOpts.assetServer = this._assetServer;
|
bundlerOpts.assetServer = this._assetServer;
|
||||||
this._packager = new Packager(packagerOpts);
|
this._bundler = new Bundler(bundlerOpts);
|
||||||
|
|
||||||
var onFileChange = this._onFileChange.bind(this);
|
var onFileChange = this._onFileChange.bind(this);
|
||||||
this._fileWatcher.on('all', onFileChange);
|
this._fileWatcher.on('all', onFileChange);
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
this._debouncedFileChangeHandler = _.debounce(function(filePath) {
|
this._debouncedFileChangeHandler = _.debounce(function(filePath) {
|
||||||
self._rebuildPackages(filePath);
|
self._rebuildBundles(filePath);
|
||||||
self._informChangeWatchers();
|
self._informChangeWatchers();
|
||||||
}, 50);
|
}, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
Server.prototype._onFileChange = function(type, filepath, root) {
|
Server.prototype._onFileChange = function(type, filepath, root) {
|
||||||
var absPath = path.join(root, filepath);
|
var absPath = path.join(root, filepath);
|
||||||
this._packager.invalidateFile(absPath);
|
this._bundler.invalidateFile(absPath);
|
||||||
// Make sure the file watcher event runs through the system before
|
// Make sure the file watcher event runs through the system before
|
||||||
// we rebuild the packages.
|
// we rebuild the bundles.
|
||||||
this._debouncedFileChangeHandler(absPath);
|
this._debouncedFileChangeHandler(absPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
Server.prototype._rebuildPackages = function() {
|
Server.prototype._rebuildBundles = function() {
|
||||||
var buildPackage = this.buildPackage.bind(this);
|
var buildBundle = this.buildBundle.bind(this);
|
||||||
var packages = this._packages;
|
var bundles = this._bundles;
|
||||||
|
|
||||||
Object.keys(packages).forEach(function(optionsJson) {
|
Object.keys(bundles).forEach(function(optionsJson) {
|
||||||
var options = JSON.parse(optionsJson);
|
var options = JSON.parse(optionsJson);
|
||||||
// Wait for a previous build (if exists) to finish.
|
// Wait for a previous build (if exists) to finish.
|
||||||
packages[optionsJson] = (packages[optionsJson] || Promise.resolve()).finally(function() {
|
bundles[optionsJson] = (bundles[optionsJson] || Promise.resolve()).finally(function() {
|
||||||
// With finally promise callback we can't change the state of the promise
|
// With finally promise callback we can't change the state of the promise
|
||||||
// so we need to reassign the promise.
|
// so we need to reassign the promise.
|
||||||
packages[optionsJson] = buildPackage(options).then(function(p) {
|
bundles[optionsJson] = buildBundle(options).then(function(p) {
|
||||||
// Make a throwaway call to getSource to cache the source string.
|
// Make a throwaway call to getSource to cache the source string.
|
||||||
p.getSource({
|
p.getSource({
|
||||||
inlineSourceMap: options.inlineSourceMap,
|
inlineSourceMap: options.inlineSourceMap,
|
||||||
|
@ -147,7 +147,7 @@ Server.prototype._rebuildPackages = function() {
|
||||||
return p;
|
return p;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return packages[optionsJson];
|
return bundles[optionsJson];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -168,11 +168,11 @@ Server.prototype._informChangeWatchers = function() {
|
||||||
Server.prototype.end = function() {
|
Server.prototype.end = function() {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
this._fileWatcher.end(),
|
this._fileWatcher.end(),
|
||||||
this._packager.kill(),
|
this._bundler.kill(),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
var packageOpts = declareOpts({
|
var bundleOpts = declareOpts({
|
||||||
sourceMapUrl: {
|
sourceMapUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -199,10 +199,10 @@ var packageOpts = declareOpts({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Server.prototype.buildPackage = function(options) {
|
Server.prototype.buildBundle = function(options) {
|
||||||
var opts = packageOpts(options);
|
var opts = bundleOpts(options);
|
||||||
|
|
||||||
return this._packager.package(
|
return this._bundler.bundle(
|
||||||
opts.entryFile,
|
opts.entryFile,
|
||||||
opts.runModule,
|
opts.runModule,
|
||||||
opts.sourceMapUrl,
|
opts.sourceMapUrl,
|
||||||
|
@ -210,13 +210,13 @@ Server.prototype.buildPackage = function(options) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Server.prototype.buildPackageFromUrl = function(reqUrl) {
|
Server.prototype.buildBundleFromUrl = function(reqUrl) {
|
||||||
var options = getOptionsFromUrl(reqUrl);
|
var options = getOptionsFromUrl(reqUrl);
|
||||||
return this.buildPackage(options);
|
return this.buildBundle(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Server.prototype.getDependencies = function(main) {
|
Server.prototype.getDependencies = function(main) {
|
||||||
return this._packager.getDependencies(main);
|
return this._bundler.getDependencies(main);
|
||||||
};
|
};
|
||||||
|
|
||||||
Server.prototype._processDebugRequest = function(reqUrl, res) {
|
Server.prototype._processDebugRequest = function(reqUrl, res) {
|
||||||
|
@ -224,13 +224,13 @@ Server.prototype._processDebugRequest = function(reqUrl, res) {
|
||||||
var pathname = url.parse(reqUrl).pathname;
|
var pathname = url.parse(reqUrl).pathname;
|
||||||
var parts = pathname.split('/').filter(Boolean);
|
var 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(function(optionsJson) {
|
||||||
return this._packages[optionsJson].then(function(p) {
|
return this._bundles[optionsJson].then(function(p) {
|
||||||
ret += '<div><h2>' + optionsJson + '</h2>';
|
ret += '<div><h2>' + optionsJson + '</h2>';
|
||||||
ret += p.getDebugInfo();
|
ret += p.getDebugInfo();
|
||||||
});
|
});
|
||||||
|
@ -244,7 +244,7 @@ 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');
|
||||||
|
@ -352,9 +352,9 @@ Server.prototype.processRequest = function(req, res, next) {
|
||||||
var startReqEventId = Activity.startEvent('request:' + req.url);
|
var startReqEventId = Activity.startEvent('request:' + req.url);
|
||||||
var options = getOptionsFromUrl(req.url);
|
var options = getOptionsFromUrl(req.url);
|
||||||
var optionsJson = JSON.stringify(options);
|
var optionsJson = JSON.stringify(options);
|
||||||
var building = this._packages[optionsJson] || this.buildPackage(options);
|
var building = this._bundles[optionsJson] || this.buildBundle(options);
|
||||||
|
|
||||||
this._packages[optionsJson] = building;
|
this._bundles[optionsJson] = building;
|
||||||
building.then(
|
building.then(
|
||||||
function(p) {
|
function(p) {
|
||||||
if (requestType === 'bundle') {
|
if (requestType === 'bundle') {
|
||||||
|
@ -376,7 +376,7 @@ Server.prototype.processRequest = function(req, res, next) {
|
||||||
).done();
|
).done();
|
||||||
};
|
};
|
||||||
|
|
||||||
Server.prototype._handleError = function(res, packageID, error) {
|
Server.prototype._handleError = function(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 +390,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);
|
||||||
|
|
Loading…
Reference in New Issue