Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions local-cli/bundle/buildBundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const TerminalReporter = require('../../packager/react-packager/src/lib/Terminal

const outputBundle = require('./output/bundle');
const path = require('path');
const fs = require('fs');
const saveAssets = require('./saveAssets');
const defaultAssetExts = require('../../packager/defaults').assetExts;

Expand Down Expand Up @@ -61,6 +62,12 @@ function buildBundle(args, config, output = outputBundle, packagerInstance) {
reporter: new TerminalReporter(),
};

if (typeof args.manifestFile === 'string') {
options.manifestReferrence = JSON.parse(
fs.readFileSync(args.manifestFile, 'utf-8')
);
}

packagerInstance = new Server(options);
shouldClosePackager = true;
}
Expand Down
6 changes: 6 additions & 0 deletions local-cli/bundle/bundleCommandLineArgs.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ module.exports = [
}, {
command: '--sourcemap-output [string]',
description: 'File name where to store the sourcemap file for resulting bundle, ex. /tmp/groups.map',
}, {
command: '--manifest-output [string]',
description: 'File name where to store the manifest file for bundle splitting, ex. ./output/base.manifest.json',
}, {
command: '--manifest-file [path]',
description: 'Path to the manifest file if want to split bundle, ex. ./output/base.manifest.json',
}, {
command: '--assets-dest [string]',
description: 'Directory name where to store assets referenced in the bundle',
Expand Down
23 changes: 19 additions & 4 deletions local-cli/bundle/output/bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ function saveBundleAndMap(
bundleOutput,
bundleEncoding: encoding,
dev,
sourcemapOutput
sourcemapOutput,
manifestOutput
} = options;

log('start');
Expand All @@ -58,14 +59,28 @@ function saveBundleAndMap(
Promise.all([writeBundle, writeMetadata])
.then(() => log('Done writing bundle output'));

const writeTasks = [writeBundle];

if (sourcemapOutput) {
log('Writing sourcemap output to:', sourcemapOutput);
const writeMap = writeFile(sourcemapOutput, codeWithMap.map, null);
writeMap.then(() => log('Done writing sourcemap output'));
return Promise.all([writeBundle, writeMetadata, writeMap]);
} else {
return writeBundle;
writeTasks.push(writeMetadata, writeMap);
}

if (manifestOutput) {
log('Writing manifest output to:', manifestOutput);
const manifest = createBundleManifest(bundle);
const writeManifest = writeFile(manifestOutput, manifest, null);
writeManifest.then(() => log('Done writing manifest output'));
writeTasks.push(writeManifest);
}

return Promise.all(writeTasks);
}

function createBundleManifest(bundle) {
return JSON.stringify(bundle.getManifest(), null, 2);
}

exports.build = buildBundle;
Expand Down
1 change: 1 addition & 0 deletions local-cli/bundle/types.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export type OutputOptions = {
dev?: boolean,
platform: string,
sourcemapOutput?: string,
manifestOutput?: string,
};

export type RequestOptions = {|
Expand Down
25 changes: 25 additions & 0 deletions packager/react-packager/src/Bundler/Bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class Bundle extends BundleBase {
sourceCode: code,
sourcePath: name + '.js',
meta: {preloaded: true},
isRequireCall: true,
}));
this._numRequireCalls += 1;
}
Expand Down Expand Up @@ -242,6 +243,30 @@ class Bundle extends BundleBase {
return eTag;
}

getManifest() {
const modules = this.getModules();
const manifest: {
modules: Object,
lastId: number,
} = {
modules: {},
lastId:0,
};
modules.forEach(module => {
// Filter out polyfills and requireCalls
if (module.name && !module.isPolyfill && !module.isRequireCall ) {
manifest.modules[module.name] = {
id: module.id,
};
}
if (typeof module.id === 'number' && typeof manifest.lastId === 'number') {
manifest.lastId = Math.max(manifest.lastId, module.id);
}
});

return manifest;
}

_getSourceMapFile() {
return this._sourceMapUrl
? this._sourceMapUrl.replace('.map', '.bundle')
Expand Down
96 changes: 95 additions & 1 deletion packager/react-packager/src/Bundler/__tests__/Bundle-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,84 @@ describe('Bundle', () => {
});
});

describe('manifest bundle', () => {
it('exclude polyfills and requireCalls in manifest', () => {
const modules = [
{
bundle: bundle,
id:0,
code: 'foo',
sourceCode: 'foo',
sourcePath: 'foo path',
name: 'path/foo.js',
},
{
bundle: bundle,
id:'prefix-1',
code: 'bar',
sourceCode: 'bar',
sourcePath: 'bar path',
name: 'path/bar.js',
},
{
bundle: bundle,
id:2,
code: 'foobar',
sourceCode: 'foobar',
sourcePath: 'foobar path',
name: 'path/foobar.js',
},
{
bundle: bundle,
id:3,
code: 'uname module',
sourceCode: 'uname module',
sourcePath: '',
},
{
bundle: bundle,
id:4,
code: 'polyfill module',
sourceCode: 'polyfill module',
sourcePath: '',
isPolyfill: true,
},
{
bundle: bundle,
id:5,
code: ';require(0);',
sourceCode: ';require(0);',
sourcePath: '',
isRequireCall: true,
}
];

return Promise.all(modules.map(module => addModule(module))).then(() => {
}).then(() => {
bundle.setMainModuleId(0);
bundle.finalize({
runBeforeMainModule: [],
runMainModule: true,
});
const manifest = bundle.getManifest();
expect(manifest).toEqual({
modules:{
'path/foo.js': {
id: 0,
},
'path/bar.js': {
id: 'prefix-1',
},
'path/foobar.js': {
id: 2
}
},
lastId:5
});
});
});
});

describe('main module id:', function() {
it('can save a main module ID', function() {
const id = 'arbitrary module ID';
Expand Down Expand Up @@ -482,7 +560,20 @@ function resolverFor(code, map) {
};
}

function addModule({bundle, code, sourceCode, sourcePath, map, virtual, polyfill, meta, id = ''}) {
function addModule({
bundle,
code,
sourceCode,
sourcePath,
map,
virtual,
polyfill,
meta,
id = '',
name,
isPolyfill,
isRequireCall,
}) {
return bundle.addModule(
resolverFor(code, map),
null,
Expand All @@ -496,6 +587,9 @@ function addModule({bundle, code, sourceCode, sourcePath, map, virtual, polyfill
meta,
virtual,
polyfill,
name,
isPolyfill,
isRequireCall,
}),
);
}
Expand Down
122 changes: 121 additions & 1 deletion packager/react-packager/src/Bundler/__tests__/Bundler-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jest
.mock('../../lib/declareOpts');

var Bundler = require('../');
var Bundle = require('../Bundle');
var Resolver = require('../../Resolver');
var sizeOf = require('image-size');
var fs = require('fs');
Expand All @@ -35,6 +36,7 @@ describe('Bundler', function() {

function createModule({
path,
name,
id,
dependencies,
isAsset,
Expand All @@ -46,7 +48,7 @@ describe('Bundler', function() {
path,
resolution,
getDependencies: () => Promise.resolve(dependencies),
getName: () => Promise.resolve(id),
getName: () => Promise.resolve(name ? name : id),
isJSON: () => isJSON,
isAsset: () => isAsset,
isPolyfill: () => isPolyfill,
Expand Down Expand Up @@ -335,4 +337,122 @@ describe('Bundler', function() {
]));
});
});


describe('bundle with manifest reference', () => {
var otherBundler;
var doBundle;
var getModuleIdInResolver;
var refModule = {path: '/root/ref.js', name: '/root/ref.js', dependencies: []};
var moduleFoo = {path: '/root/foo.js', name: '/root/foo.js', dependencies: []};
var moduleBar = {path: '/root/bar.js', name: '/root/bar.js', dependencies: []};
var manifestReferrence = {
modules: {
[refModule.name]: {
id: 456
}
},
lastId: 10,
};

beforeEach(function() {
fs.statSync.mockImplementation(function() {
return {
isDirectory: () => true
};
});

Resolver.mockImplementation(function() {
return {
getDependencies: (
entryFile,
options,
transformOptions,
onProgress,
getModuleId
) => {
getModuleIdInResolver = getModuleId;
return Promise.resolve({
mainModuleId: 0,
dependencies: [
createModule(refModule),
createModule(moduleFoo),
createModule(moduleBar),
createModule({path: '', name: 'aPolyfill.js', dependencies: [], isPolyfill:true}),
createModule({path: '', name: 'anotherPolyfill.js', dependencies: [], isPolyfill:true}),
],
transformOptions,
getModuleId,
getResolvedDependencyPairs: () => [],
})
},
getModuleSystemDependencies: () => [],
wrapModule: options => Promise.resolve(options),
};
});

Bundle.mockImplementation(function() {
const _modules = [];

return {
setRamGroups: jest.fn(),
setMainModuleId: jest.fn(),
finalize: jest.fn(),
getModules: () => {
return _modules;
},
addModule: (resolver, resolutionResponse, module, moduleTransport) => {
return _modules.push({...moduleTransport}) - 1;
}
}
});

otherBundler = new Bundler({
projectRoots: ['/root'],
assetServer: {
getAssetData: jest.fn(),
},
manifestReferrence,
});

doBundle = otherBundler.bundle({
entryFile: '/root/foo.js',
runBeforeMainModule: [],
runModule: true,
});
});

it('skip dependencies that exist in the manifest reference', function() {
return doBundle.then(bundle => {
const modulesNames = bundle.getModules().map(module => module.name);

expect(modulesNames.indexOf(refModule.name)).toBe(-1);
expect(modulesNames.indexOf(moduleFoo.name)).not.toBe(-1);
expect(modulesNames.indexOf(moduleBar.name)).not.toBe(-1);
});
});

it('skip polyfills if used manifest reference', function() {
return doBundle.then(bundle => {
expect(bundle.getModules().some(module => module.name == 'somePolyfill.js')).toBeFalsy();
expect(bundle.getModules().some(module => module.isPolyfill)).toBeFalsy();
});
});

it('get the moduleId from manifest reference that if module was already exists in the manifest', function() {
const refModuleReference = manifestReferrence.modules[refModule.name];
return doBundle.then(bundle => {
expect(getModuleIdInResolver(refModule)).toBe(refModuleReference.id);
expect(otherBundler._getModuleId(refModule)).toBe(refModuleReference.id);
});
});

it('create new moduleId should bigger than the lastId in the manifest', function() {
return doBundle.then(bundle => {
bundle.getModules().forEach(module => {
expect(module.id).toBeGreaterThan(manifestReferrence.lastId);
});
});
});
});
});
Loading