mirror of https://github.com/status-im/metro.git
/ [node-haste] Remove support for asynchronous dependencies (`System.import`)
Summary: public Remove the unused feature for async dependencies / bundle layouts. We can bring it back later, if needed. Reviewed By: cpojer Differential Revision: D2916543 fb-gh-sync-id: 3a3890f10d7d275a4cb9371a6e9cace601a82b2c shipit-source-id: 3a3890f10d7d275a4cb9371a6e9cace601a82b2c
This commit is contained in:
parent
03a85ea8c2
commit
54e6576199
|
@ -13,7 +13,6 @@ const fs = require('fs');
|
|||
const path = require('path');
|
||||
const Promise = require('promise');
|
||||
const ProgressBar = require('progress');
|
||||
const BundlesLayout = require('../BundlesLayout');
|
||||
const Cache = require('../DependencyResolver/Cache');
|
||||
const Transformer = require('../JSTransformer');
|
||||
const Resolver = require('../Resolver');
|
||||
|
@ -126,13 +125,6 @@ class Bundler {
|
|||
cache: this._cache,
|
||||
});
|
||||
|
||||
this._bundlesLayout = new BundlesLayout({
|
||||
dependencyResolver: this._resolver,
|
||||
resetCache: opts.resetCache,
|
||||
cacheVersion: opts.cacheVersion,
|
||||
projectRoots: opts.projectRoots,
|
||||
});
|
||||
|
||||
this._transformer = new Transformer({
|
||||
projectRoots: opts.projectRoots,
|
||||
blacklistRE: opts.blacklistRE,
|
||||
|
@ -156,10 +148,6 @@ class Bundler {
|
|||
return this._cache.end();
|
||||
}
|
||||
|
||||
getLayout(main, isDev) {
|
||||
return this._bundlesLayout.generateLayout(main, isDev);
|
||||
}
|
||||
|
||||
bundle(options) {
|
||||
const {dev, isUnbundle, platform} = options;
|
||||
const moduleSystemDeps =
|
||||
|
|
|
@ -1,320 +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';
|
||||
|
||||
jest.dontMock('../index')
|
||||
.mock('fs');
|
||||
|
||||
var Promise = require('promise');
|
||||
var BundlesLayout = require('../index');
|
||||
var Resolver = require('../../Resolver');
|
||||
var loadCacheSync = require('../../DependencyResolver/Cache/lib/loadCacheSync');
|
||||
|
||||
describe('BundlesLayout', () => {
|
||||
function newBundlesLayout(options) {
|
||||
return new BundlesLayout(Object.assign({
|
||||
projectRoots: ['/root'],
|
||||
dependencyResolver: new Resolver(),
|
||||
}, options));
|
||||
}
|
||||
|
||||
describe('layout', () => {
|
||||
function isPolyfill() {
|
||||
return false;
|
||||
}
|
||||
|
||||
describe('getLayout', () => {
|
||||
function dep(path) {
|
||||
return {
|
||||
path: path,
|
||||
isPolyfill: isPolyfill,
|
||||
};
|
||||
}
|
||||
|
||||
pit('should bundle sync dependencies', () => {
|
||||
Resolver.prototype.getDependencies.mockImpl((path) => {
|
||||
switch (path) {
|
||||
case '/root/index.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/index.js'), dep('/root/a.js')],
|
||||
asyncDependencies: [],
|
||||
});
|
||||
case '/root/a.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/a.js')],
|
||||
asyncDependencies: [],
|
||||
});
|
||||
default:
|
||||
throw 'Undefined path: ' + path;
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout({resetCache: true})
|
||||
.getLayout('/root/index.js')
|
||||
.then(bundles =>
|
||||
expect(bundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js', '/root/a.js'],
|
||||
children: [],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
pit('should separate async dependencies into different bundle', () => {
|
||||
Resolver.prototype.getDependencies.mockImpl((path) => {
|
||||
switch (path) {
|
||||
case '/root/index.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/index.js')],
|
||||
asyncDependencies: [['/root/a.js']],
|
||||
});
|
||||
case '/root/a.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/a.js')],
|
||||
asyncDependencies: [],
|
||||
});
|
||||
default:
|
||||
throw 'Undefined path: ' + path;
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout({resetCache: true})
|
||||
.getLayout('/root/index.js')
|
||||
.then(bundles =>
|
||||
expect(bundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [{
|
||||
id:'bundle.0.1',
|
||||
modules: ['/root/a.js'],
|
||||
children: [],
|
||||
}],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
pit('separate async dependencies of async dependencies', () => {
|
||||
Resolver.prototype.getDependencies.mockImpl((path) => {
|
||||
switch (path) {
|
||||
case '/root/index.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/index.js')],
|
||||
asyncDependencies: [['/root/a.js']],
|
||||
});
|
||||
case '/root/a.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/a.js')],
|
||||
asyncDependencies: [['/root/b.js']],
|
||||
});
|
||||
case '/root/b.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/b.js')],
|
||||
asyncDependencies: [],
|
||||
});
|
||||
default:
|
||||
throw 'Undefined path: ' + path;
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout({resetCache: true})
|
||||
.getLayout('/root/index.js')
|
||||
.then(bundles =>
|
||||
expect(bundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/a.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1.2',
|
||||
modules: ['/root/b.js'],
|
||||
children: [],
|
||||
}],
|
||||
}],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
pit('separate bundle sync dependencies of async ones on same bundle', () => {
|
||||
Resolver.prototype.getDependencies.mockImpl((path) => {
|
||||
switch (path) {
|
||||
case '/root/index.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/index.js')],
|
||||
asyncDependencies: [['/root/a.js']],
|
||||
});
|
||||
case '/root/a.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/a.js'), dep('/root/b.js')],
|
||||
asyncDependencies: [],
|
||||
});
|
||||
case '/root/b.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/b.js')],
|
||||
asyncDependencies: [],
|
||||
});
|
||||
default:
|
||||
throw 'Undefined path: ' + path;
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout({resetCache: true})
|
||||
.getLayout('/root/index.js')
|
||||
.then(bundles =>
|
||||
expect(bundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/a.js', '/root/b.js'],
|
||||
children: [],
|
||||
}],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
pit('separate cache in which bundle is each dependency', () => {
|
||||
Resolver.prototype.getDependencies.mockImpl((path) => {
|
||||
switch (path) {
|
||||
case '/root/index.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/index.js'), dep('/root/a.js')],
|
||||
asyncDependencies: [],
|
||||
});
|
||||
case '/root/a.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/a.js')],
|
||||
asyncDependencies: [['/root/b.js']],
|
||||
});
|
||||
case '/root/b.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/b.js')],
|
||||
asyncDependencies: [],
|
||||
});
|
||||
default:
|
||||
throw 'Undefined path: ' + path;
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout({resetCache: true})
|
||||
.getLayout('/root/index.js')
|
||||
.then(bundles =>
|
||||
expect(bundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js', '/root/a.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/b.js'],
|
||||
children: [],
|
||||
}],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
pit('separate cache in which bundle is each dependency', () => {
|
||||
Resolver.prototype.getDependencies.mockImpl((path) => {
|
||||
switch (path) {
|
||||
case '/root/index.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/index.js'), dep('/root/a.js')],
|
||||
asyncDependencies: [['/root/b.js'], ['/root/c.js']],
|
||||
});
|
||||
case '/root/a.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/a.js')],
|
||||
asyncDependencies: [],
|
||||
});
|
||||
case '/root/b.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/b.js')],
|
||||
asyncDependencies: [['/root/d.js']],
|
||||
});
|
||||
case '/root/c.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/c.js')],
|
||||
asyncDependencies: [],
|
||||
});
|
||||
case '/root/d.js':
|
||||
return Promise.resolve({
|
||||
dependencies: [dep('/root/d.js')],
|
||||
asyncDependencies: [],
|
||||
});
|
||||
default:
|
||||
throw 'Undefined path: ' + path;
|
||||
}
|
||||
});
|
||||
|
||||
var layout = newBundlesLayout({resetCache: true});
|
||||
return layout.getLayout('/root/index.js').then(() => {
|
||||
expect(layout.getBundleIDForModule('/root/index.js')).toBe('bundle.0');
|
||||
expect(layout.getBundleIDForModule('/root/a.js')).toBe('bundle.0');
|
||||
expect(layout.getBundleIDForModule('/root/b.js')).toBe('bundle.0.1');
|
||||
expect(layout.getBundleIDForModule('/root/c.js')).toBe('bundle.0.2');
|
||||
expect(layout.getBundleIDForModule('/root/d.js')).toBe('bundle.0.1.3');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('cache', () => {
|
||||
beforeEach(() => {
|
||||
loadCacheSync.mockReturnValue({
|
||||
'/root/index.js': {
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/a.js'],
|
||||
children: [],
|
||||
}],
|
||||
},
|
||||
'/root/b.js': {
|
||||
id: 'bundle.2',
|
||||
modules: ['/root/b.js'],
|
||||
children: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
pit('should load layouts', () => {
|
||||
const layout = newBundlesLayout({ resetCache: false });
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
layout.getLayout('/root/index.js'),
|
||||
layout.getLayout('/root/b.js'),
|
||||
])
|
||||
.then(([layoutIndex, layoutB]) => {
|
||||
expect(layoutIndex).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/a.js'],
|
||||
children: [],
|
||||
}],
|
||||
});
|
||||
|
||||
expect(layoutB).toEqual({
|
||||
id: 'bundle.2',
|
||||
modules: ['/root/b.js'],
|
||||
children: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should load moduleToBundle map', () => {
|
||||
const layout = newBundlesLayout({ resetCache: false });
|
||||
|
||||
expect(layout.getBundleIDForModule('/root/index.js')).toBe('bundle.0');
|
||||
expect(layout.getBundleIDForModule('/root/a.js')).toBe('bundle.0.1');
|
||||
expect(layout.getBundleIDForModule('/root/b.js')).toBe('bundle.2');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,599 +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';
|
||||
|
||||
jest
|
||||
.autoMockOff()
|
||||
.mock('../../DependencyResolver/Cache')
|
||||
.mock('../../Activity');
|
||||
|
||||
const Promise = require('promise');
|
||||
const path = require('path');
|
||||
|
||||
jest.mock('fs');
|
||||
|
||||
var BundlesLayout = require('../index');
|
||||
var Cache = require('../../DependencyResolver/Cache');
|
||||
var Resolver = require('../../Resolver');
|
||||
var fs = require('fs');
|
||||
|
||||
describe('BundlesLayout', () => {
|
||||
var fileWatcher;
|
||||
|
||||
const polyfills = [
|
||||
'polyfills/prelude_dev.js',
|
||||
'polyfills/prelude.js',
|
||||
'polyfills/require.js',
|
||||
'polyfills/polyfills.js',
|
||||
'polyfills/console.js',
|
||||
'polyfills/error-guard.js',
|
||||
'polyfills/String.prototype.es6.js',
|
||||
'polyfills/Array.prototype.es6.js',
|
||||
'polyfills/Array.es6.js',
|
||||
'polyfills/Object.es7.js',
|
||||
'polyfills/babelHelpers.js',
|
||||
];
|
||||
const baseFs = getBaseFs();
|
||||
|
||||
beforeEach(() => {
|
||||
fileWatcher = {
|
||||
on: () => this,
|
||||
isWatchman: () => Promise.resolve(false)
|
||||
};
|
||||
});
|
||||
|
||||
describe('generate', () => {
|
||||
function newBundlesLayout() {
|
||||
const resolver = new Resolver({
|
||||
projectRoots: ['/root', '/' + __dirname.split('/')[1]],
|
||||
fileWatcher: fileWatcher,
|
||||
cache: new Cache(),
|
||||
assetExts: ['js', 'png'],
|
||||
assetRoots: ['/root'],
|
||||
});
|
||||
|
||||
return new BundlesLayout({
|
||||
dependencyResolver: resolver,
|
||||
resetCache: true,
|
||||
projectRoots: ['/root', '/' + __dirname.split('/')[1]],
|
||||
});
|
||||
}
|
||||
|
||||
function stripPolyfills(bundle) {
|
||||
return Promise
|
||||
.all(bundle.children.map(childModule => stripPolyfills(childModule)))
|
||||
.then(children => {
|
||||
const modules = bundle.modules
|
||||
.filter(moduleName => { // filter polyfills
|
||||
for (let p of polyfills) {
|
||||
if (moduleName.indexOf(p) !== -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return {
|
||||
id: bundle.id,
|
||||
modules: modules,
|
||||
children: children,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function setMockFilesystem(mockFs) {
|
||||
fs.__setMockFilesystem(Object.assign(mockFs, baseFs));
|
||||
}
|
||||
|
||||
pit('should bundle single-module app', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/`,
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
pit('should bundle dependant modules', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/
|
||||
require("xa");`,
|
||||
'a.js': `
|
||||
/**
|
||||
* @providesModule xa
|
||||
*/`,
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js', '/root/a.js'],
|
||||
children: [],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
pit('should split bundles for async dependencies', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/
|
||||
${'System.import'}("xa");`,
|
||||
'a.js': `
|
||||
/**,
|
||||
* @providesModule xa
|
||||
*/`,
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/a.js'],
|
||||
children: [],
|
||||
}],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
pit('should split into multiple bundles separate async dependencies', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/
|
||||
${'System.import'}("xa");
|
||||
${'System.import'}("xb");`,
|
||||
'a.js': `
|
||||
/**,
|
||||
* @providesModule xa
|
||||
*/`,
|
||||
'b.js': `
|
||||
/**
|
||||
* @providesModule xb
|
||||
*/`,
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [
|
||||
{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/a.js'],
|
||||
children: [],
|
||||
}, {
|
||||
id: 'bundle.0.2',
|
||||
modules: ['/root/b.js'],
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
pit('should fully traverse sync dependencies', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/
|
||||
require("xa");
|
||||
${'System.import'}("xb");`,
|
||||
'a.js': `
|
||||
/**,
|
||||
* @providesModule xa
|
||||
*/`,
|
||||
'b.js': `
|
||||
/**
|
||||
* @providesModule xb
|
||||
*/`,
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js', '/root/a.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/b.js'],
|
||||
children: [],
|
||||
}],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
pit('should include sync dependencies async dependencies might have', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/
|
||||
${'System.import'}("xa");`,
|
||||
'a.js': `
|
||||
/**,
|
||||
* @providesModule xa
|
||||
*/,
|
||||
require("xb");`,
|
||||
'b.js': `
|
||||
/**
|
||||
* @providesModule xb
|
||||
*/
|
||||
require("xc");`,
|
||||
'c.js': `
|
||||
/**
|
||||
* @providesModule xc
|
||||
*/`,
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/a.js', '/root/b.js', '/root/c.js'],
|
||||
children: [],
|
||||
}],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
pit('should allow duplicated dependencies across bundles', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/
|
||||
${'System.import'}("xa");
|
||||
${'System.import'}("xb");`,
|
||||
'a.js': `
|
||||
/**,
|
||||
* @providesModule xa
|
||||
*/,
|
||||
require("xc");`,
|
||||
'b.js': `
|
||||
/**
|
||||
* @providesModule xb
|
||||
*/
|
||||
require("xc");`,
|
||||
'c.js': `
|
||||
/**
|
||||
* @providesModule xc
|
||||
*/`,
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [
|
||||
{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/a.js', '/root/c.js'],
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 'bundle.0.2',
|
||||
modules: ['/root/b.js', '/root/c.js'],
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
pit('should put in separate bundles async dependencies of async dependencies', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/
|
||||
${'System.import'}("xa");`,
|
||||
'a.js': `
|
||||
/**,
|
||||
* @providesModule xa
|
||||
*/,
|
||||
${'System.import'}("xb");`,
|
||||
'b.js': `
|
||||
/**
|
||||
* @providesModule xb
|
||||
*/
|
||||
require("xc");`,
|
||||
'c.js': `
|
||||
/**
|
||||
* @providesModule xc
|
||||
*/`,
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [
|
||||
{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/a.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1.2',
|
||||
modules: ['/root/b.js', '/root/c.js'],
|
||||
children: [],
|
||||
}],
|
||||
},
|
||||
],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
pit('should put image dependencies into separate bundles', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/
|
||||
${'System.import'}("xa");`,
|
||||
'a.js':`
|
||||
/**,
|
||||
* @providesModule xa
|
||||
*/,
|
||||
require("./img.png");`,
|
||||
'img.png': '',
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/a.js', '/root/img.png'],
|
||||
children: [],
|
||||
}],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
pit('should put image dependencies across bundles', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/
|
||||
${'System.import'}("xa");
|
||||
${'System.import'}("xb");`,
|
||||
'a.js':`
|
||||
/**,
|
||||
* @providesModule xa
|
||||
*/,
|
||||
require("./img.png");`,
|
||||
'b.js':`
|
||||
/**,
|
||||
* @providesModule xb
|
||||
*/,
|
||||
require("./img.png");`,
|
||||
'img.png': '',
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [
|
||||
{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/a.js', '/root/img.png'],
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 'bundle.0.2',
|
||||
modules: ['/root/b.js', '/root/img.png'],
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
pit('could async require asset', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/
|
||||
${'System.import'}("./img.png");`,
|
||||
'img.png': '',
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/img.png'],
|
||||
children: [],
|
||||
}],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
pit('should include deprecated assets into separate bundles', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/
|
||||
${'System.import'}("xa");`,
|
||||
'a.js':`
|
||||
/**,
|
||||
* @providesModule xa
|
||||
*/,
|
||||
require("image!img");`,
|
||||
'img.png': '',
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/a.js', '/root/img.png'],
|
||||
children: [],
|
||||
}],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
pit('could async require deprecated asset', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/
|
||||
${'System.import'}("image!img");`,
|
||||
'img.png': '',
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/img.png'],
|
||||
children: [],
|
||||
}],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
pit('should put packages into bundles', () => {
|
||||
setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': `
|
||||
/**
|
||||
* @providesModule xindex
|
||||
*/
|
||||
${'System.import'}("aPackage");`,
|
||||
'aPackage': {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'aPackage',
|
||||
main: './main.js',
|
||||
browser: {
|
||||
'./main.js': './client.js',
|
||||
},
|
||||
}),
|
||||
'main.js': 'some other code',
|
||||
'client.js': 'some code',
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||
stripPolyfills(bundles).then(resolvedBundles =>
|
||||
expect(resolvedBundles).toEqual({
|
||||
id: 'bundle.0',
|
||||
modules: ['/root/index.js'],
|
||||
children: [{
|
||||
id: 'bundle.0.1',
|
||||
modules: ['/root/aPackage/client.js'],
|
||||
children: [],
|
||||
}],
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function getBaseFs() {
|
||||
const p = path.join(__dirname, '../../../Resolver/polyfills').substring(1);
|
||||
const root = {};
|
||||
let currentPath = root;
|
||||
|
||||
p.split('/').forEach(part => {
|
||||
const child = {};
|
||||
currentPath[part] = child;
|
||||
currentPath = child;
|
||||
});
|
||||
|
||||
polyfills.forEach(polyfill =>
|
||||
currentPath[polyfill.split('/')[1]] = ''
|
||||
);
|
||||
|
||||
return root;
|
||||
}
|
||||
});
|
|
@ -1,219 +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';
|
||||
|
||||
const Activity = require('../Activity');
|
||||
|
||||
const _ = require('underscore');
|
||||
const declareOpts = require('../lib/declareOpts');
|
||||
const fs = require('fs');
|
||||
const getCacheFilePath = require('../DependencyResolver/Cache/lib/getCacheFilePath');
|
||||
const loadCacheSync = require('../DependencyResolver/Cache/lib/loadCacheSync');
|
||||
const version = require('../../../../package.json').version;
|
||||
const path = require('path');
|
||||
const tmpdir = require('os').tmpDir();
|
||||
|
||||
const validateOpts = declareOpts({
|
||||
dependencyResolver: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
},
|
||||
resetCache: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
cacheVersion: {
|
||||
type: 'string',
|
||||
default: '1.0',
|
||||
},
|
||||
projectRoots: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const BUNDLE_PREFIX = 'bundle';
|
||||
|
||||
/**
|
||||
* Class that takes care of separating the graph of dependencies into
|
||||
* separate bundles
|
||||
*/
|
||||
class BundlesLayout {
|
||||
constructor(options) {
|
||||
const opts = validateOpts(options);
|
||||
this._resolver = opts.dependencyResolver;
|
||||
|
||||
// Cache in which bundle is each module.
|
||||
this._moduleToBundle = Object.create(null);
|
||||
|
||||
// Cache the bundles layouts for each entry point. This entries
|
||||
// are not evicted unless the user explicitly specifies so as
|
||||
// computing them is pretty expensive
|
||||
this._layouts = Object.create(null);
|
||||
|
||||
// TODO: watch for file creations and removals to update this caches
|
||||
|
||||
this._cacheFilePath = this._getCacheFilePath(opts);
|
||||
if (!opts.resetCache) {
|
||||
this._loadCacheSync(this._cacheFilePath);
|
||||
} else {
|
||||
this._persistCacheEventually();
|
||||
}
|
||||
}
|
||||
|
||||
getLayout(entryPath, isDev) {
|
||||
if (this._layouts[entryPath]) {
|
||||
return this._layouts[entryPath];
|
||||
}
|
||||
var currentBundleID = 0;
|
||||
const rootBundle = {
|
||||
id: BUNDLE_PREFIX + '.' + currentBundleID++,
|
||||
modules: [],
|
||||
children: [],
|
||||
};
|
||||
var pending = [{paths: [entryPath], bundle: rootBundle}];
|
||||
|
||||
this._layouts[entryPath] = promiseWhile(
|
||||
() => pending.length > 0,
|
||||
() => rootBundle,
|
||||
() => {
|
||||
const {paths, bundle} = pending.shift();
|
||||
|
||||
// pending sync dependencies we still need to explore for the current
|
||||
// pending dependency
|
||||
const pendingSyncDeps = paths;
|
||||
|
||||
// accum variable for sync dependencies of the current pending
|
||||
// dependency we're processing
|
||||
const syncDependencies = Object.create(null);
|
||||
|
||||
return promiseWhile(
|
||||
() => pendingSyncDeps.length > 0,
|
||||
() => {
|
||||
const dependencies = Object.keys(syncDependencies);
|
||||
if (dependencies.length > 0) {
|
||||
bundle.modules = dependencies;
|
||||
}
|
||||
|
||||
// persist changes to layouts
|
||||
this._persistCacheEventually();
|
||||
},
|
||||
index => {
|
||||
const pendingSyncDep = pendingSyncDeps.shift();
|
||||
return this._resolver
|
||||
.getDependencies(pendingSyncDep, {dev: isDev})
|
||||
.then(deps => {
|
||||
deps.dependencies.forEach(dep => {
|
||||
if (dep.path !== pendingSyncDep && !dep.isPolyfill()) {
|
||||
pendingSyncDeps.push(dep.path);
|
||||
}
|
||||
syncDependencies[dep.path] = true;
|
||||
this._moduleToBundle[dep.path] = bundle.id;
|
||||
});
|
||||
deps.asyncDependencies.forEach(asyncDeps => {
|
||||
const childBundle = {
|
||||
id: bundle.id + '.' + currentBundleID++,
|
||||
modules: [],
|
||||
children: [],
|
||||
};
|
||||
|
||||
bundle.children.push(childBundle);
|
||||
pending.push({paths: asyncDeps, bundle: childBundle});
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return this._layouts[entryPath];
|
||||
}
|
||||
|
||||
getBundleIDForModule(path) {
|
||||
return this._moduleToBundle[path];
|
||||
}
|
||||
|
||||
_loadCacheSync(cachePath) {
|
||||
const loadCacheId = Activity.startEvent('Loading bundles layout');
|
||||
const cacheOnDisk = loadCacheSync(cachePath);
|
||||
|
||||
// TODO: create single-module bundles for unexistent modules
|
||||
// TODO: remove modules that no longer exist
|
||||
Object.keys(cacheOnDisk).forEach(entryPath => {
|
||||
this._layouts[entryPath] = Promise.resolve(cacheOnDisk[entryPath]);
|
||||
this._fillModuleToBundleMap(cacheOnDisk[entryPath]);
|
||||
});
|
||||
|
||||
Activity.endEvent(loadCacheId);
|
||||
}
|
||||
|
||||
_fillModuleToBundleMap(bundle) {
|
||||
bundle.modules.forEach(module => this._moduleToBundle[module] = bundle.id);
|
||||
bundle.children.forEach(child => this._fillModuleToBundleMap(child));
|
||||
}
|
||||
|
||||
_persistCacheEventually() {
|
||||
_.debounce(
|
||||
this._persistCache.bind(this),
|
||||
2000,
|
||||
);
|
||||
}
|
||||
|
||||
_persistCache() {
|
||||
if (this._persisting !== null) {
|
||||
return this._persisting;
|
||||
}
|
||||
|
||||
this._persisting = Promise
|
||||
.all(_.values(this._layouts))
|
||||
.then(bundlesLayout => {
|
||||
var json = Object.create(null);
|
||||
Object.keys(this._layouts).forEach((p, i) =>
|
||||
json[p] = bundlesLayout[i]
|
||||
);
|
||||
|
||||
return Promise.denodeify(fs.writeFile)(
|
||||
this._cacheFilepath,
|
||||
JSON.stringify(json),
|
||||
);
|
||||
})
|
||||
.then(() => this._persisting = null);
|
||||
|
||||
return this._persisting;
|
||||
}
|
||||
|
||||
_getCacheFilePath(options) {
|
||||
return getCacheFilePath(
|
||||
tmpdir,
|
||||
'react-packager-bundles-cache-',
|
||||
version,
|
||||
options.projectRoots.join(',').split(path.sep).join('-'),
|
||||
options.cacheVersion || '0',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Runs the body Promise meanwhile the condition callback is satisfied.
|
||||
// Once it's not satisfied anymore, it returns what the results callback
|
||||
// indicates
|
||||
function promiseWhile(condition, result, body) {
|
||||
return _promiseWhile(condition, result, body, 0);
|
||||
}
|
||||
|
||||
function _promiseWhile(condition, result, body, index) {
|
||||
if (!condition()) {
|
||||
return Promise.resolve(result());
|
||||
}
|
||||
|
||||
return body(index).then(() =>
|
||||
_promiseWhile(condition, result, body, index + 1)
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = BundlesLayout;
|
|
@ -21,10 +21,6 @@ class AssetModule extends Module {
|
|||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
getAsyncDependencies() {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
read() {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
|
|
|
@ -24,10 +24,6 @@ class AssetModule_DEPRECATED extends Module {
|
|||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
getAsyncDependencies() {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
hash() {
|
||||
return `AssetModule_DEPRECATED : ${this.path}`;
|
||||
}
|
||||
|
|
|
@ -187,23 +187,6 @@ class ResolutionRequest {
|
|||
});
|
||||
}
|
||||
|
||||
getAsyncDependencies(response) {
|
||||
return Promise.resolve().then(() => {
|
||||
const mod = this._moduleCache.getModule(this._entryPath);
|
||||
return mod.getAsyncDependencies().then(bundles =>
|
||||
Promise
|
||||
.all(bundles.map(bundle =>
|
||||
Promise.all(bundle.map(
|
||||
dep => this.resolveDependency(mod, dep)
|
||||
))
|
||||
))
|
||||
.then(bs => bs.map(bundle => bundle.map(dep => dep.path)))
|
||||
);
|
||||
}).then(asyncDependencies => asyncDependencies.forEach(
|
||||
(dependency) => response.pushAsyncDependency(dependency)
|
||||
));
|
||||
}
|
||||
|
||||
_getAllMocks(pattern) {
|
||||
// Take all mocks in all the roots into account. This is necessary
|
||||
// because currently mocks are global: any module can be mocked by
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
class ResolutionResponse {
|
||||
constructor() {
|
||||
this.dependencies = [];
|
||||
this.asyncDependencies = [];
|
||||
this.mainModuleId = null;
|
||||
this.mocks = null;
|
||||
this.numPrependedDependencies = 0;
|
||||
|
@ -22,13 +21,11 @@ class ResolutionResponse {
|
|||
copy(properties) {
|
||||
const {
|
||||
dependencies = this.dependencies,
|
||||
asyncDependencies = this.asyncDependencies,
|
||||
mainModuleId = this.mainModuleId,
|
||||
mocks = this.mocks,
|
||||
} = properties;
|
||||
return Object.assign(new this.constructor(), this, {
|
||||
dependencies,
|
||||
asyncDependencies,
|
||||
mainModuleId,
|
||||
mocks,
|
||||
});
|
||||
|
@ -69,11 +66,6 @@ class ResolutionResponse {
|
|||
this.numPrependedDependencies += 1;
|
||||
}
|
||||
|
||||
pushAsyncDependency(dependency) {
|
||||
this._assertNotFinalized();
|
||||
this.asyncDependencies.push(dependency);
|
||||
}
|
||||
|
||||
setResolvedDependencyPairs(module, pairs) {
|
||||
this._assertNotFinalized();
|
||||
const hash = module.hash();
|
||||
|
|
|
@ -4068,40 +4068,6 @@ describe('DependencyGraph', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getAsyncDependencies', () => {
|
||||
pit('should get dependencies', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': [
|
||||
'/**',
|
||||
' * @providesModule index',
|
||||
' */',
|
||||
'System.' + 'import("a")',
|
||||
].join('\n'),
|
||||
'a.js': [
|
||||
'/**',
|
||||
' * @providesModule a',
|
||||
' */',
|
||||
].join('\n'),
|
||||
},
|
||||
});
|
||||
|
||||
var dgraph = new DependencyGraph({
|
||||
...defaults,
|
||||
roots: [root],
|
||||
});
|
||||
|
||||
return dgraph.getDependencies('/root/index.js')
|
||||
.then(response => response.finalize())
|
||||
.then(({ asyncDependencies }) => {
|
||||
expect(asyncDependencies).toEqual([
|
||||
['/root/a.js'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Extensions', () => {
|
||||
pit('supports custom file extensions', () => {
|
||||
var root = '/root';
|
||||
|
|
|
@ -185,14 +185,11 @@ class DependencyGraph {
|
|||
|
||||
const response = new ResolutionResponse();
|
||||
|
||||
return Promise.all([
|
||||
req.getOrderedDependencies(
|
||||
return req.getOrderedDependencies(
|
||||
response,
|
||||
this._opts.mocksPattern,
|
||||
recursive,
|
||||
),
|
||||
req.getAsyncDependencies(response),
|
||||
]).then(() => response);
|
||||
).then(() => response);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -91,14 +91,6 @@ class Module {
|
|||
);
|
||||
}
|
||||
|
||||
getAsyncDependencies() {
|
||||
return this._cache.get(
|
||||
this.path,
|
||||
'asyncDependencies',
|
||||
() => this.read().then(data => data.asyncDependencies)
|
||||
);
|
||||
}
|
||||
|
||||
invalidate() {
|
||||
this._cache.invalidate(this.path);
|
||||
}
|
||||
|
@ -146,7 +138,6 @@ class Module {
|
|||
return {
|
||||
id,
|
||||
dependencies: [],
|
||||
asyncDependencies: [],
|
||||
code: content,
|
||||
};
|
||||
} else {
|
||||
|
@ -155,13 +146,12 @@ class Module {
|
|||
? transformCode(this, content)
|
||||
: Promise.resolve({code: content});
|
||||
|
||||
return codePromise.then(({code, dependencies, asyncDependencies}) => {
|
||||
return codePromise.then(({code, dependencies}) => {
|
||||
const {deps} = this._extractor(code);
|
||||
return {
|
||||
id,
|
||||
code,
|
||||
dependencies: dependencies || deps.sync,
|
||||
asyncDependencies: asyncDependencies || deps.async,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -167,45 +167,6 @@ describe('Module', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Async Dependencies', () => {
|
||||
function expectAsyncDependenciesToEqual(expected) {
|
||||
const module = createModule();
|
||||
return module.getAsyncDependencies().then(actual =>
|
||||
expect(actual).toEqual(expected)
|
||||
);
|
||||
}
|
||||
|
||||
pit('should recognize single dependency', () => {
|
||||
mockIndexFile('System.' + 'import("dep1")');
|
||||
|
||||
return expectAsyncDependenciesToEqual([['dep1']]);
|
||||
});
|
||||
|
||||
pit('should parse single quoted dependencies', () => {
|
||||
mockIndexFile('System.' + 'import(\'dep1\')');
|
||||
|
||||
return expectAsyncDependenciesToEqual([['dep1']]);
|
||||
});
|
||||
|
||||
pit('should parse multiple async dependencies on the same module', () => {
|
||||
mockIndexFile([
|
||||
'System.' + 'import("dep1")',
|
||||
'System.' + 'import("dep2")',
|
||||
].join('\n'));
|
||||
|
||||
return expectAsyncDependenciesToEqual([
|
||||
['dep1'],
|
||||
['dep2'],
|
||||
]);
|
||||
});
|
||||
|
||||
pit('parse fine new lines', () => {
|
||||
mockIndexFile('System.' + 'import(\n"dep1"\n)');
|
||||
|
||||
return expectAsyncDependenciesToEqual([['dep1']]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Code', () => {
|
||||
const fileContents = 'arbitrary(code)';
|
||||
beforeEach(function() {
|
||||
|
@ -247,7 +208,7 @@ describe('Module', () => {
|
|||
const fileContents = 'arbitrary(code);';
|
||||
const exampleCode = `
|
||||
require('a');
|
||||
System.import('b');
|
||||
arbitrary.code('b');
|
||||
require('c');`;
|
||||
|
||||
beforeEach(function() {
|
||||
|
@ -268,12 +229,8 @@ describe('Module', () => {
|
|||
transformCode.mockReturnValue(Promise.resolve({code: exampleCode}));
|
||||
const module = createModule({transformCode});
|
||||
|
||||
return Promise.all([
|
||||
module.getDependencies(),
|
||||
module.getAsyncDependencies(),
|
||||
]).then(([dependencies, asyncDependencies]) => {
|
||||
return module.getDependencies().then(dependencies => {
|
||||
expect(dependencies).toEqual(['a', 'c']);
|
||||
expect(asyncDependencies).toEqual([['b']]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -285,29 +242,8 @@ describe('Module', () => {
|
|||
}));
|
||||
const module = createModule({transformCode});
|
||||
|
||||
return Promise.all([
|
||||
module.getDependencies(),
|
||||
module.getAsyncDependencies(),
|
||||
]).then(([dependencies, asyncDependencies]) => {
|
||||
return module.getDependencies().then(dependencies => {
|
||||
expect(dependencies).toEqual(mockedDependencies);
|
||||
expect(asyncDependencies).toEqual([['b']]);
|
||||
});
|
||||
});
|
||||
|
||||
pit('uses async dependencies that `transformCode` resolves to, instead of extracting them', () => {
|
||||
const mockedAsyncDependencies = [['foo', 'bar'], ['baz']];
|
||||
transformCode.mockReturnValue(Promise.resolve({
|
||||
code: exampleCode,
|
||||
asyncDependencies: mockedAsyncDependencies,
|
||||
}));
|
||||
const module = createModule({transformCode});
|
||||
|
||||
return Promise.all([
|
||||
module.getDependencies(),
|
||||
module.getAsyncDependencies(),
|
||||
]).then(([dependencies, asyncDependencies]) => {
|
||||
expect(dependencies).toEqual(['a', 'c']);
|
||||
expect(asyncDependencies).toEqual(mockedAsyncDependencies);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ const lineCommentRe = /\/\/.+(\n|$)/g;
|
|||
function extractRequires(code) {
|
||||
var deps = {
|
||||
sync: [],
|
||||
async: [],
|
||||
};
|
||||
|
||||
code = code
|
||||
|
@ -41,15 +40,6 @@ function extractRequires(code) {
|
|||
deps.sync.push(dep);
|
||||
return match;
|
||||
})
|
||||
// Parse async dependencies this module has. As opposed to what happens
|
||||
// with sync dependencies, when the module is required, it's async
|
||||
// dependencies won't be loaded into memory. This is deferred till the
|
||||
// code path gets to the import statement:
|
||||
// System.import('dep1')
|
||||
.replace(replacePatterns.SYSTEM_IMPORT_RE, (match, pre, quot, dep, post) => {
|
||||
deps.async.push([dep]);
|
||||
return match;
|
||||
});
|
||||
|
||||
return {code, deps};
|
||||
}
|
||||
|
|
|
@ -12,4 +12,3 @@
|
|||
exports.IMPORT_RE = /(\bimport\s+(?:[^'"]+\s+from\s+)??)(['"])([^'"]+)(\2)/g;
|
||||
exports.EXPORT_RE = /(\bexport\s+(?:[^'"]+\s+from\s+)??)(['"])([^'"]+)(\2)/g;
|
||||
exports.REQUIRE_RE = /(\brequire\s*?\(\s*?)(['"])([^'"]+)(\2\s*?\))/g;
|
||||
exports.SYSTEM_IMPORT_RE = /(\bSystem\.import\s*?\(\s*?)(['"])([^'"]+)(\2\s*?\))/g;
|
||||
|
|
|
@ -37,10 +37,9 @@ describe('Resolver', function() {
|
|||
});
|
||||
|
||||
class ResolutionResponseMock {
|
||||
constructor({dependencies, mainModuleId, asyncDependencies}) {
|
||||
constructor({dependencies, mainModuleId}) {
|
||||
this.dependencies = dependencies;
|
||||
this.mainModuleId = mainModuleId;
|
||||
this.asyncDependencies = asyncDependencies;
|
||||
}
|
||||
|
||||
prependDependency(dependency) {
|
||||
|
@ -80,7 +79,6 @@ describe('Resolver', function() {
|
|||
return Promise.resolve(new ResolutionResponseMock({
|
||||
dependencies: deps,
|
||||
mainModuleId: 'index',
|
||||
asyncDependencies: [],
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -180,7 +178,6 @@ describe('Resolver', function() {
|
|||
return Promise.resolve(new ResolutionResponseMock({
|
||||
dependencies: deps,
|
||||
mainModuleId: 'index',
|
||||
asyncDependencies: [],
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -208,7 +205,6 @@ describe('Resolver', function() {
|
|||
return Promise.resolve(new ResolutionResponseMock({
|
||||
dependencies: deps,
|
||||
mainModuleId: 'index',
|
||||
asyncDependencies: [],
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -637,7 +633,6 @@ describe('Resolver', function() {
|
|||
const resolutionResponse = new ResolutionResponseMock({
|
||||
dependencies: [module],
|
||||
mainModuleId: 'test module',
|
||||
asyncDependencies: [],
|
||||
});
|
||||
|
||||
resolutionResponse.getResolvedDependencyPairs = (module) => {
|
||||
|
|
|
@ -1,63 +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';
|
||||
|
||||
jest.dontMock('../loadBundles');
|
||||
jest.mock('NativeModules');
|
||||
|
||||
let loadBundles;
|
||||
let loadBundlesCalls;
|
||||
|
||||
describe('loadBundles', () => {
|
||||
beforeEach(() => {
|
||||
loadBundles = jest.genMockFunction();
|
||||
loadBundlesCalls = loadBundles.mock.calls;
|
||||
require('NativeModules').RCTBundlesLoader = {loadBundles};
|
||||
|
||||
require('../loadBundles');
|
||||
});
|
||||
|
||||
it('should set `global.__loadBundles` function when polyfill is initialized', () => {
|
||||
expect(typeof global.__loadBundles).toBe('function');
|
||||
});
|
||||
|
||||
it('should return a promise', () => {
|
||||
loadBundles.mockImpl((bundles, callback) => callback());
|
||||
expect(global.__loadBundles(['bundle.0']) instanceof Promise).toBeTruthy();
|
||||
});
|
||||
|
||||
pit('shouldn\'t request already loaded bundles', () => {
|
||||
loadBundles.mockImpl((bundles, callback) => callback());
|
||||
return global.__loadBundles(['bundle.0'])
|
||||
.then(() => global.__loadBundles(['bundle.0']))
|
||||
.then(() => expect(loadBundlesCalls.length).toBe(1));
|
||||
});
|
||||
|
||||
pit('shouldn\'n request inflight bundles', () => {
|
||||
loadBundles.mockImpl((bundles, callback) => {
|
||||
if (bundles.length === 1 && bundles[0] === 'bundle.0') {
|
||||
setTimeout(callback, 1000);
|
||||
} else if (bundles.length === 1 && bundles[0] === 'bundle.1') {
|
||||
setTimeout(callback, 500);
|
||||
}
|
||||
});
|
||||
|
||||
const promises = Promise.all([
|
||||
global.__loadBundles(['bundle.0']),
|
||||
global.__loadBundles(['bundle.0', 'bundle.1']),
|
||||
]).then(() => {
|
||||
expect(loadBundlesCalls.length).toBe(2);
|
||||
expect(loadBundlesCalls[0][0][0]).toBe('bundle.0');
|
||||
expect(loadBundlesCalls[1][0][0]).toBe('bundle.1');
|
||||
});
|
||||
|
||||
jest.runAllTimers();
|
||||
return promises;
|
||||
});
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
/* eslint strict:0 */
|
||||
|
||||
let loadBundlesOnNative = (bundles) =>
|
||||
new Promise((resolve) =>
|
||||
require('NativeModules').RCTBundlesLoader.loadBundles(bundles, resolve));
|
||||
|
||||
let requestedBundles = Object.create(null);
|
||||
|
||||
/**
|
||||
* Returns a promise that is fulfilled once all the indicated bundles are
|
||||
* loaded into memory and injected into the JS engine.
|
||||
* This invokation might need to go through the bridge
|
||||
* and run native code to load some, if not all, the requested bundles.
|
||||
* If all the bundles have already been loaded, the promise is resolved
|
||||
* immediately. Otherwise, we'll determine which bundles still need to get
|
||||
* loaded considering both, the ones already loaded, and the ones being
|
||||
* currently asynchronously loaded by other invocations to `__loadBundles`,
|
||||
* and return a promise that will get fulfilled once all these are finally
|
||||
* loaded.
|
||||
*
|
||||
* Note this function should only be invoked by generated code.
|
||||
*/
|
||||
global.__loadBundles = function(bundles) {
|
||||
// split bundles by whether they've already been requested or not
|
||||
const bundlesToRequest = bundles.filter(b => !requestedBundles[b]);
|
||||
|
||||
// keep a reference to the promise loading each bundle
|
||||
if (bundlesToRequest.length > 0) {
|
||||
const nativePromise = loadBundlesOnNative(bundlesToRequest);
|
||||
bundlesToRequest.forEach(b => requestedBundles[b] = nativePromise);
|
||||
}
|
||||
|
||||
return Promise.all(bundles.map(bundle => requestedBundles[bundle]));
|
||||
};
|
|
@ -1,71 +0,0 @@
|
|||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @emails oncall+jsinfra
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
jest.autoMockOff();
|
||||
jest.mock('../../../BundlesLayout');
|
||||
|
||||
const babel = require('babel-core');
|
||||
const BundlesLayout = require('../../../BundlesLayout');
|
||||
|
||||
const testData = {
|
||||
isolated: {
|
||||
input: 'System.' + 'import("moduleA");',
|
||||
output: 'loadBundles(["bundle.0"]);'
|
||||
},
|
||||
single: {
|
||||
input: 'System.' + 'import("moduleA").then(function (bundleA) {});',
|
||||
output: 'loadBundles(["bundle.0"]).then(function (bundleA) {});'
|
||||
},
|
||||
multiple: {
|
||||
input: [
|
||||
'Promise.all([',
|
||||
'System.' + 'import("moduleA"), System.' + 'import("moduleB"),',
|
||||
']).then(function (bundlesA, bundlesB) {});',
|
||||
].join('\n'),
|
||||
output: [
|
||||
'Promise.all([',
|
||||
'loadBundles(["bundle.0"]), loadBundles(["bundle.1"])',
|
||||
']).then(function (bundlesA, bundlesB) {});',
|
||||
].join(''),
|
||||
},
|
||||
};
|
||||
|
||||
describe('System.import', () => {
|
||||
let layout = new BundlesLayout();
|
||||
BundlesLayout.prototype.getBundleIDForModule.mockImpl(module => {
|
||||
switch (module) {
|
||||
case 'moduleA': return 'bundle.0';
|
||||
case 'moduleB': return 'bundle.1';
|
||||
}
|
||||
});
|
||||
|
||||
function transform(source) {
|
||||
return babel.transform(source, {
|
||||
plugins: [
|
||||
[require('../'), { bundlesLayout: layout }]
|
||||
],
|
||||
}).code;
|
||||
}
|
||||
|
||||
function test(data) {
|
||||
// transform and remove new lines
|
||||
expect(transform(data.input).replace(/(\r\n|\n|\r)/gm,'')).toEqual(data.output);
|
||||
}
|
||||
|
||||
it('should transform isolated `System.import`', () => {
|
||||
test(testData.isolated);
|
||||
});
|
||||
|
||||
it('should transform single `System.import`', () => {
|
||||
test(testData.single);
|
||||
});
|
||||
|
||||
it('should transform multiple `System.import`s', () => {
|
||||
test(testData.multiple);
|
||||
});
|
||||
});
|
|
@ -1,63 +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.
|
||||
*
|
||||
*/
|
||||
/*jslint node: true */
|
||||
'use strict';
|
||||
|
||||
const t = require('babel-types');
|
||||
|
||||
/**
|
||||
* Transforms asynchronous module importing into a function call
|
||||
* that includes which bundle needs to be loaded
|
||||
*
|
||||
* Transforms:
|
||||
*
|
||||
* System.import('moduleA')
|
||||
*
|
||||
* to:
|
||||
*
|
||||
* loadBundles('bundleA')
|
||||
*/
|
||||
module.exports = function() {
|
||||
return {
|
||||
visitor: {
|
||||
CallExpression: function (path, state) {
|
||||
if (!isAppropriateSystemImportCall(path.node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var bundlesLayout = state.opts.bundlesLayout;
|
||||
var bundleID = bundlesLayout.getBundleIDForModule(
|
||||
path.node.arguments[0].value
|
||||
);
|
||||
|
||||
var bundles = bundleID.split('.');
|
||||
bundles.splice(0, 1);
|
||||
bundles = bundles.map(function(id) {
|
||||
return t.stringLiteral('bundle.' + id);
|
||||
});
|
||||
|
||||
path.replaceWith(t.callExpression(
|
||||
t.identifier('loadBundles'),
|
||||
[t.arrayExpression(bundles)]
|
||||
));
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
function isAppropriateSystemImportCall(node) {
|
||||
return (
|
||||
node.callee.type === 'MemberExpression' &&
|
||||
node.callee.object.name === 'System' &&
|
||||
node.callee.property.name === 'import' &&
|
||||
node.arguments.length === 1 &&
|
||||
node.arguments[0].type === 'StringLiteral'
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue