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);
|
||||
};
|
||||
|
||||
exports.buildPackage = function(options, packageOptions) {
|
||||
|
||||
// Renamed "package" to "bundle". But maintain backwards
|
||||
// compat.
|
||||
exports.buildPackage =
|
||||
exports.buildBundle = function(options, bundleOptions) {
|
||||
var server = createServer(options);
|
||||
return server.buildPackage(packageOptions)
|
||||
return server.buildBundle(bundleOptions)
|
||||
.then(function(p) {
|
||||
server.end();
|
||||
return p;
|
||||
});
|
||||
};
|
||||
|
||||
exports.buildPackageFromUrl = function(options, reqUrl) {
|
||||
exports.buildPackageFromUrl =
|
||||
exports.buildBundleFromUrl = function(options, reqUrl) {
|
||||
var server = createServer(options);
|
||||
return server.buildPackageFromUrl(reqUrl)
|
||||
return server.buildBundleFromUrl(reqUrl)
|
||||
.then(function(p) {
|
||||
server.end();
|
||||
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;
|
||||
|
||||
describe('Package', function() {
|
||||
describe('Bundle', function() {
|
||||
var ModuleTransport;
|
||||
var Package;
|
||||
var ppackage;
|
||||
var Bundle;
|
||||
var bundle;
|
||||
|
||||
beforeEach(function() {
|
||||
Package = require('../Package');
|
||||
Bundle = require('../Bundle');
|
||||
ModuleTransport = require('../../lib/ModuleTransport');
|
||||
ppackage = new Package('test_url');
|
||||
ppackage.getSourceMap = jest.genMockFn().mockImpl(function() {
|
||||
bundle = new Bundle('test_url');
|
||||
bundle.getSourceMap = jest.genMockFn().mockImpl(function() {
|
||||
return 'test-source-map';
|
||||
});
|
||||
});
|
||||
|
||||
describe('source package', function() {
|
||||
it('should create a package and get the source', function() {
|
||||
ppackage.addModule(new ModuleTransport({
|
||||
describe('source bundle', function() {
|
||||
it('should create a bundle and get the source', function() {
|
||||
bundle.addModule(new ModuleTransport({
|
||||
code: 'transformed foo;',
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path',
|
||||
}));
|
||||
ppackage.addModule(new ModuleTransport({
|
||||
bundle.addModule(new ModuleTransport({
|
||||
code: 'transformed bar;',
|
||||
sourceCode: 'source bar',
|
||||
sourcePath: 'bar path',
|
||||
}));
|
||||
|
||||
ppackage.finalize({});
|
||||
expect(ppackage.getSource()).toBe([
|
||||
bundle.finalize({});
|
||||
expect(bundle.getSource()).toBe([
|
||||
'transformed foo;',
|
||||
'transformed bar;',
|
||||
'\/\/@ sourceMappingURL=test_url'
|
||||
|
@ -48,7 +48,7 @@ describe('Package', 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({
|
||||
code: 'transformed foo;',
|
||||
sourceCode: 'source foo',
|
||||
|
@ -67,22 +67,22 @@ describe('Package', function() {
|
|||
].join('\n'));
|
||||
});
|
||||
|
||||
it('should create a package and add run module code', function() {
|
||||
ppackage.addModule(new ModuleTransport({
|
||||
it('should create a bundle and add run module code', function() {
|
||||
bundle.addModule(new ModuleTransport({
|
||||
code: 'transformed foo;',
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path'
|
||||
}));
|
||||
|
||||
ppackage.addModule(new ModuleTransport({
|
||||
bundle.addModule(new ModuleTransport({
|
||||
code: 'transformed bar;',
|
||||
sourceCode: 'source bar',
|
||||
sourcePath: 'bar path'
|
||||
}));
|
||||
|
||||
ppackage.setMainModuleId('foo');
|
||||
ppackage.finalize({runMainModule: true});
|
||||
expect(ppackage.getSource()).toBe([
|
||||
bundle.setMainModuleId('foo');
|
||||
bundle.finalize({runMainModule: true});
|
||||
expect(bundle.getSource()).toBe([
|
||||
'transformed foo;',
|
||||
'transformed bar;',
|
||||
';require("foo");',
|
||||
|
@ -100,19 +100,19 @@ describe('Package', function() {
|
|||
return minified;
|
||||
};
|
||||
|
||||
ppackage.addModule(new ModuleTransport({
|
||||
bundle.addModule(new ModuleTransport({
|
||||
code: 'transformed foo;',
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path'
|
||||
}));
|
||||
ppackage.finalize();
|
||||
expect(ppackage.getMinifiedSourceAndMap()).toBe(minified);
|
||||
bundle.finalize();
|
||||
expect(bundle.getMinifiedSourceAndMap()).toBe(minified);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sourcemap package', function() {
|
||||
describe('sourcemap bundle', function() {
|
||||
it('should create sourcemap', function() {
|
||||
var p = new Package('test_url');
|
||||
var p = new Bundle('test_url');
|
||||
p.addModule(new ModuleTransport({
|
||||
code: [
|
||||
'transformed foo',
|
||||
|
@ -143,11 +143,11 @@ describe('Package', function() {
|
|||
p.setMainModuleId('foo');
|
||||
p.finalize({runMainModule: true});
|
||||
var s = p.getSourceMap();
|
||||
expect(s).toEqual(genSourceMap(p._modules));
|
||||
expect(s).toEqual(genSourceMap(p.getModules()));
|
||||
});
|
||||
|
||||
it('should combine sourcemaps', function() {
|
||||
var p = new Package('test_url');
|
||||
var p = new Bundle('test_url');
|
||||
|
||||
p.addModule(new ModuleTransport({
|
||||
code: 'transformed foo;\n',
|
||||
|
@ -215,7 +215,7 @@ describe('Package', function() {
|
|||
|
||||
describe('getAssets()', function() {
|
||||
it('should save and return asset objects', function() {
|
||||
var p = new Package('test_url');
|
||||
var p = new Bundle('test_url');
|
||||
var asset1 = {};
|
||||
var asset2 = {};
|
||||
p.addAsset(asset1);
|
||||
|
@ -227,7 +227,7 @@ describe('Package', function() {
|
|||
|
||||
describe('getJSModulePaths()', function() {
|
||||
it('should return module paths', function() {
|
||||
var p = new Package('test_url');
|
||||
var p = new Bundle('test_url');
|
||||
p.addModule(new ModuleTransport({
|
||||
code: 'transformed foo;\n',
|
||||
sourceCode: 'source foo',
|
||||
|
@ -248,7 +248,7 @@ describe('Package', function() {
|
|||
|
||||
function genSourceMap(modules) {
|
||||
var sourceMapGen = new SourceMapGenerator({file: 'bundle.js', version: 3});
|
||||
var packageLineNo = 0;
|
||||
var bundleLineNo = 0;
|
||||
for (var i = 0; i < modules.length; i++) {
|
||||
var module = modules[i];
|
||||
var transformedCode = module.code;
|
||||
|
@ -259,7 +259,7 @@ describe('Package', function() {
|
|||
for (var t = 0; t < transformedCode.length; t++) {
|
||||
if (t === 0 || lastCharNewLine) {
|
||||
sourceMapGen.addMapping({
|
||||
generated: {line: packageLineNo + 1, column: 0},
|
||||
generated: {line: bundleLineNo + 1, column: 0},
|
||||
original: {line: transformedLineCount + 1, column: 0},
|
||||
source: sourcePath
|
||||
});
|
||||
|
@ -267,10 +267,10 @@ describe('Package', function() {
|
|||
lastCharNewLine = transformedCode[t] === '\n';
|
||||
if (lastCharNewLine) {
|
||||
transformedLineCount++;
|
||||
packageLineNo++;
|
||||
bundleLineNo++;
|
||||
}
|
||||
}
|
||||
packageLineNo++;
|
||||
bundleLineNo++;
|
||||
sourceMapGen.setSourceContent(
|
||||
sourcePath,
|
||||
sourceCode
|
|
@ -9,7 +9,7 @@
|
|||
'use strict';
|
||||
|
||||
jest
|
||||
.setMock('worker-farm', function() { return function() {};})
|
||||
.setMock('worker-farm', () => () => undefined)
|
||||
.dontMock('underscore')
|
||||
.dontMock('../../lib/ModuleTransport')
|
||||
.setMock('uglify-js')
|
||||
|
@ -19,11 +19,11 @@ jest.mock('fs');
|
|||
|
||||
var Promise = require('promise');
|
||||
|
||||
describe('Packager', function() {
|
||||
describe('Bundler', function() {
|
||||
var getDependencies;
|
||||
var wrapModule;
|
||||
var Packager;
|
||||
var packager;
|
||||
var Bundler;
|
||||
var bundler;
|
||||
var assetServer;
|
||||
var modules;
|
||||
|
||||
|
@ -37,7 +37,7 @@ describe('Packager', function() {
|
|||
};
|
||||
});
|
||||
|
||||
Packager = require('../');
|
||||
Bundler = require('../');
|
||||
|
||||
require('fs').statSync.mockImpl(function() {
|
||||
return {
|
||||
|
@ -53,7 +53,7 @@ describe('Packager', function() {
|
|||
getAssetData: jest.genMockFn(),
|
||||
};
|
||||
|
||||
packager = new Packager({
|
||||
bundler = new Bundler({
|
||||
projectRoots: ['/root'],
|
||||
assetServer: assetServer,
|
||||
});
|
||||
|
@ -118,8 +118,8 @@ describe('Packager', function() {
|
|||
});
|
||||
});
|
||||
|
||||
pit('create a package', function() {
|
||||
return packager.package('/root/foo.js', true, 'source_map_url')
|
||||
pit('create a bundle', function() {
|
||||
return bundler.bundle('/root/foo.js', true, 'source_map_url')
|
||||
.then(function(p) {
|
||||
expect(p.addModule.mock.calls[0][0]).toEqual({
|
||||
code: 'lol transformed /root/foo.js lol',
|
||||
|
@ -204,7 +204,7 @@ describe('Packager', function() {
|
|||
});
|
||||
|
||||
pit('gets the list of dependencies', function() {
|
||||
return packager.getDependencies('/root/foo.js', true)
|
||||
return bundler.getDependencies('/root/foo.js', true)
|
||||
.then(({dependencies}) => {
|
||||
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 ModuleCache = require('../ModuleCache');
|
||||
const Promise = require('promise');
|
||||
const _ = require('underscore');
|
||||
const crawl = require('../crawlers');
|
||||
const debug = require('debug')('DependencyGraph');
|
||||
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() {
|
||||
var server;
|
||||
var Packager;
|
||||
var Bundler;
|
||||
var FileWatcher;
|
||||
|
||||
var options = {
|
||||
|
@ -57,10 +57,10 @@ describe('processRequest', function() {
|
|||
var triggerFileChange;
|
||||
|
||||
beforeEach(function() {
|
||||
Packager = require('../../Packager');
|
||||
Bundler = require('../../Bundler');
|
||||
FileWatcher = require('../../FileWatcher');
|
||||
|
||||
Packager.prototype.package = jest.genMockFunction().mockImpl(function() {
|
||||
Bundler.prototype.bundle = jest.genMockFunction().mockImpl(function() {
|
||||
return Promise.resolve({
|
||||
getSource: function() {
|
||||
return 'this is the source';
|
||||
|
@ -81,7 +81,7 @@ describe('processRequest', function() {
|
|||
return this;
|
||||
};
|
||||
|
||||
Packager.prototype.invalidateFile = invalidatorFunc;
|
||||
Bundler.prototype.invalidateFile = invalidatorFunc;
|
||||
|
||||
var Server = require('../');
|
||||
server = new Server(options);
|
||||
|
@ -121,7 +121,7 @@ describe('processRequest', function() {
|
|||
'index.ios.includeRequire.bundle'
|
||||
).then(function(response) {
|
||||
expect(response).toEqual('this is the source');
|
||||
expect(Packager.prototype.package).toBeCalledWith(
|
||||
expect(Bundler.prototype.bundle).toBeCalledWith(
|
||||
'index.ios.js',
|
||||
true,
|
||||
'index.ios.includeRequire.map',
|
||||
|
@ -142,7 +142,7 @@ describe('processRequest', 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(
|
||||
requestHandler,
|
||||
'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() {
|
||||
var packageFunc = jest.genMockFunction();
|
||||
packageFunc
|
||||
pit('rebuilds the bundles that contain a file when that file is changed', function() {
|
||||
var bundleFunc = jest.genMockFunction();
|
||||
bundleFunc
|
||||
.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
getSource: function() {
|
||||
|
@ -173,7 +173,7 @@ describe('processRequest', function() {
|
|||
})
|
||||
);
|
||||
|
||||
Packager.prototype.package = packageFunc;
|
||||
Bundler.prototype.bundle = bundleFunc;
|
||||
|
||||
var Server = require('../../Server');
|
||||
server = new Server(options);
|
||||
|
@ -184,13 +184,13 @@ describe('processRequest', function() {
|
|||
return makeRequest(requestHandler, 'mybundle.bundle?runModule=true')
|
||||
.then(function(response) {
|
||||
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]);
|
||||
jest.runAllTimers();
|
||||
jest.runAllTimers();
|
||||
})
|
||||
.then(function() {
|
||||
expect(packageFunc.mock.calls.length).toBe(2);
|
||||
expect(bundleFunc.mock.calls.length).toBe(2);
|
||||
return makeRequest(requestHandler, 'mybundle.bundle?runModule=true')
|
||||
.then(function(response) {
|
||||
expect(response).toEqual('this is the rebuilt source');
|
||||
|
@ -259,12 +259,12 @@ describe('processRequest', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('buildPackage(options)', function() {
|
||||
it('Calls the packager with the correct args', function() {
|
||||
server.buildPackage({
|
||||
describe('buildBundle(options)', function() {
|
||||
it('Calls the bundler with the correct args', function() {
|
||||
server.buildBundle({
|
||||
entryFile: 'foo file'
|
||||
});
|
||||
expect(Packager.prototype.package).toBeCalledWith(
|
||||
expect(Bundler.prototype.bundle).toBeCalledWith(
|
||||
'foo file',
|
||||
true,
|
||||
undefined,
|
||||
|
@ -273,10 +273,10 @@ describe('processRequest', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('buildPackageFromUrl(options)', function() {
|
||||
it('Calls the packager with the correct args', function() {
|
||||
server.buildPackageFromUrl('/path/to/foo.bundle?dev=false&runModule=false');
|
||||
expect(Packager.prototype.package).toBeCalledWith(
|
||||
describe('buildBundleFromUrl(options)', function() {
|
||||
it('Calls the bundler with the correct args', function() {
|
||||
server.buildBundleFromUrl('/path/to/foo.bundle?dev=false&runModule=false');
|
||||
expect(Bundler.prototype.bundle).toBeCalledWith(
|
||||
'path/to/foo.js',
|
||||
false,
|
||||
'/path/to/foo.map',
|
||||
|
|
|
@ -12,7 +12,7 @@ var url = require('url');
|
|||
var path = require('path');
|
||||
var declareOpts = require('../lib/declareOpts');
|
||||
var FileWatcher = require('../FileWatcher');
|
||||
var Packager = require('../Packager');
|
||||
var Bundler = require('../Bundler');
|
||||
var Activity = require('../Activity');
|
||||
var AssetServer = require('../AssetServer');
|
||||
var Promise = require('promise');
|
||||
|
@ -68,7 +68,7 @@ function Server(options) {
|
|||
var opts = validateOpts(options);
|
||||
|
||||
this._projectRoots = opts.projectRoots;
|
||||
this._packages = Object.create(null);
|
||||
this._bundles = Object.create(null);
|
||||
this._changeWatchers = [];
|
||||
|
||||
var assetGlobs = opts.assetExts.map(function(ext) {
|
||||
|
@ -105,40 +105,40 @@ function Server(options) {
|
|||
assetExts: opts.assetExts,
|
||||
});
|
||||
|
||||
var packagerOpts = Object.create(opts);
|
||||
packagerOpts.fileWatcher = this._fileWatcher;
|
||||
packagerOpts.assetServer = this._assetServer;
|
||||
this._packager = new Packager(packagerOpts);
|
||||
var bundlerOpts = Object.create(opts);
|
||||
bundlerOpts.fileWatcher = this._fileWatcher;
|
||||
bundlerOpts.assetServer = this._assetServer;
|
||||
this._bundler = new Bundler(bundlerOpts);
|
||||
|
||||
var onFileChange = this._onFileChange.bind(this);
|
||||
this._fileWatcher.on('all', onFileChange);
|
||||
|
||||
var self = this;
|
||||
this._debouncedFileChangeHandler = _.debounce(function(filePath) {
|
||||
self._rebuildPackages(filePath);
|
||||
self._rebuildBundles(filePath);
|
||||
self._informChangeWatchers();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
Server.prototype._onFileChange = function(type, filepath, root) {
|
||||
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
|
||||
// we rebuild the packages.
|
||||
// we rebuild the bundles.
|
||||
this._debouncedFileChangeHandler(absPath);
|
||||
};
|
||||
|
||||
Server.prototype._rebuildPackages = function() {
|
||||
var buildPackage = this.buildPackage.bind(this);
|
||||
var packages = this._packages;
|
||||
Server.prototype._rebuildBundles = function() {
|
||||
var buildBundle = this.buildBundle.bind(this);
|
||||
var bundles = this._bundles;
|
||||
|
||||
Object.keys(packages).forEach(function(optionsJson) {
|
||||
Object.keys(bundles).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() {
|
||||
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.
|
||||
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.
|
||||
p.getSource({
|
||||
inlineSourceMap: options.inlineSourceMap,
|
||||
|
@ -147,7 +147,7 @@ Server.prototype._rebuildPackages = function() {
|
|||
return p;
|
||||
});
|
||||
});
|
||||
return packages[optionsJson];
|
||||
return bundles[optionsJson];
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -168,11 +168,11 @@ Server.prototype._informChangeWatchers = function() {
|
|||
Server.prototype.end = function() {
|
||||
Promise.all([
|
||||
this._fileWatcher.end(),
|
||||
this._packager.kill(),
|
||||
this._bundler.kill(),
|
||||
]);
|
||||
};
|
||||
|
||||
var packageOpts = declareOpts({
|
||||
var bundleOpts = declareOpts({
|
||||
sourceMapUrl: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
|
@ -199,10 +199,10 @@ var packageOpts = declareOpts({
|
|||
},
|
||||
});
|
||||
|
||||
Server.prototype.buildPackage = function(options) {
|
||||
var opts = packageOpts(options);
|
||||
Server.prototype.buildBundle = function(options) {
|
||||
var opts = bundleOpts(options);
|
||||
|
||||
return this._packager.package(
|
||||
return this._bundler.bundle(
|
||||
opts.entryFile,
|
||||
opts.runModule,
|
||||
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);
|
||||
return this.buildPackage(options);
|
||||
return this.buildBundle(options);
|
||||
};
|
||||
|
||||
Server.prototype.getDependencies = function(main) {
|
||||
return this._packager.getDependencies(main);
|
||||
return this._bundler.getDependencies(main);
|
||||
};
|
||||
|
||||
Server.prototype._processDebugRequest = function(reqUrl, res) {
|
||||
|
@ -224,13 +224,13 @@ Server.prototype._processDebugRequest = function(reqUrl, res) {
|
|||
var pathname = url.parse(reqUrl).pathname;
|
||||
var parts = pathname.split('/').filter(Boolean);
|
||||
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>';
|
||||
res.end(ret);
|
||||
} else if (parts[1] === 'packages') {
|
||||
ret += '<h1> Cached Packages </h1>';
|
||||
Promise.all(Object.keys(this._packages).map(function(optionsJson) {
|
||||
return this._packages[optionsJson].then(function(p) {
|
||||
} else if (parts[1] === 'bundles') {
|
||||
ret += '<h1> Cached Bundles </h1>';
|
||||
Promise.all(Object.keys(this._bundles).map(function(optionsJson) {
|
||||
return this._bundles[optionsJson].then(function(p) {
|
||||
ret += '<div><h2>' + optionsJson + '</h2>';
|
||||
ret += p.getDebugInfo();
|
||||
});
|
||||
|
@ -244,7 +244,7 @@ Server.prototype._processDebugRequest = function(reqUrl, res) {
|
|||
);
|
||||
} else if (parts[1] === 'graph'){
|
||||
ret += '<h1> Dependency Graph </h2>';
|
||||
ret += this._packager.getGraphDebugInfo();
|
||||
ret += this._bundler.getGraphDebugInfo();
|
||||
res.end(ret);
|
||||
} else {
|
||||
res.writeHead('404');
|
||||
|
@ -352,9 +352,9 @@ Server.prototype.processRequest = function(req, res, next) {
|
|||
var startReqEventId = Activity.startEvent('request:' + req.url);
|
||||
var options = getOptionsFromUrl(req.url);
|
||||
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(
|
||||
function(p) {
|
||||
if (requestType === 'bundle') {
|
||||
|
@ -376,7 +376,7 @@ Server.prototype.processRequest = function(req, res, next) {
|
|||
).done();
|
||||
};
|
||||
|
||||
Server.prototype._handleError = function(res, packageID, error) {
|
||||
Server.prototype._handleError = function(res, bundleID, error) {
|
||||
res.writeHead(error.status || 500, {
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
});
|
||||
|
@ -390,7 +390,7 @@ Server.prototype._handleError = function(res, packageID, error) {
|
|||
res.end(JSON.stringify(error));
|
||||
|
||||
if (error.type === 'NotFoundError') {
|
||||
delete this._packages[packageID];
|
||||
delete this._bundles[bundleID];
|
||||
}
|
||||
} else {
|
||||
console.error(error.stack || error);
|
||||
|
|
Loading…
Reference in New Issue