mirror of
https://github.com/status-im/metro.git
synced 2025-03-02 19:50:28 +00:00
Updates from Mon Aug 31st.
This commit is contained in:
commit
0621018da6
@ -85,6 +85,10 @@ function connectToDebuggerProxy() {
|
|||||||
|
|
||||||
ws.onmessage = function(message) {
|
ws.onmessage = function(message) {
|
||||||
var object = JSON.parse(message.data);
|
var object = JSON.parse(message.data);
|
||||||
|
if (!object.method) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var sendReply = function(result) {
|
var sendReply = function(result) {
|
||||||
ws.send(JSON.stringify({replyID: object.id, result: result}));
|
ws.send(JSON.stringify({replyID: object.id, result: result}));
|
||||||
};
|
};
|
||||||
|
12
react-packager/src/Bundler/index.js
vendored
12
react-packager/src/Bundler/index.js
vendored
@ -13,6 +13,7 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const Promise = require('promise');
|
const Promise = require('promise');
|
||||||
const ProgressBar = require('progress');
|
const ProgressBar = require('progress');
|
||||||
|
const BundlesLayout = require('../BundlesLayout');
|
||||||
const Cache = require('../Cache');
|
const Cache = require('../Cache');
|
||||||
const Transformer = require('../JSTransformer');
|
const Transformer = require('../JSTransformer');
|
||||||
const DependencyResolver = require('../DependencyResolver');
|
const DependencyResolver = require('../DependencyResolver');
|
||||||
@ -104,6 +105,13 @@ class Bundler {
|
|||||||
cache: this._cache,
|
cache: this._cache,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._bundlesLayout = new BundlesLayout({
|
||||||
|
dependencyResolver: this._resolver,
|
||||||
|
resetCache: opts.resetCache,
|
||||||
|
cacheVersion: opts.cacheVersion,
|
||||||
|
projectRoots: opts.projectRoots,
|
||||||
|
});
|
||||||
|
|
||||||
this._transformer = new Transformer({
|
this._transformer = new Transformer({
|
||||||
projectRoots: opts.projectRoots,
|
projectRoots: opts.projectRoots,
|
||||||
blacklistRE: opts.blacklistRE,
|
blacklistRE: opts.blacklistRE,
|
||||||
@ -120,6 +128,10 @@ class Bundler {
|
|||||||
return this._cache.end();
|
return this._cache.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLayout(main, isDev) {
|
||||||
|
return this._bundlesLayout.generateLayout(main, isDev);
|
||||||
|
}
|
||||||
|
|
||||||
bundle(main, runModule, sourceMapUrl, isDev, platform) {
|
bundle(main, runModule, sourceMapUrl, isDev, platform) {
|
||||||
const bundle = new Bundle(sourceMapUrl);
|
const bundle = new Bundle(sourceMapUrl);
|
||||||
const findEventId = Activity.startEvent('find dependencies');
|
const findEventId = Activity.startEvent('find dependencies');
|
||||||
|
@ -8,31 +8,35 @@
|
|||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
jest
|
jest.dontMock('../index');
|
||||||
.dontMock('../index');
|
jest.mock('fs');
|
||||||
|
|
||||||
const Promise = require('promise');
|
const Promise = require('promise');
|
||||||
|
|
||||||
describe('BundlesLayout', () => {
|
describe('BundlesLayout', () => {
|
||||||
var BundlesLayout;
|
let BundlesLayout;
|
||||||
var DependencyResolver;
|
let DependencyResolver;
|
||||||
|
let loadCacheSync;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
BundlesLayout = require('../index');
|
BundlesLayout = require('../index');
|
||||||
DependencyResolver = require('../../DependencyResolver');
|
DependencyResolver = require('../../DependencyResolver');
|
||||||
|
loadCacheSync = require('../../lib/loadCacheSync');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('generate', () => {
|
function newBundlesLayout(options) {
|
||||||
function newBundlesLayout() {
|
return new BundlesLayout(Object.assign({
|
||||||
return new BundlesLayout({
|
projectRoots: ['/root'],
|
||||||
dependencyResolver: new DependencyResolver(),
|
dependencyResolver: new DependencyResolver(),
|
||||||
});
|
}, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe('layout', () => {
|
||||||
function isPolyfill() {
|
function isPolyfill() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe('getLayout', () => {
|
||||||
function dep(path) {
|
function dep(path) {
|
||||||
return {
|
return {
|
||||||
path: path,
|
path: path,
|
||||||
@ -58,7 +62,9 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout({resetCache: true})
|
||||||
|
.getLayout('/root/index.js')
|
||||||
|
.then(bundles =>
|
||||||
expect(bundles).toEqual({
|
expect(bundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
modules: ['/root/index.js', '/root/a.js'],
|
modules: ['/root/index.js', '/root/a.js'],
|
||||||
@ -85,7 +91,9 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout({resetCache: true})
|
||||||
|
.getLayout('/root/index.js')
|
||||||
|
.then(bundles =>
|
||||||
expect(bundles).toEqual({
|
expect(bundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
modules: ['/root/index.js'],
|
modules: ['/root/index.js'],
|
||||||
@ -121,7 +129,9 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout({resetCache: true})
|
||||||
|
.getLayout('/root/index.js')
|
||||||
|
.then(bundles =>
|
||||||
expect(bundles).toEqual({
|
expect(bundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
modules: ['/root/index.js'],
|
modules: ['/root/index.js'],
|
||||||
@ -161,7 +171,9 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout({resetCache: true})
|
||||||
|
.getLayout('/root/index.js')
|
||||||
|
.then(bundles =>
|
||||||
expect(bundles).toEqual({
|
expect(bundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
modules: ['/root/index.js'],
|
modules: ['/root/index.js'],
|
||||||
@ -197,8 +209,10 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(
|
return newBundlesLayout({resetCache: true})
|
||||||
bundles => expect(bundles).toEqual({
|
.getLayout('/root/index.js')
|
||||||
|
.then(bundles =>
|
||||||
|
expect(bundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
modules: ['/root/index.js', '/root/a.js'],
|
modules: ['/root/index.js', '/root/a.js'],
|
||||||
children: [{
|
children: [{
|
||||||
@ -243,8 +257,8 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var layout = newBundlesLayout();
|
var layout = newBundlesLayout({resetCache: true});
|
||||||
return layout.generateLayout(['/root/index.js']).then(() => {
|
return layout.getLayout('/root/index.js').then(() => {
|
||||||
expect(layout.getBundleIDForModule('/root/index.js')).toBe('bundle.0');
|
expect(layout.getBundleIDForModule('/root/index.js')).toBe('bundle.0');
|
||||||
expect(layout.getBundleIDForModule('/root/a.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/b.js')).toBe('bundle.0.1');
|
||||||
@ -254,3 +268,60 @@ describe('BundlesLayout', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -75,7 +75,11 @@ describe('BundlesLayout', () => {
|
|||||||
assetRoots: ['/root'],
|
assetRoots: ['/root'],
|
||||||
});
|
});
|
||||||
|
|
||||||
return new BundlesLayout({dependencyResolver: resolver});
|
return new BundlesLayout({
|
||||||
|
dependencyResolver: resolver,
|
||||||
|
resetCache: true,
|
||||||
|
projectRoots: ['/root', '/' + __dirname.split('/')[1]],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function stripPolyfills(bundle) {
|
function stripPolyfills(bundle) {
|
||||||
@ -114,7 +118,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
@ -140,7 +144,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
@ -166,7 +170,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
@ -201,7 +205,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
@ -242,7 +246,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
@ -282,7 +286,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
@ -323,7 +327,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
@ -370,7 +374,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
@ -408,7 +412,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
@ -446,7 +450,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
@ -480,7 +484,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
@ -512,7 +516,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
@ -539,7 +543,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
@ -576,7 +580,7 @@ describe('BundlesLayout', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles =>
|
return newBundlesLayout().getLayout('/root/index.js').then(bundles =>
|
||||||
stripPolyfills(bundles).then(resolvedBundles =>
|
stripPolyfills(bundles).then(resolvedBundles =>
|
||||||
expect(resolvedBundles).toEqual({
|
expect(resolvedBundles).toEqual({
|
||||||
id: 'bundle.0',
|
id: 'bundle.0',
|
||||||
|
106
react-packager/src/BundlesLayout/index.js
vendored
106
react-packager/src/BundlesLayout/index.js
vendored
@ -8,14 +8,33 @@
|
|||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const Activity = require('../Activity');
|
||||||
|
|
||||||
const _ = require('underscore');
|
const _ = require('underscore');
|
||||||
const declareOpts = require('../lib/declareOpts');
|
const declareOpts = require('../lib/declareOpts');
|
||||||
|
const fs = require('fs');
|
||||||
|
const getCacheFilePath = require('../lib/getCacheFilePath');
|
||||||
|
const loadCacheSync = require('../lib/loadCacheSync');
|
||||||
|
const version = require('../../../../package.json').version;
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
const validateOpts = declareOpts({
|
const validateOpts = declareOpts({
|
||||||
dependencyResolver: {
|
dependencyResolver: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
resetCache: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
cacheVersion: {
|
||||||
|
type: 'string',
|
||||||
|
default: '1.0',
|
||||||
|
},
|
||||||
|
projectRoots: {
|
||||||
|
type: 'array',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const BUNDLE_PREFIX = 'bundle';
|
const BUNDLE_PREFIX = 'bundle';
|
||||||
@ -29,19 +48,37 @@ class BundlesLayout {
|
|||||||
const opts = validateOpts(options);
|
const opts = validateOpts(options);
|
||||||
this._resolver = opts.dependencyResolver;
|
this._resolver = opts.dependencyResolver;
|
||||||
|
|
||||||
|
// Cache in which bundle is each module.
|
||||||
this._moduleToBundle = Object.create(null);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
generateLayout(entryPaths, isDev) {
|
getLayout(entryPath, isDev) {
|
||||||
|
if (this._layouts[entryPath]) {
|
||||||
|
return this._layouts[entryPath];
|
||||||
|
}
|
||||||
var currentBundleID = 0;
|
var currentBundleID = 0;
|
||||||
const rootBundle = {
|
const rootBundle = {
|
||||||
id: BUNDLE_PREFIX + '.' + currentBundleID++,
|
id: BUNDLE_PREFIX + '.' + currentBundleID++,
|
||||||
modules: [],
|
modules: [],
|
||||||
children: [],
|
children: [],
|
||||||
};
|
};
|
||||||
var pending = [{paths: entryPaths, bundle: rootBundle}];
|
var pending = [{paths: [entryPath], bundle: rootBundle}];
|
||||||
|
|
||||||
return promiseWhile(
|
this._layouts[entryPath] = promiseWhile(
|
||||||
() => pending.length > 0,
|
() => pending.length > 0,
|
||||||
() => rootBundle,
|
() => rootBundle,
|
||||||
() => {
|
() => {
|
||||||
@ -62,6 +99,9 @@ class BundlesLayout {
|
|||||||
if (dependencies.length > 0) {
|
if (dependencies.length > 0) {
|
||||||
bundle.modules = dependencies;
|
bundle.modules = dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// persist changes to layouts
|
||||||
|
this._persistCacheEventually();
|
||||||
},
|
},
|
||||||
index => {
|
index => {
|
||||||
const pendingSyncDep = pendingSyncDeps.shift();
|
const pendingSyncDep = pendingSyncDeps.shift();
|
||||||
@ -90,11 +130,71 @@ class BundlesLayout {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return this._layouts[entryPath];
|
||||||
}
|
}
|
||||||
|
|
||||||
getBundleIDForModule(path) {
|
getBundleIDForModule(path) {
|
||||||
return this._moduleToBundle[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(
|
||||||
|
'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.
|
// Runs the body Promise meanwhile the condition callback is satisfied.
|
||||||
|
@ -11,7 +11,9 @@
|
|||||||
jest
|
jest
|
||||||
.dontMock('underscore')
|
.dontMock('underscore')
|
||||||
.dontMock('absolute-path')
|
.dontMock('absolute-path')
|
||||||
.dontMock('../');
|
.dontMock('../')
|
||||||
|
.dontMock('../../lib/loadCacheSync')
|
||||||
|
.dontMock('../../lib/getCacheFilePath');
|
||||||
|
|
||||||
jest
|
jest
|
||||||
.mock('os')
|
.mock('os')
|
||||||
|
57
react-packager/src/Cache/index.js
vendored
57
react-packager/src/Cache/index.js
vendored
@ -8,17 +8,17 @@
|
|||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var _ = require('underscore');
|
const Promise = require('promise');
|
||||||
var crypto = require('crypto');
|
const _ = require('underscore');
|
||||||
var declareOpts = require('../lib/declareOpts');
|
const declareOpts = require('../lib/declareOpts');
|
||||||
var fs = require('fs');
|
const fs = require('fs');
|
||||||
var isAbsolutePath = require('absolute-path');
|
const getCacheFilePath = require('../lib/getCacheFilePath');
|
||||||
var path = require('path');
|
const isAbsolutePath = require('absolute-path');
|
||||||
var Promise = require('promise');
|
const loadCacheSync = require('../lib/loadCacheSync');
|
||||||
var tmpdir = require('os').tmpDir();
|
const path = require('path');
|
||||||
var version = require('../../../../package.json').version;
|
const version = require('../../../../package.json').version;
|
||||||
|
|
||||||
var validateOpts = declareOpts({
|
const validateOpts = declareOpts({
|
||||||
resetCache: {
|
resetCache: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
@ -164,21 +164,7 @@ class Cache {
|
|||||||
|
|
||||||
_loadCacheSync(cachePath) {
|
_loadCacheSync(cachePath) {
|
||||||
var ret = Object.create(null);
|
var ret = Object.create(null);
|
||||||
if (!fs.existsSync(cachePath)) {
|
var cacheOnDisk = loadCacheSync(cachePath);
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cacheOnDisk;
|
|
||||||
try {
|
|
||||||
cacheOnDisk = JSON.parse(fs.readFileSync(cachePath));
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof SyntaxError) {
|
|
||||||
console.warn('Unable to parse cache file. Will clear and continue.');
|
|
||||||
fs.unlinkSync(cachePath);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter outdated cache and convert to promises.
|
// Filter outdated cache and convert to promises.
|
||||||
Object.keys(cacheOnDisk).forEach(key => {
|
Object.keys(cacheOnDisk).forEach(key => {
|
||||||
@ -203,20 +189,13 @@ class Cache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_getCacheFilePath(options) {
|
_getCacheFilePath(options) {
|
||||||
var hash = crypto.createHash('md5');
|
return getCacheFilePath(
|
||||||
hash.update(version);
|
'react-packager-cache-',
|
||||||
|
version,
|
||||||
var roots = options.projectRoots.join(',').split(path.sep).join('-');
|
options.projectRoots.join(',').split(path.sep).join('-'),
|
||||||
hash.update(roots);
|
options.cacheVersion || '0',
|
||||||
|
options.transformModulePath,
|
||||||
var cacheVersion = options.cacheVersion || '0';
|
);
|
||||||
hash.update(cacheVersion);
|
|
||||||
|
|
||||||
hash.update(options.transformModulePath);
|
|
||||||
|
|
||||||
var name = 'react-packager-cache-' + hash.digest('hex');
|
|
||||||
|
|
||||||
return path.join(tmpdir, name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
react-packager/src/JSTransformer/index.js
vendored
13
react-packager/src/JSTransformer/index.js
vendored
@ -23,7 +23,10 @@ const readFile = Promise.denodeify(fs.readFile);
|
|||||||
const MAX_CALLS_PER_WORKER = 600;
|
const MAX_CALLS_PER_WORKER = 600;
|
||||||
|
|
||||||
// Worker will timeout if one of the callers timeout.
|
// Worker will timeout if one of the callers timeout.
|
||||||
const DEFAULT_MAX_CALL_TIME = 60000;
|
const DEFAULT_MAX_CALL_TIME = 120000;
|
||||||
|
|
||||||
|
// How may times can we tolerate failures from the worker.
|
||||||
|
const MAX_RETRIES = 3;
|
||||||
|
|
||||||
const validateOpts = declareOpts({
|
const validateOpts = declareOpts({
|
||||||
projectRoots: {
|
projectRoots: {
|
||||||
@ -63,6 +66,7 @@ class Transformer {
|
|||||||
maxConcurrentCallsPerWorker: 1,
|
maxConcurrentCallsPerWorker: 1,
|
||||||
maxCallsPerWorker: MAX_CALLS_PER_WORKER,
|
maxCallsPerWorker: MAX_CALLS_PER_WORKER,
|
||||||
maxCallTime: opts.transformTimeoutInterval,
|
maxCallTime: opts.transformTimeoutInterval,
|
||||||
|
maxRetries: MAX_RETRIES,
|
||||||
}, opts.transformModulePath);
|
}, opts.transformModulePath);
|
||||||
|
|
||||||
this._transform = Promise.denodeify(this._workers);
|
this._transform = Promise.denodeify(this._workers);
|
||||||
@ -118,6 +122,13 @@ class Transformer {
|
|||||||
);
|
);
|
||||||
timeoutErr.type = 'TimeoutError';
|
timeoutErr.type = 'TimeoutError';
|
||||||
throw timeoutErr;
|
throw timeoutErr;
|
||||||
|
} else if (err.type === 'ProcessTerminatedError') {
|
||||||
|
const uncaughtError = new Error(
|
||||||
|
'Uncaught error in the transformer worker: ' +
|
||||||
|
this._opts.transformModulePath
|
||||||
|
);
|
||||||
|
uncaughtError.type = 'ProcessTerminatedError';
|
||||||
|
throw uncaughtError;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw formatError(err, filePath);
|
throw formatError(err, filePath);
|
||||||
|
@ -13,6 +13,10 @@ const Promise = require('promise');
|
|||||||
const bser = require('bser');
|
const bser = require('bser');
|
||||||
const debug = require('debug')('ReactPackager:SocketClient');
|
const debug = require('debug')('ReactPackager:SocketClient');
|
||||||
const net = require('net');
|
const net = require('net');
|
||||||
|
const path = require('path');
|
||||||
|
const tmpdir = require('os').tmpdir();
|
||||||
|
|
||||||
|
const LOG_PATH = path.join(tmpdir, 'react-packager.log');
|
||||||
|
|
||||||
class SocketClient {
|
class SocketClient {
|
||||||
static create(sockPath) {
|
static create(sockPath) {
|
||||||
@ -81,7 +85,9 @@ class SocketClient {
|
|||||||
delete this._resolvers[message.id];
|
delete this._resolvers[message.id];
|
||||||
|
|
||||||
if (message.type === 'error') {
|
if (message.type === 'error') {
|
||||||
resolver.reject(new Error(message.data));
|
resolver.reject(new Error(
|
||||||
|
message.data + '\n' + 'See logs ' + LOG_PATH
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
resolver.resolve(message.data);
|
resolver.resolve(message.data);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ class SocketServer {
|
|||||||
options
|
options
|
||||||
);
|
);
|
||||||
resolve(this);
|
resolve(this);
|
||||||
|
process.on('exit', () => fs.unlinkSync(sockPath));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this._server.on('connection', (sock) => this._handleConnection(sock));
|
this._server.on('connection', (sock) => this._handleConnection(sock));
|
||||||
@ -41,8 +42,6 @@ class SocketServer {
|
|||||||
this._packagerServer = new Server(options);
|
this._packagerServer = new Server(options);
|
||||||
this._jobs = 0;
|
this._jobs = 0;
|
||||||
this._dieEventually();
|
this._dieEventually();
|
||||||
|
|
||||||
process.on('exit', () => fs.unlinkSync(sockPath));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onReady() {
|
onReady() {
|
||||||
@ -72,6 +71,11 @@ class SocketServer {
|
|||||||
debug('request error', error);
|
debug('request error', error);
|
||||||
this._jobs--;
|
this._jobs--;
|
||||||
this._reply(sock, m.id, 'error', error.stack);
|
this._reply(sock, m.id, 'error', error.stack);
|
||||||
|
|
||||||
|
// Fatal error from JSTransformer transform workers.
|
||||||
|
if (error.type === 'ProcessTerminatedError') {
|
||||||
|
setImmediate(() => process.exit(1));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (m.type) {
|
switch (m.type) {
|
||||||
@ -138,12 +142,17 @@ class SocketServer {
|
|||||||
process.send({ type: 'createdServer' });
|
process.send({ type: 'createdServer' });
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
debug('error creating server', error.code);
|
|
||||||
if (error.code === 'EADDRINUSE') {
|
if (error.code === 'EADDRINUSE') {
|
||||||
// Server already listening, this may happen if multiple
|
// Server already listening, this may happen if multiple
|
||||||
// clients where started in quick succussion (buck).
|
// clients where started in quick succussion (buck).
|
||||||
process.send({ type: 'createdServer' });
|
process.send({ type: 'createdServer' });
|
||||||
|
|
||||||
|
// Kill this server because some other server with the same
|
||||||
|
// config and socket already started.
|
||||||
|
debug('server already started');
|
||||||
|
setImmediate(() => process.exit());
|
||||||
} else {
|
} else {
|
||||||
|
debug('error creating server', error.code);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,6 @@ describe('SocketClient', () => {
|
|||||||
data: 'some error'
|
data: 'some error'
|
||||||
});
|
});
|
||||||
|
|
||||||
return promise.catch(m => expect(m.message).toBe('some error'));
|
return promise.catch(m => expect(m.message).toContain('some error'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -26,6 +26,17 @@ describe('SocketInterface', () => {
|
|||||||
pit('creates socket path by hashing options', () => {
|
pit('creates socket path by hashing options', () => {
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
fs.existsSync = jest.genMockFn().mockImpl(() => true);
|
fs.existsSync = jest.genMockFn().mockImpl(() => true);
|
||||||
|
fs.unlinkSync = jest.genMockFn();
|
||||||
|
let callback;
|
||||||
|
|
||||||
|
require('child_process').spawn.mockImpl(() => ({
|
||||||
|
on: (event, cb) => callback = cb,
|
||||||
|
send: (message) => {
|
||||||
|
setImmediate(() => callback({ type: 'createdServer' }));
|
||||||
|
},
|
||||||
|
unref: () => undefined,
|
||||||
|
disconnect: () => undefined,
|
||||||
|
}));
|
||||||
|
|
||||||
// Check that given two equivelant server options, we end up with the same
|
// Check that given two equivelant server options, we end up with the same
|
||||||
// socket path.
|
// socket path.
|
||||||
@ -49,6 +60,7 @@ describe('SocketInterface', () => {
|
|||||||
pit('should fork a server', () => {
|
pit('should fork a server', () => {
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
fs.existsSync = jest.genMockFn().mockImpl(() => false);
|
fs.existsSync = jest.genMockFn().mockImpl(() => false);
|
||||||
|
fs.unlinkSync = jest.genMockFn();
|
||||||
let sockPath;
|
let sockPath;
|
||||||
let callback;
|
let callback;
|
||||||
|
|
||||||
|
37
react-packager/src/SocketInterface/index.js
vendored
37
react-packager/src/SocketInterface/index.js
vendored
@ -13,12 +13,14 @@ const SocketClient = require('./SocketClient');
|
|||||||
const SocketServer = require('./SocketServer');
|
const SocketServer = require('./SocketServer');
|
||||||
const _ = require('underscore');
|
const _ = require('underscore');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
const debug = require('debug')('ReactPackager:SocketInterface');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const net = require('net');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const tmpdir = require('os').tmpdir();
|
const tmpdir = require('os').tmpdir();
|
||||||
const {spawn} = require('child_process');
|
const {spawn} = require('child_process');
|
||||||
|
|
||||||
const CREATE_SERVER_TIMEOUT = 30000;
|
const CREATE_SERVER_TIMEOUT = 60000;
|
||||||
|
|
||||||
const SocketInterface = {
|
const SocketInterface = {
|
||||||
getOrCreateSocketFor(options) {
|
getOrCreateSocketFor(options) {
|
||||||
@ -38,10 +40,32 @@ const SocketInterface = {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (fs.existsSync(sockPath)) {
|
if (fs.existsSync(sockPath)) {
|
||||||
|
var sock = net.connect(sockPath);
|
||||||
|
sock.on('connect', () => {
|
||||||
|
sock.end();
|
||||||
resolve(SocketClient.create(sockPath));
|
resolve(SocketClient.create(sockPath));
|
||||||
return;
|
});
|
||||||
|
sock.on('error', (e) => {
|
||||||
|
try {
|
||||||
|
debug('deleting socket for not responding', sockPath);
|
||||||
|
fs.unlinkSync(sockPath);
|
||||||
|
} catch (err) {
|
||||||
|
// Another client might have deleted it first.
|
||||||
}
|
}
|
||||||
|
createServer(resolve, reject, options, sockPath);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
createServer(resolve, reject, options, sockPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
listenOnServerMessages() {
|
||||||
|
return SocketServer.listenOnServerIPCMessages();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function createServer(resolve, reject, options, sockPath) {
|
||||||
const logPath = path.join(tmpdir, 'react-packager.log');
|
const logPath = path.join(tmpdir, 'react-packager.log');
|
||||||
|
|
||||||
const timeout = setTimeout(
|
const timeout = setTimeout(
|
||||||
@ -78,11 +102,11 @@ const SocketInterface = {
|
|||||||
if (m && m.type && m.type === 'createdServer') {
|
if (m && m.type && m.type === 'createdServer') {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
child.disconnect();
|
child.disconnect();
|
||||||
|
|
||||||
resolve(SocketClient.create(sockPath));
|
resolve(SocketClient.create(sockPath));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (options.blacklistRE) {
|
if (options.blacklistRE) {
|
||||||
options.blacklistRE = { source: options.blacklistRE.source };
|
options.blacklistRE = { source: options.blacklistRE.source };
|
||||||
}
|
}
|
||||||
@ -91,13 +115,6 @@ const SocketInterface = {
|
|||||||
type: 'createSocketServer',
|
type: 'createSocketServer',
|
||||||
data: { sockPath, options }
|
data: { sockPath, options }
|
||||||
});
|
});
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
listenOnServerMessages() {
|
|
||||||
return SocketServer.listenOnServerIPCMessages();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = SocketInterface;
|
module.exports = SocketInterface;
|
||||||
|
25
react-packager/src/lib/getCacheFilePath.js
vendored
Normal file
25
react-packager/src/lib/getCacheFilePath.js
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 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 crypto = require('crypto');
|
||||||
|
const path = require('path');
|
||||||
|
const tmpdir = require('os').tmpDir();
|
||||||
|
|
||||||
|
function getCacheFilePath(args) {
|
||||||
|
args = Array.prototype.slice.call(args);
|
||||||
|
const prefix = args.shift();
|
||||||
|
|
||||||
|
let hash = crypto.createHash('md5');
|
||||||
|
args.forEach(arg => hash.update(arg));
|
||||||
|
|
||||||
|
return path.join(tmpdir, prefix + hash.digest('hex'));
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = getCacheFilePath;
|
30
react-packager/src/lib/loadCacheSync.js
vendored
Normal file
30
react-packager/src/lib/loadCacheSync.js
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* 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 fs = require('fs');
|
||||||
|
|
||||||
|
function loadCacheSync(cachePath) {
|
||||||
|
if (!fs.existsSync(cachePath)) {
|
||||||
|
return Object.create(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(fs.readFileSync(cachePath));
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof SyntaxError) {
|
||||||
|
console.warn('Unable to parse cache file. Will clear and continue.');
|
||||||
|
fs.unlinkSync(cachePath);
|
||||||
|
return Object.create(null);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = loadCacheSync;
|
@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* 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('../')],
|
||||||
|
blacklist: ['strict'],
|
||||||
|
extra: { 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);
|
||||||
|
});
|
||||||
|
});
|
65
react-packager/src/transforms/babel-plugin-system-import/index.js
vendored
Normal file
65
react-packager/src/transforms/babel-plugin-system-import/index.js
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
var t = require('babel-core').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 systemImportTransform(babel) {
|
||||||
|
return new babel.Transformer('system-import', {
|
||||||
|
CallExpression: function(node, parent, scope, state) {
|
||||||
|
if (!isAppropriateSystemImportCall(node, parent)) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bundlesLayout = state.opts.extra.bundlesLayout;
|
||||||
|
var bundleID = bundlesLayout.getBundleIDForModule(
|
||||||
|
node.arguments[0].value
|
||||||
|
);
|
||||||
|
|
||||||
|
var bundles = bundleID.split('.');
|
||||||
|
bundles.splice(0, 1);
|
||||||
|
bundles = bundles.map(function(id) {
|
||||||
|
return t.literal('bundle.' + id);
|
||||||
|
});
|
||||||
|
|
||||||
|
return t.callExpression(
|
||||||
|
t.identifier('loadBundles'),
|
||||||
|
[t.arrayExpression(bundles)]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
metadata: {
|
||||||
|
group: 'fb'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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 === 'Literal'
|
||||||
|
);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user