transform before extracting dependencies

Summary:Make packager transform files before extracting their dependencies.

This allows us to extract dependencies added by transforms (and to avoid including them manually).

It also allows for better optimization and to get rid of the “whole program optimization” step:
This diff utilizes the new worker introduced in D2976677 / d94a567 – that means that minified builds inline the following variables:

- `__DEV__` → `false`
- `process.env.NODE_ENV` → `'production'`
- `Platform.OS` / `React.Platform.OS` → `'android'` / `'ios'`

and eliminates branches of conditionals with constant conditions. Dependency extraction happens only after that step, which means that production bundles don’t include any modules that are not used.

Fixes #4185

Reviewed By: martinbigio

Differential Revision: D2977169

fb-gh-sync-id: e6ce8dd29d1b49aec49b309201141f5b2709da1d
shipit-source-id: e6ce8dd29d1b49aec49b309201141f5b2709da1d
This commit is contained in:
David Aurelio 2016-03-08 09:50:14 -08:00 committed by Facebook Github Bot 0
parent 0bdf452803
commit 3e1708bcc3
27 changed files with 616 additions and 2090 deletions

View File

@ -1,5 +1,5 @@
{
"version": "0.1.4",
"version": "0.2.0",
"name": "react-native-packager",
"description": "Build native apps with React!",
"repository": {

View File

@ -11,53 +11,42 @@
const _ = require('underscore');
const base64VLQ = require('./base64-vlq');
const BundleBase = require('./BundleBase');
const UglifyJS = require('uglify-js');
const ModuleTransport = require('../lib/ModuleTransport');
const Activity = require('../Activity');
const crypto = require('crypto');
const SOURCEMAPPING_URL = '\n\/\/# sourceMappingURL=';
const minifyCode = code =>
UglifyJS.minify(code, {fromString: true, ascii_only: true}).code;
const getCode = x => x.code;
const getMinifiedCode = x => minifyCode(x.code);
const getNameAndCode = ({name, code}) => ({name, code});
const getNameAndMinifiedCode =
({name, code}) => ({name, code: minifyCode(code)});
class Bundle extends BundleBase {
constructor(sourceMapUrl) {
constructor({sourceMapUrl, minify} = {}) {
super();
this._sourceMap = false;
this._sourceMapUrl = sourceMapUrl;
this._shouldCombineSourceMaps = false;
this._numPrependedModules = 0;
this._numRequireCalls = 0;
this._minify = minify;
}
addModule(resolver, response, module, transformed) {
return resolver.wrapModule(
response,
addModule(resolver, resolutionResponse, module, moduleTransport) {
return resolver.wrapModule({
resolutionResponse,
module,
transformed.code
).then(({code, name}) => {
const moduleTransport = new ModuleTransport({
code,
name,
map: transformed.map,
sourceCode: transformed.sourceCode,
sourcePath: transformed.sourcePath,
virtual: transformed.virtual,
});
name: moduleTransport.name,
code: moduleTransport.code,
map: moduleTransport.map,
meta: moduleTransport.meta,
minify: this._minify,
}).then(({code, map}) => {
// If we get a map from the transformer we'll switch to a mode
// were we're combining the source maps as opposed to
if (!this._shouldCombineSourceMaps && moduleTransport.map != null) {
if (!this._shouldCombineSourceMaps && map != null) {
this._shouldCombineSourceMaps = true;
}
super.addModule(moduleTransport);
super.addModule(new ModuleTransport({...moduleTransport, code, map}));
});
}
@ -103,10 +92,6 @@ class Bundle extends BundleBase {
options = options || {};
if (options.minify) {
return this.getMinifiedSourceAndMap(options.dev).code;
}
let source = super.getSource();
if (options.inlineSourceMap) {
@ -118,84 +103,22 @@ class Bundle extends BundleBase {
return source;
}
getUnbundle({minify}) {
const allModules = super.getModules().slice();
getUnbundle() {
const allModules = this.getModules().slice();
const prependedModules = this._numPrependedModules;
const requireCalls = this._numRequireCalls;
const modules =
allModules
.splice(prependedModules, allModules.length - requireCalls - prependedModules);
const startupCode =
allModules
.map(minify ? getMinifiedCode : getCode)
.join('\n');
const startupCode = allModules.map(getCode).join('\n');
return {
startupCode,
modules:
modules.map(minify ? getNameAndMinifiedCode : getNameAndCode)
modules: modules.map(getNameAndCode)
};
}
getMinifiedSourceAndMap(dev) {
super.assertFinalized();
if (this._minifiedSourceAndMap) {
return this._minifiedSourceAndMap;
}
let source = this.getSource();
let map = this.getSourceMap();
if (!dev) {
const wpoActivity = Activity.startEvent('Whole Program Optimisations');
const wpoResult = require('babel-core').transform(source, {
retainLines: true,
compact: true,
plugins: require('../transforms/whole-program-optimisations'),
inputSourceMap: map,
});
Activity.endEvent(wpoActivity);
source = wpoResult.code;
map = wpoResult.map;
}
try {
const minifyActivity = Activity.startEvent('minify');
this._minifiedSourceAndMap = UglifyJS.minify(source, {
fromString: true,
outSourceMap: this._sourceMapUrl,
inSourceMap: map,
output: {ascii_only: true},
});
Activity.endEvent(minifyActivity);
return this._minifiedSourceAndMap;
} catch(e) {
// Sometimes, when somebody is using a new syntax feature that we
// don't yet have transform for, the untransformed line is sent to
// uglify, and it chokes on it. This code tries to print the line
// and the module for easier debugging
let errorMessage = 'Error while minifying JS\n';
if (e.line) {
errorMessage += 'Transformed code line: "' +
source.split('\n')[e.line - 1] + '"\n';
}
if (e.pos) {
let fromIndex = source.lastIndexOf('__d(\'', e.pos);
if (fromIndex > -1) {
fromIndex += '__d(\''.length;
const toIndex = source.indexOf('\'', fromIndex);
errorMessage += 'Module name (best guess): ' +
source.substring(fromIndex, toIndex) + '\n';
}
}
errorMessage += e.toString();
throw new Error(errorMessage);
}
}
/**
* I found a neat trick in the sourcemap spec that makes it easy
* to concat sourcemaps. The `sections` field allows us to combine
@ -211,14 +134,17 @@ class Bundle extends BundleBase {
};
let line = 0;
super.getModules().forEach(function(module) {
this.getModules().forEach(module => {
let map = module.map;
if (module.virtual) {
map = generateSourceMapForVirtualModule(module);
}
if (options.excludeSource) {
map = _.extend({}, map, {sourcesContent: []});
if (map.sourcesContent && map.sourcesContent.length) {
map = _.extend({}, map, {sourcesContent: []});
}
}
result.sections.push({
@ -234,25 +160,20 @@ class Bundle extends BundleBase {
getSourceMap(options) {
super.assertFinalized();
options = options || {};
if (options.minify) {
return this.getMinifiedSourceAndMap(options.dev).map;
}
if (this._shouldCombineSourceMaps) {
return this._getCombinedSourceMaps(options);
}
const mappings = this._getMappings();
const modules = this.getModules();
const map = {
file: this._getSourceMapFile(),
sources: _.pluck(super.getModules(), 'sourcePath'),
sources: modules.map(module => module.sourcePath),
version: 3,
names: [],
mappings: mappings,
sourcesContent: options.excludeSource
? [] : _.pluck(super.getModules(), 'sourceCode')
? [] : modules.map(module => module.sourceCode)
};
return map;
}
@ -316,12 +237,10 @@ class Bundle extends BundleBase {
}
getJSModulePaths() {
return super.getModules().filter(function(module) {
return this.getModules()
// Filter out non-js files. Like images etc.
return !module.virtual;
}).map(function(module) {
return module.sourcePath;
});
.filter(module => !module.virtual)
.map(module => module.sourcePath);
}
getDebugInfo() {
@ -338,7 +257,7 @@ class Bundle extends BundleBase {
'}',
'</style>',
'<h3> Module paths and transformed code: </h3>',
super.getModules().map(function(m) {
this.getModules().map(function(m) {
return '<div> <h4> Path: </h4>' + m.sourcePath + '<br/> <h4> Source: </h4>' +
'<code><pre class="collapsed" onclick="this.classList.remove(\'collapsed\')">' +
_.escape(m.code) + '</pre></code></div>';
@ -354,15 +273,17 @@ class Bundle extends BundleBase {
sourceMapUrl: this._sourceMapUrl,
numPrependedModules: this._numPrependedModules,
numRequireCalls: this._numRequireCalls,
shouldCombineSourceMaps: this._shouldCombineSourceMaps,
};
}
static fromJSON(json) {
const bundle = new Bundle(json.sourceMapUrl);
const bundle = new Bundle({sourceMapUrl: json.sourceMapUrl});
bundle._sourceMapUrl = json.sourceMapUrl;
bundle._numPrependedModules = json.numPrependedModules;
bundle._numRequireCalls = json.numRequireCalls;
bundle._shouldCombineSourceMaps = json.shouldCombineSourceMaps;
BundleBase.fromJSON(bundle, json);

View File

@ -21,21 +21,14 @@ class HMRBundle extends BundleBase {
this._sourceMappingURLs = [];
}
addModule(resolver, response, module, transformed) {
return resolver.resolveRequires(response,
addModule(resolver, response, module, moduleTransport) {
return resolver.resolveRequires(
response,
module,
transformed.code,
).then(({name, code}) => {
const moduleTransport = new ModuleTransport({
code,
name,
map: transformed.map,
sourceCode: transformed.sourceCode,
sourcePath: transformed.sourcePath,
virtual: transformed.virtual,
});
super.addModule(moduleTransport);
moduleTransport.code,
moduleTransport.meta.dependencyOffsets,
).then(code => {
super.addModule(new ModuleTransport({...moduleTransport, code}));
this._sourceMappingURLs.push(this._sourceMappingURLFn(moduleTransport.sourcePath));
this._sourceURLs.push(this._sourceURLFn(moduleTransport.sourcePath));
});

View File

@ -11,17 +11,15 @@
jest.autoMockOff();
const Bundle = require('../Bundle');
const ModuleTransport = require('../../lib/ModuleTransport');
const Promise = require('Promise');
const SourceMapGenerator = require('source-map').SourceMapGenerator;
const UglifyJS = require('uglify-js');
const crypto = require('crypto');
describe('Bundle', () => {
var bundle;
beforeEach(() => {
bundle = new Bundle('test_url');
bundle = new Bundle({sourceMapUrl: 'test_url'});
bundle.getSourceMap = jest.genMockFn().mockImpl(() => {
return 'test-source-map';
});
@ -108,34 +106,11 @@ describe('Bundle', () => {
].join('\n'));
});
});
pit('should get minified source', () => {
const minified = {
code: 'minified',
map: 'map',
};
UglifyJS.minify = function() {
return minified;
};
return Promise.resolve().then(() => {
return addModule({
bundle,
code: 'transformed foo;',
sourceCode: 'source foo',
sourcePath: 'foo path',
});
}).then(() => {
bundle.finalize();
expect(bundle.getMinifiedSourceAndMap({dev: true})).toBe(minified);
});
});
});
describe('sourcemap bundle', () => {
pit('should create sourcemap', () => {
const otherBundle = new Bundle('test_url');
const otherBundle = new Bundle({sourceMapUrl: 'test_url'});
return Promise.resolve().then(() => {
return addModule({
@ -179,7 +154,7 @@ describe('Bundle', () => {
});
pit('should combine sourcemaps', () => {
const otherBundle = new Bundle('test_url');
const otherBundle = new Bundle({sourceMapUrl: 'test_url'});
return Promise.resolve().then(() => {
return addModule({
@ -269,7 +244,7 @@ describe('Bundle', () => {
describe('getAssets()', () => {
it('should save and return asset objects', () => {
var p = new Bundle('test_url');
var p = new Bundle({sourceMapUrl: 'test_url'});
var asset1 = {};
var asset2 = {};
p.addAsset(asset1);
@ -281,7 +256,7 @@ describe('Bundle', () => {
describe('getJSModulePaths()', () => {
pit('should return module paths', () => {
var otherBundle = new Bundle('test_url');
var otherBundle = new Bundle({sourceMapUrl: 'test_url'});
return Promise.resolve().then(() => {
return addModule({
bundle: otherBundle,
@ -305,7 +280,7 @@ describe('Bundle', () => {
describe('getEtag()', function() {
it('should return an etag', function() {
var bundle = new Bundle('test_url');
var bundle = new Bundle({sourceMapUrl: 'test_url'});
bundle.finalize({});
var eTag = crypto.createHash('md5').update(bundle.getSource()).digest('hex');
expect(bundle.getEtag()).toEqual(eTag);
@ -365,19 +340,17 @@ function genSourceMap(modules) {
return sourceMapGen.toJSON();
}
function resolverFor(code) {
function resolverFor(code, map) {
return {
wrapModule: (response, module, sourceCode) => Promise.resolve(
{name: 'name', code}
),
wrapModule: () => Promise.resolve({code, map}),
};
}
function addModule({bundle, code, sourceCode, sourcePath, map, virtual}) {
return bundle.addModule(
resolverFor(code),
resolverFor(code, map),
null,
null,
{sourceCode, sourcePath, map, virtual}
{code, sourceCode, sourcePath, map, virtual}
);
}

View File

@ -19,7 +19,6 @@ jest
jest.mock('fs');
var Bundler = require('../');
var JSTransformer = require('../../JSTransformer');
var Resolver = require('../../Resolver');
var sizeOf = require('image-size');
var fs = require('fs');
@ -43,25 +42,29 @@ describe('Bundler', function() {
isJSON() { return isJSON; },
isAsset() { return isAsset; },
isAsset_DEPRECATED() { return isAsset_DEPRECATED; },
read: () => ({
code: 'arbitrary',
source: 'arbitrary',
}),
};
}
var getDependencies;
var getModuleSystemDependencies;
var wrapModule;
var bundler;
var assetServer;
var modules;
var projectRoots;
beforeEach(function() {
getDependencies = jest.genMockFn();
getModuleSystemDependencies = jest.genMockFn();
wrapModule = jest.genMockFn();
projectRoots = ['/root'];
Resolver.mockImpl(function() {
return {
getDependencies: getDependencies,
getModuleSystemDependencies: getModuleSystemDependencies,
wrapModule: wrapModule,
};
});
@ -80,7 +83,7 @@ describe('Bundler', function() {
};
bundler = new Bundler({
projectRoots: ['/root'],
projectRoots,
assetServer: assetServer,
});
@ -109,34 +112,18 @@ describe('Bundler', function() {
}),
];
getDependencies.mockImpl(function() {
return Promise.resolve({
getDependencies.mockImpl((main, options, transformOptions) =>
Promise.resolve({
mainModuleId: 'foo',
dependencies: modules
});
});
dependencies: modules,
transformOptions,
})
);
getModuleSystemDependencies.mockImpl(function() {
return [];
});
JSTransformer.prototype.loadFileAndTransform
.mockImpl(function(path) {
return Promise.resolve({
code: 'transformed ' + path,
map: 'sourcemap ' + path,
sourceCode: 'source ' + path,
sourcePath: path
});
});
wrapModule.mockImpl(function(response, module, code) {
return module.getName().then(name => ({
name,
code: 'lol ' + code + ' lol'
}));
});
sizeOf.mockImpl(function(path, cb) {
cb(null, { width: 50, height: 100 });
});
@ -207,10 +194,20 @@ describe('Bundler', function() {
});
pit('gets the list of dependencies from the resolver', function() {
return bundler.getDependencies('/root/foo.js', true).then(() =>
const entryFile = '/root/foo.js';
return bundler.getDependencies({entryFile, recursive: true}).then(() =>
expect(getDependencies).toBeCalledWith(
'/root/foo.js',
{ dev: true, recursive: true },
{ minify: false,
dev: true,
transform: {
dev: true,
hot: false,
projectRoots,
}
},
undefined,
)
);
});

View File

@ -26,7 +26,6 @@ const imageSize = require('image-size');
const version = require('../../../../package.json').version;
const sizeOf = Promise.denodeify(imageSize);
const readFile = Promise.denodeify(fs.readFile);
const noop = () => {};
@ -82,10 +81,6 @@ const validateOpts = declareOpts({
type: 'number',
required: false,
},
disableInternalTransforms: {
type: 'boolean',
default: false,
},
});
class Bundler {
@ -123,6 +118,10 @@ class Bundler {
cacheKey: cacheKeyParts.join('$'),
});
this._transformer = new Transformer({
transformModulePath: opts.transformModulePath,
});
this._resolver = new Resolver({
projectRoots: opts.projectRoots,
blacklistRE: opts.blacklistRE,
@ -132,14 +131,10 @@ class Bundler {
fileWatcher: opts.fileWatcher,
assetExts: opts.assetExts,
cache: this._cache,
});
this._transformer = new Transformer({
projectRoots: opts.projectRoots,
blacklistRE: opts.blacklistRE,
cache: this._cache,
transformModulePath: opts.transformModulePath,
disableInternalTransforms: opts.disableInternalTransforms,
transformCode:
(module, code, options) =>
this._transformer.transformFile(module.path, code, options),
minifyCode: this._transformer.minify,
});
this._projectRoots = opts.projectRoots;
@ -158,11 +153,11 @@ class Bundler {
}
bundle(options) {
const {dev, unbundle, platform} = options;
const {dev, minify, unbundle} = options;
const moduleSystemDeps =
this._resolver.getModuleSystemDependencies({dev, unbundle, platform});
this._resolver.getModuleSystemDependencies({dev, unbundle});
return this._bundle({
bundle: new Bundle(options.sourceMapUrl),
bundle: new Bundle({minify, sourceMapUrl: options.sourceMapUrl}),
moduleSystemDeps,
...options,
});
@ -230,7 +225,8 @@ class Bundler {
entryFile,
runModule: runMainModule,
runBeforeMainModule,
dev: isDev,
dev,
minify,
platform,
moduleSystemDeps = [],
hot,
@ -264,7 +260,8 @@ class Bundler {
return this._buildBundle({
entryFile,
isDev,
dev,
minify,
platform,
bundle,
hot,
@ -279,7 +276,7 @@ class Bundler {
runModule: runMainModule,
runBeforeMainModule,
sourceMapUrl,
dev: isDev,
dev,
platform,
}) {
const onModuleTransformed = ({module, transformed, response, bundle}) => {
@ -303,17 +300,19 @@ class Bundler {
return this._buildBundle({
entryFile,
isDev,
dev,
platform,
onModuleTransformed,
finalizeBundle,
minify: false,
bundle: new PrepackBundle(sourceMapUrl),
});
}
_buildBundle({
entryFile,
isDev,
dev,
minify,
platform,
bundle,
hot,
@ -323,54 +322,54 @@ class Bundler {
finalizeBundle = noop,
}) {
const findEventId = Activity.startEvent('find dependencies');
if (!resolutionResponse) {
resolutionResponse = this.getDependencies(entryFile, isDev, platform);
let onProgess;
if (process.stdout.isTTY) {
const bar = new ProgressBar(
'transformed :current/:total (:percent)',
{complete: '=', incomplete: ' ', width: 40, total: 1},
);
onProgess = (_, total) => {
bar.total = total;
bar.tick();
};
}
resolutionResponse = this.getDependencies(
{entryFile, dev, platform, hot, onProgess, minify});
}
return Promise.resolve(resolutionResponse).then(response => {
Activity.endEvent(findEventId);
onResolutionResponse(response);
const transformEventId = Activity.startEvent('transform');
const bar = process.stdout.isTTY
? new ProgressBar('transforming [:bar] :percent :current/:total', {
complete: '=',
incomplete: ' ',
width: 40,
total: response.dependencies.length,
})
: {tick() {}};
const transformPromises =
response.dependencies.map(module =>
this._transformModule({
mainModuleName: response.mainModuleId,
bundle,
const toModuleTransport = module =>
this._toModuleTransport({
module,
bundle,
transformOptions: response.transformOptions,
}).then(transformed => {
onModuleTransformed({
module,
platform,
dev: isDev,
hot
}).then(transformed => {
bar.tick();
onModuleTransformed({module, transformed, response, bundle});
return {module, transformed};
})
response,
bundle,
transformed,
});
return {module, transformed};
});
return Promise.all(response.dependencies.map(toModuleTransport))
.then(transformedModules =>
Promise
.resolve(finalizeBundle({bundle, transformedModules, response}))
.then(() => bundle)
);
return Promise.all(transformPromises).then(transformedModules => {
Activity.endEvent(transformEventId);
return Promise
.resolve(finalizeBundle({bundle, transformedModules, response}))
.then(() => bundle);
});
});
}
invalidateFile(filePath) {
if (this._transformOptionsModule) {
this._transformOptionsModule.onFileChange &&
this._transformOptionsModule.onFileChange();
}
this._transformer.invalidateFile(filePath);
this._cache.invalidate(filePath);
}
getShallowDependencies(entryFile) {
@ -385,19 +384,35 @@ class Bundler {
return this._resolver.getModuleForPath(entryFile);
}
getDependencies(main, isDev, platform, recursive = true) {
return this._resolver.getDependencies(
main,
{
dev: isDev,
getDependencies({
entryFile,
platform,
dev = true,
minify = !dev,
hot = false,
recursive = true,
onProgess,
}) {
return this.getTransformOptions(
entryFile, {dev, platform, hot, projectRoots: this._projectRoots}
).then(transformSpecificOptions => {
const transformOptions = {
minify,
dev,
platform,
recursive,
},
);
transform: transformSpecificOptions,
};
return this._resolver.getDependencies(
entryFile,
{dev, platform, recursive},
transformOptions,
onProgess,
);
});
}
getOrderedDependencyPaths({ entryFile, dev, platform }) {
return this.getDependencies(entryFile, dev, platform).then(
return this.getDependencies({entryFile, dev, platform}).then(
({ dependencies }) => {
const ret = [];
const promises = [];
@ -428,36 +443,32 @@ class Bundler {
);
}
_transformModule({
bundle,
module,
mainModuleName,
platform = null,
dev = true,
hot = false,
}) {
_toModuleTransport({module, bundle, transformOptions}) {
let moduleTransport;
if (module.isAsset_DEPRECATED()) {
return this._generateAssetModule_DEPRECATED(bundle, module);
moduleTransport = this._generateAssetModule_DEPRECATED(bundle, module);
} else if (module.isAsset()) {
return this._generateAssetModule(bundle, module, platform);
} else if (module.isJSON()) {
return generateJSONModule(module);
} else {
return this._getTransformOptions(
{
bundleEntry: mainModuleName,
platform: platform,
dev: dev,
modulePath: module.path,
},
{hot},
).then(options => {
return this._transformer.loadFileAndTransform(
path.resolve(module.path),
options,
);
});
moduleTransport = this._generateAssetModule(
bundle, module, transformOptions.platform);
}
if (moduleTransport) {
return Promise.resolve(moduleTransport);
}
return Promise.all([
module.getName(),
module.read(transformOptions),
]).then((
[name, {code, dependencies, dependencyOffsets, map, source}]
) => new ModuleTransport({
name,
code,
map,
meta: {dependencies, dependencyOffsets},
sourceCode: source,
sourcePath: module.path
}));
}
getGraphDebugInfo() {
@ -480,9 +491,10 @@ class Bundler {
bundle.addAsset(img);
const code = 'module.exports = ' + JSON.stringify(img) + ';';
const code = 'module.exports=' + JSON.stringify(img) + ';';
return new ModuleTransport({
name: id,
code: code,
sourceCode: code,
sourcePath: module.path,
@ -525,19 +537,31 @@ class Bundler {
type: assetData.type,
};
const ASSET_TEMPLATE = 'module.exports = require("AssetRegistry").registerAsset(%json);';
const code = ASSET_TEMPLATE.replace('%json', JSON.stringify(asset));
const json = JSON.stringify(asset);
const code =
`module.exports = require('AssetRegistry').registerAsset(${json});`;
const dependencies = ['AssetRegistry'];
const dependencyOffsets = [code.indexOf('AssetRegistry') - 1];
return {asset, code};
return {
asset,
code,
meta: {dependencies, dependencyOffsets}
};
});
}
_generateAssetModule(bundle, module, platform = null) {
return this._generateAssetObjAndCode(module, platform).then(({asset, code}) => {
return Promise.all([
module.getName(),
this._generateAssetObjAndCode(module, platform),
]).then(([name, {asset, code, meta}]) => {
bundle.addAsset(asset);
return new ModuleTransport({
code: code,
name,
code,
meta,
sourceCode: code,
sourcePath: module.path,
virtual: true,
@ -545,37 +569,15 @@ class Bundler {
});
}
_getTransformOptions(config, options) {
const transformerOptions = this._transformOptionsModule
? this._transformOptionsModule.get(Object.assign(
{
bundler: this,
platform: options.platform,
dev: options.dev,
},
config,
))
: Promise.resolve(null);
return transformerOptions.then(overrides => {
return {...options, ...overrides};
});
getTransformOptions(mainModuleName, options) {
const extraOptions = this._transformOptionsModule
? this._transformOptionsModule(mainModuleName, options, this)
: null;
return Promise.resolve(extraOptions)
.then(extraOptions => Object.assign(options, extraOptions));
}
}
function generateJSONModule(module) {
return readFile(module.path).then(function(data) {
const code = 'module.exports = ' + data.toString('utf8') + ';';
return new ModuleTransport({
code: code,
sourceCode: code,
sourcePath: module.path,
virtual: true,
});
});
}
function getPathRelativeToRoot(roots, absPath) {
for (let i = 0; i < roots.length; i++) {
const relPath = path.relative(roots[i], absPath);
@ -594,12 +596,4 @@ function verifyRootExists(root) {
assert(fs.statSync(root).isDirectory(), 'Root has to be a valid directory');
}
class DummyCache {
get(filepath, field, loaderCb) {
return loaderCb();
}
end(){}
invalidate(filepath){}
}
module.exports = Bundler;

View File

@ -12,61 +12,66 @@ jest
.dontMock('../../lib/ModuleTransport')
.dontMock('../');
jest.mock('fs');
jest.setMock('temp', {path: () => '/arbitrary/path'});
const fs = {writeFileSync: jest.genMockFn()};
const temp = {path: () => '/arbitrary/path'};
const workerFarm = jest.genMockFn();
jest.setMock('fs', fs);
jest.setMock('temp', temp);
jest.setMock('worker-farm', workerFarm);
var Transformer = require('../');
var fs = require('fs');
var Cache;
var options;
const {any} = jasmine;
describe('Transformer', function() {
var workers;
let options, workers, Cache;
const fileName = '/an/arbitrary/file.js';
const transformModulePath = __filename;
beforeEach(function() {
Cache = jest.genMockFn();
Cache.prototype.get = jest.genMockFn().mockImpl((a, b, c) => c());
workers = jest.genMockFn();
jest.setMock('worker-farm', jest.genMockFn().mockImpl(function() {
return workers;
}));
options = {
transformModulePath: '/foo/bar',
cache: new Cache({}),
};
fs.writeFileSync.mockClear();
options = {transformModulePath};
workerFarm.mockClear();
workerFarm.mockImpl((opts, path, methods) => {
const api = workers = {};
methods.forEach(method => api[method] = jest.genMockFn());
return api;
});
});
pit('should loadFileAndTransform', function() {
workers.mockImpl(function(data, callback) {
callback(null, { code: 'transformed', map: 'sourceMap' });
});
fs.readFile.mockImpl(function(file, callback) {
callback(null, 'content');
it('passes transform module path, file path, source code, and options to the worker farm when transforming', () => {
const transformOptions = {arbitrary: 'options'};
const code = 'arbitrary(code)';
new Transformer(options).transformFile(fileName, code, transformOptions);
expect(workers.transformAndExtractDependencies).toBeCalledWith(
transformModulePath,
fileName,
code,
transformOptions,
any(Function),
);
});
pit('passes the data produced by the worker back', () => {
const transformer = new Transformer(options);
const result = { code: 'transformed', map: 'sourceMap' };
workers.transformAndExtractDependencies.mockImpl(function(transformPath, filename, code, options, callback) {
callback(null, result);
});
return new Transformer(options).loadFileAndTransform('file')
.then(function(data) {
expect(data).toEqual({
code: 'transformed',
map: 'sourceMap',
sourcePath: 'file',
sourceCode: 'content'
});
});
return transformer.transformFile(fileName, '', {})
.then(data => expect(data).toBe(result));
});
pit('should add file info to parse errors', function() {
const transformer = new Transformer(options);
var message = 'message';
var snippet = 'snippet';
fs.readFile.mockImpl(function(file, callback) {
callback(null, 'var x;\nvar answer = 1 = x;');
});
workers.mockImpl(function(data, callback) {
workers.transformAndExtractDependencies.mockImpl(function(transformPath, filename, code, options, callback) {
var babelError = new SyntaxError(message);
babelError.type = 'SyntaxError';
babelError.description = message;
@ -78,13 +83,13 @@ describe('Transformer', function() {
callback(babelError);
});
return new Transformer(options).loadFileAndTransform('foo-file.js')
return transformer.transformFile(fileName, '', {})
.catch(function(error) {
expect(error.type).toEqual('TransformError');
expect(error.message).toBe('SyntaxError ' + message);
expect(error.lineNumber).toBe(2);
expect(error.column).toBe(15);
expect(error.filename).toBe('foo-file.js');
expect(error.filename).toBe(fileName);
expect(error.description).toBe(message);
expect(error.snippet).toBe(snippet);
});

View File

@ -1,109 +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();
jest.mock('babel-core');
const worker = require('../worker');
const babel = require('babel-core');
const code = 'code';
describe('Resolver', function() {
beforeEach(() => {
babel.transform.mockImpl((source, options) => source);
});
describe('when no external transform is provided', () => {
xit('should invoke internal transform if available', () => {
transform({
sourceCode: 'code',
filename: 'test',
options: options({opts: {}, internalTransformsEnabled: true}),
});
expect(babel.transform.mock.calls.length).toBe(1);
});
it('should not invoke internal transform if unavailable', () => {
transform({
sourceCode: 'code',
filename: 'test',
options: options({opts: {}, internalTransformsEnabled: false}),
});
expect(babel.transform.mock.calls.length).toBe(0);
});
});
describe('when external transform is provided', () => {
xit('should invoke both transformers if internal is available', () => {
transform({
sourceCode: code,
filename: 'test',
options: options({
opts: {
externalTransformModulePath: require.resolve(
'../../../../transformer.js'
),
},
internalTransformsEnabled: true,
}),
});
expect(babel.transform.mock.calls.length).toBe(2);
});
it('should invoke only external transformer if internal is not available', () => {
transform({
sourceCode: 'code',
filename: 'test',
options: options({
opts: {
externalTransformModulePath: require.resolve(
'../../../../transformer.js'
),
},
internalTransformsEnabled: false,
}),
});
expect(babel.transform.mock.calls.length).toBe(1);
});
xit('should pipe errors through transform pipeline', () => {
const error = new Error('transform error');
babel.transform.mockImpl((source, options) => {
throw error;
});
const callback = transform({
sourceCode: 'code',
filename: 'test',
options: options({
opts: {
externalTransformModulePath: require.resolve(
'../../../../transformer.js'
),
},
internalTransformsEnabled: true,
}),
});
expect(callback.mock.calls[0][0]).toBe(error);
});
});
});
function transform(data) {
const callback = jest.genMockFunction();
worker(data, callback);
return callback;
}
function options({opts, internalTransformsEnabled}) {
return Object.assign(opts, {hot: internalTransformsEnabled});
}

View File

@ -8,18 +8,13 @@
*/
'use strict';
const ModuleTransport = require('../lib/ModuleTransport');
const Promise = require('promise');
const declareOpts = require('../lib/declareOpts');
const fs = require('fs');
const os = require('os');
const temp = require('temp');
const util = require('util');
const workerFarm = require('worker-farm');
const debug = require('debug')('ReactNativePackager:JStransformer');
const readFile = Promise.denodeify(fs.readFile);
// Avoid memory leaks caused in workers. This number seems to be a good enough number
// to avoid any memory leak while not slowing down initial builds.
// TODO(amasad): Once we get bundle splitting, we can drive this down a bit more.
@ -32,33 +27,14 @@ const DEFAULT_MAX_CALL_TIME = 301000;
const MAX_RETRIES = 2;
const validateOpts = declareOpts({
projectRoots: {
type: 'array',
required: true,
},
blacklistRE: {
type: 'object', // typeof regex is object
},
polyfillModuleNames: {
type: 'array',
default: [],
},
transformModulePath: {
type:'string',
required: false,
},
cache: {
type: 'object',
required: true,
},
transformTimeoutInterval: {
type: 'number',
default: DEFAULT_MAX_CALL_TIME,
},
disableInternalTransforms: {
type: 'boolean',
default: false,
},
});
const maxConcurrentWorkers = ((cores, override) => {
@ -82,120 +58,67 @@ class Transformer {
constructor(options) {
const opts = this._opts = validateOpts(options);
this._cache = opts.cache;
this._transformModulePath = opts.transformModulePath;
this._projectRoots = opts.projectRoots;
const {transformModulePath} = opts;
if (opts.transformModulePath != null) {
let transformer;
if (transformModulePath) {
this._transformModulePath = require.resolve(transformModulePath);
if (opts.disableInternalTransforms) {
transformer = opts.transformModulePath;
} else {
transformer = this._workerWrapperPath = temp.path();
fs.writeFileSync(
this._workerWrapperPath,
`
module.exports = require(${JSON.stringify(require.resolve('./worker'))});
require(${JSON.stringify(String(opts.transformModulePath))});
`
);
}
this._workers = workerFarm(
{
autoStart: true,
maxConcurrentCallsPerWorker: 1,
maxConcurrentWorkers: maxConcurrentWorkers,
maxCallsPerWorker: MAX_CALLS_PER_WORKER,
maxCallTime: opts.transformTimeoutInterval,
maxRetries: MAX_RETRIES,
},
require.resolve('./worker'),
['minify', 'transformAndExtractDependencies']
);
this._workers = workerFarm({
autoStart: true,
maxConcurrentCallsPerWorker: 1,
maxConcurrentWorkers: maxConcurrentWorkers,
maxCallsPerWorker: MAX_CALLS_PER_WORKER,
maxCallTime: opts.transformTimeoutInterval,
maxRetries: MAX_RETRIES,
}, transformer);
this._transform = Promise.denodeify(this._workers);
this._transform = Promise.denodeify(this._workers.transformAndExtractDependencies);
this.minify = Promise.denodeify(this._workers.minify);
}
}
kill() {
this._workers && workerFarm.end(this._workers);
if (this._workerWrapperPath &&
typeof this._workerWrapperPath === 'string') {
fs.unlink(this._workerWrapperPath, () => {}); // we don't care about potential errors here
}
}
invalidateFile(filePath) {
this._cache.invalidate(filePath);
}
loadFileAndTransform(filePath, options) {
if (this._transform == null) {
transformFile(fileName, code, options) {
if (!this._transform) {
return Promise.reject(new Error('No transfrom module'));
}
debug('transforming file', fileName);
return this
._transform(this._transformModulePath, fileName, code, options)
.then(result => {
debug('done transforming file', fileName);
return result;
})
.catch(error => {
if (error.type === 'TimeoutError') {
const timeoutErr = new Error(
`TimeoutError: transforming ${fileName} took longer than ` +
`${this._opts.transformTimeoutInterval / 1000} seconds.\n` +
`You can adjust timeout via the 'transformTimeoutInterval' option`
);
timeoutErr.type = 'TimeoutError';
throw timeoutErr;
} else if (error.type === 'ProcessTerminatedError') {
const uncaughtError = new Error(
'Uncaught error in the transformer worker: ' +
this._opts.transformModulePath
);
uncaughtError.type = 'ProcessTerminatedError';
throw uncaughtError;
}
debug('transforming file', filePath);
const optionsJSON = JSON.stringify(options);
return this._cache.get(
filePath,
'transformedSource-' + optionsJSON,
// TODO: use fastfs to avoid reading file from disk again
() => readFile(filePath).then(
buffer => {
const sourceCode = buffer.toString('utf8');
return this._transform({
sourceCode,
filename: filePath,
options: {
...options,
projectRoots: this._projectRoots,
externalTransformModulePath: this._transformModulePath,
},
}).then(res => {
if (res.error) {
console.warn(
'Error property on the result value from the transformer',
'module is deprecated and will be removed in future versions.',
'Please pass an error object as the first argument to the callback'
);
throw formatError(res.error, filePath);
}
debug('done transforming file', filePath);
return new ModuleTransport({
code: res.code,
map: res.map,
sourcePath: filePath,
sourceCode: sourceCode,
});
}).catch(err => {
if (err.type === 'TimeoutError') {
const timeoutErr = new Error(
`TimeoutError: transforming ${filePath} took longer than ` +
`${this._opts.transformTimeoutInterval / 1000} seconds.\n` +
`You can adjust timeout via the 'transformTimeoutInterval' option`
);
timeoutErr.type = 'TimeoutError';
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(error, fileName);
});
}
}
module.exports = Transformer;
Transformer.TransformError = TransformError;

View File

@ -1,77 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
var babel = require('babel-core');
var makeInternalConfig = require('babel-preset-react-native/configs/internal');
// Runs internal transforms on the given sourceCode. Note that internal
// transforms should be run after the external ones to ensure that they run on
// Javascript code
function internalTransforms(sourceCode, filename, options) {
var internalBabelConfig = makeInternalConfig(options);
if (!internalBabelConfig) {
return {
code: sourceCode,
filename: filename,
};
}
var result = babel.transform(sourceCode, Object.assign({
filename: filename,
sourceFileName: filename,
}, internalBabelConfig));
return {
code: result.code,
filename: filename,
};
}
function onExternalTransformDone(data, callback, error, externalOutput) {
if (error) {
callback(error);
return;
}
var result = internalTransforms(
externalOutput.code,
externalOutput.filename,
data.options
);
callback(null, result);
}
module.exports = function(data, callback) {
try {
if (data.options.externalTransformModulePath) {
var externalTransformModule = require(
data.options.externalTransformModulePath
);
externalTransformModule(
data,
onExternalTransformDone.bind(null, data, callback)
);
} else {
onExternalTransformDone(
data,
callback,
null,
{
code: data.sourceCode,
filename: data.filename
}
);
}
} catch (e) {
callback(e);
}
};

View File

@ -16,6 +16,16 @@ function parse(code) {
return babel.transform(code, {code: false, babelrc: false, compact: true});
}
const babelOptions = {
babelrc: false,
compact: true,
retainLines: false,
};
function normalize({code}) {
return babel.transform(code, babelOptions).code;
}
describe('constant expressions', () => {
it('can optimize conditional expressions with constant conditions', () => {
const code = `
@ -29,7 +39,7 @@ describe('constant expressions', () => {
'foo'==='bar' ? b : c,
f() ? g() : h()
);`;
expect(constantFolding('arbitrary.js', parse(code)).code)
expect(normalize(constantFolding('arbitrary.js', parse(code))))
.toEqual(`a(true,true,2,true,{},{a:1},c,f()?g():h());`);
});
@ -39,7 +49,7 @@ describe('constant expressions', () => {
var b = 'android' == 'android'
? ('production' != 'production' ? 'a' : 'A')
: 'i';`;
expect(constantFolding('arbitrary.js', parse(code)).code)
expect(normalize(constantFolding('arbitrary.js', parse(code))))
.toEqual(`var a=1;var b='A';`);
});
@ -48,7 +58,7 @@ describe('constant expressions', () => {
var a = true || 1;
var b = 'android' == 'android' &&
'production' != 'production' || null || "A";`;
expect(constantFolding('arbitrary.js', parse(code)).code)
expect(normalize(constantFolding('arbitrary.js', parse(code))))
.toEqual(`var a=true;var b="A";`);
});
@ -60,7 +70,7 @@ describe('constant expressions', () => {
var d = null || z();
var e = !1 && z();
`;
expect(constantFolding('arbitrary.js', parse(code)).code)
expect(normalize(constantFolding('arbitrary.js', parse(code))))
.toEqual(`var a="truthy";var b=z();var c=null;var d=z();var e=false;`);
});
@ -70,7 +80,7 @@ describe('constant expressions', () => {
var a = 1;
}
`;
expect(constantFolding('arbitrary.js', parse(code)).code)
expect(normalize(constantFolding('arbitrary.js', parse(code))))
.toEqual(``);
});
@ -86,7 +96,7 @@ describe('constant expressions', () => {
var a = 'b';
}
`;
expect(constantFolding('arbitrary.js', parse(code)).code)
expect(normalize(constantFolding('arbitrary.js', parse(code))))
.toEqual(`{var a=3;var b=a+4;}`);
});
@ -106,7 +116,7 @@ describe('constant expressions', () => {
}
}
`;
expect(constantFolding('arbitrary.js', parse(code)).code)
expect(normalize(constantFolding('arbitrary.js', parse(code))))
.toEqual(`{{require('c');}}`);
});
});

View File

@ -22,7 +22,7 @@ describe('Dependency extraction:', () => {
}
});
require
('more');`
('more');`;
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies)
.toEqual(['foo/bar', 'React', 'Component', 'more']);
@ -34,11 +34,11 @@ describe('Dependency extraction:', () => {
require('a');
foo.require('b');
bar.
require ( 'c').require('d')require('e')`;
require ( 'c').require('d');require('e')`;
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual(['a', 'e']);
expect(dependencyOffsets).toEqual([15, 97]);
expect(dependencyOffsets).toEqual([15, 98]);
});
it('does not extract require calls from strings', () => {
@ -51,7 +51,7 @@ describe('Dependency extraction:', () => {
return require ( "Component" );
}
});
" \\" require('more')";`
" \\" require('more')";`;
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual(['foo', 'Component']);
@ -85,12 +85,28 @@ describe('Dependency extraction:', () => {
expect(dependencyOffsets).toEqual([]);
});
it('does not extract calls to require with non-static arguments', () => {
const code = `require('foo/' + bar)`;
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual([]);
expect(dependencyOffsets).toEqual([]);
});
it('does not get confused by previous states', () => {
// yes, this was a bug
const code = `require("a");/* a comment */ var a = /[a]/.test('a');`
const code = `require("a");/* a comment */ var a = /[a]/.test('a');`;
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual(['a']);
expect(dependencyOffsets).toEqual([8]);
});
it('can handle regular expressions', () => {
const code = `require('a'); /["']/.test('foo'); require("b");`;
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual(['a', 'b']);
expect(dependencyOffsets).toEqual([8, 42]);
});
});

View File

@ -21,91 +21,37 @@ const uglify = {
jest.setMock('uglify-js', uglify);
const minify = require('../minify');
const {any} = jasmine;
const {objectContaining} = jasmine;
describe('Minification:', () => {
const fileName = '/arbitrary/file.js';
const DEPENDENCY_MARKER = '\u0002\ueffe\ue277\uead5';
const filename = '/arbitrary/file.js';
const code = 'arbitrary(code)';
let map;
beforeEach(() => {
uglify.minify.mockClear();
map = {version: 3, sources: [fileName], mappings: ''};
uglify.minify.mockReturnValue({code: '', map: '{}'});
map = {version: 3, sources: ['?'], mappings: ''};
});
it('passes the transformed code to `uglify.minify`, wrapped in an immediately invoked function expression', () => {
const code = 'arbitrary(code)';
minify('', code, {}, [], []);
expect(uglify.minify).toBeCalledWith(
`(function(){${code}}());`, any(Object));
});
it('uses the passed module locals as parameters of the IIFE', () => {
const moduleLocals = ['arbitrary', 'parameters'];
minify('', '', {}, [], moduleLocals);
expect(uglify.minify).toBeCalledWith(
`(function(${moduleLocals}){}());`, any(Object));
});
it('passes the transformed source map to `uglify.minify`', () => {
minify('', '', map, [], []);
const [, options] = uglify.minify.mock.calls[0];
expect(options.inSourceMap).toEqual(map);
});
it('passes the file name as `outSourceMap` to `uglify.minify` (uglify uses it for the `file` field on the source map)', () => {
minify(fileName, '', {}, [], []);
const [, options] = uglify.minify.mock.calls[0];
expect(options.outSourceMap).toEqual(fileName);
});
it('inserts a marker for every dependency offset before minifing', () => {
const code = `
var React = require('React');
var Immutable = require('Immutable');`;
const dependencyOffsets = [27, 67];
const expectedCode =
code.replace(/require\('/g, '$&' + DEPENDENCY_MARKER);
minify('', code, {}, dependencyOffsets, []);
expect(uglify.minify).toBeCalledWith(
`(function(){${expectedCode}}());`, any(Object));
it('passes file name, code, and source map to `uglify`', () => {
minify(filename, code, map);
expect(uglify.minify).toBeCalledWith(code, objectContaining({
fromString: true,
inSourceMap: map,
outSourceMap: filename,
}));
});
it('returns the code provided by uglify', () => {
const code = 'some(source) + code';
uglify.minify.mockReturnValue({code: `!function(a,b,c){${code}}()`});
const result = minify('', '', {}, [], []);
uglify.minify.mockReturnValue({code, map: '{}'});
const result = minify('', '', {});
expect(result.code).toBe(code);
});
it('extracts dependency offsets from the code provided by uglify', () => {
const code = `
var a=r("${DEPENDENCY_MARKER}a-dependency");
var b=r("\\x02\\ueffe\\ue277\\uead5b-dependency");
var e=r(a()?'\\u0002\\ueffe\\ue277\\uead5c-dependency'
:'\x02\ueffe\ue277\uead5d-dependency');`;
uglify.minify.mockReturnValue({code: `!function(){${code}}());`});
const result = minify('', '', {}, [], []);
expect(result.dependencyOffsets).toEqual([15, 46, 81, 114]);
});
it('returns the source map object provided by uglify', () => {
uglify.minify.mockReturnValue({map, code: ''});
const result = minify('', '', {}, [], []);
expect(result.map).toBe(map);
});
it('adds a `moduleLocals` object to the result that reflects the names of the minified module locals', () => {
const moduleLocals = ['arbitrary', 'parameters', 'here'];
uglify.minify.mockReturnValue({code: '(function(a,ll,d){}());'});
const result = minify('', '', {}, [], moduleLocals);
expect(result.moduleLocals).toEqual({
arbitrary: 'a',
parameters: 'll',
here: 'd',
});
it('parses the source map object provided by uglify and sets the sources property', () => {
uglify.minify.mockReturnValue({map: JSON.stringify(map), code: ''});
const result = minify(filename, '', {});
expect(result.map).toEqual({...map, sources: [filename]});
});
});

View File

@ -78,33 +78,6 @@ describe('code transformation worker:', () => {
});
});
it('puts an empty `moduleLocals` object on the result', done => {
transform.mockImplementation(
(_, callback) => callback(null, {code: 'arbitrary'}));
transformCode(transform, 'filename', 'code', {}, (_, data) => {
expect(data.moduleLocals).toEqual({});
done();
});
});
it('if a `moduleLocals` array is passed, the `moduleLocals` object is a key mirror of its items', done => {
transform.mockImplementation(
(_, callback) => callback(null, {code: 'arbitrary'}));
const moduleLocals =
['arbitrary', 'list', 'containing', 'variable', 'names'];
transformCode(transform, 'filename', 'code', {moduleLocals}, (_, data) => {
expect(data.moduleLocals).toEqual({
arbitrary: 'arbitrary',
list: 'list',
containing: 'containing',
variable: 'variable',
names: 'names',
});
done();
});
});
describe('dependency extraction:', () => {
let code;
@ -155,21 +128,19 @@ describe('code transformation worker:', () => {
});
describe('Minifications:', () => {
let constantFolding, extractDependencies, inline, minify, options;
let transformResult, dependencyData, moduleLocals;
let constantFolding, inline, options;
let transformResult, dependencyData;
const filename = 'arbitrary/file.js';
const foldedCode = 'arbitrary(folded(code));'
const foldedMap = {version: 3, sources: ['fold.js']}
const foldedCode = 'arbitrary(folded(code));';
const foldedMap = {version: 3, sources: ['fold.js']};
beforeEach(() => {
constantFolding = require('../constant-folding')
.mockReturnValue({code: foldedCode, map: foldedMap});
extractDependencies = require('../extract-dependencies');
inline = require('../inline');
minify = require('../minify').mockReturnValue({});
moduleLocals = ['module', 'require', 'exports'];
options = {moduleLocals, minify: true};
options = {minify: true};
dependencyData = {
dependencies: ['a', 'b', 'c'],
dependencyOffsets: [100, 120, 140]
@ -206,19 +177,6 @@ describe('code transformation worker:', () => {
});
});
it('passes the code obtained from `constant-folding` to `minify`', done => {
transformCode(transform, filename, 'code', options, () => {
expect(minify).toBeCalledWith(
filename,
foldedCode,
foldedMap,
dependencyData.dependencyOffsets,
moduleLocals
);
done();
});
});
it('uses the dependencies obtained from the optimized result', done => {
transformCode(transform, filename, 'code', options, (_, result) => {
expect(result.dependencies).toEqual(dependencyData.dependencies);
@ -226,17 +184,10 @@ describe('code transformation worker:', () => {
});
});
it('uses data produced by `minify` for the result', done => {
const minifyResult = {
code: 'minified(code)',
dependencyOffsets: [10, 30, 60],
map: {version: 3, sources: ['minified.js']},
moduleLocals: {module: 'x', require: 'y', exports: 'z'},
};
minify.mockReturnValue(minifyResult);
it('uses data produced by `constant-folding` for the result', done => {
transformCode(transform, 'filename', 'code', options, (_, result) => {
expect(result).toEqual(objectContaining(minifyResult))
expect(result)
.toEqual(objectContaining({code: foldedCode, map: foldedMap}));
done();
});
});

View File

@ -73,9 +73,12 @@ function constantFolding(filename, transformResult) {
filename,
plugins: [plugin],
inputSourceMap: transformResult.map,
sourceMaps: true,
sourceFileName: filename,
babelrc: false,
compact: true,
})
retainLines: true,
});
}
module.exports = constantFolding;

View File

@ -8,48 +8,8 @@
*/
'use strict';
const SINGLE_QUOTE = "'".charCodeAt(0);
const DOUBLE_QUOTE = '"'.charCodeAt(0);
const BACKSLASH = '\\'.charCodeAt(0);
const SLASH = '/'.charCodeAt(0);
const NEWLINE = '\n'.charCodeAt(0);
const ASTERISK = '*'.charCodeAt(0);
// dollar is the only regex special character valid in identifiers
const escapeRegExp = identifier => identifier.replace(/[$]/g, '\\$');
function binarySearch(indexes, index) {
var low = 0;
var high = indexes.length - 1;
var i = 0;
if (indexes[low] === index) {
return low;
}
while (high - low > 1) {
var current = low + ((high - low) >>> 1); // right shift divides by 2 and floors
if (index === indexes[current]) {
return current;
}
if (index > indexes[current]) {
low = current;
} else {
high = current;
}
}
return low;
}
function indexOfCharCode(string, needle, i) {
for (var charCode; (charCode = string.charCodeAt(i)); i++) {
if (charCode === needle) {
return i;
}
}
return -1;
}
const reRequire = /(?:^|[^.\s])\s*\brequire\s*\(\s*(['"])(.*?)\1/g;
const babel = require('babel-core');
const babylon = require('babylon');
/**
* Extracts dependencies (module IDs imported with the `require` function) from
@ -66,61 +26,24 @@ const reRequire = /(?:^|[^.\s])\s*\brequire\s*\(\s*(['"])(.*?)\1/g;
* The index points to the opening quote.
*/
function extractDependencies(code) {
const ranges = [0];
// are we currently in a quoted string? -> SINGLE_QUOTE or DOUBLE_QUOTE, else undefined
var currentQuote;
// scan the code for string literals and comments.
for (var i = 0, charCode; (charCode = code.charCodeAt(i)); i++) {
if (charCode === BACKSLASH) {
i += 1;
continue;
}
if (charCode === SLASH && currentQuote === undefined) {
var next = code.charCodeAt(i + 1);
var end = undefined;
if (next === SLASH) {
end = indexOfCharCode(code, NEWLINE, i + 2);
} else if (next === ASTERISK) {
end = code.indexOf('*/', i + 2) + 1; // assume valid JS input here
}
if (end === -1) {
// if the comment does not end, it goes to the end of the file
end += code.length;
}
if (end !== undefined) {
ranges.push(i, end);
i = end;
continue;
}
}
var isQuoteStart = currentQuote === undefined &&
(charCode === SINGLE_QUOTE || charCode === DOUBLE_QUOTE);
if (isQuoteStart || currentQuote === charCode) {
ranges.push(i);
currentQuote = currentQuote === charCode ? undefined : charCode;
}
}
ranges.push(i);
// extract dependencies
const ast = babylon.parse(code);
const dependencies = new Set();
const dependencyOffsets = [];
for (var match; (match = reRequire.exec(code)); ) {
// check whether the match is in a code range, and not inside of a string
// literal or a comment
if (binarySearch(ranges, match.index) % 2 === 0) {
dependencies.add(match[2]);
dependencyOffsets.push(
match[0].length - match[2].length - 2 + match.index);
}
}
return {
dependencyOffsets,
dependencies: Array.from(dependencies.values()),
};
babel.traverse(ast, {
CallExpression(path) {
const node = path.node;
const callee = node.callee;
const arg = node.arguments[0];
if (callee.type !== 'Identifier' || callee.name !== 'require' || !arg || arg.type !== 'StringLiteral') {
return;
}
dependencyOffsets.push(arg.start);
dependencies.add(arg.value);
}
});
return {dependencyOffsets, dependencies: Array.from(dependencies)};
}
module.exports = extractDependencies;

View File

@ -13,12 +13,6 @@ const extractDependencies = require('./extract-dependencies');
const inline = require('./inline');
const minify = require('./minify');
function keyMirrorFromArray(array) {
var keyMirror = {};
array.forEach(key => keyMirror[key] = key);
return keyMirror;
}
function makeTransformParams(filename, sourceCode, options) {
if (filename.endsWith('.json')) {
sourceCode = 'module.exports=' + sourceCode;
@ -28,7 +22,6 @@ function makeTransformParams(filename, sourceCode, options) {
function transformCode(transform, filename, sourceCode, options, callback) {
const params = makeTransformParams(filename, sourceCode, options.transform);
const moduleLocals = options.moduleLocals || [];
const isJson = filename.endsWith('.json');
transform(params, (error, transformed) => {
@ -52,28 +45,35 @@ function transformCode(transform, filename, sourceCode, options, callback) {
code = code.replace(/^\w+\.exports=/, '');
}
const moduleLocals = options.moduleLocals || [];
const dependencyData = isJson || options.extern
const result = isJson || options.extern
? {dependencies: [], dependencyOffsets: []}
: extractDependencies(code);
var result;
if (options.minify) {
result = minify(
filename, code, map, dependencyData.dependencyOffsets, moduleLocals);
result.dependencies = dependencyData.dependencies;
} else {
result = dependencyData;
result.code = code;
result.map = map;
result.moduleLocals = keyMirrorFromArray(moduleLocals);
}
result.code = code;
result.map = map;
callback(null, result);
});
}
module.exports = function(transform, filename, sourceCode, options, callback) {
exports.transformAndExtractDependencies = (
transform,
filename,
sourceCode,
options,
callback
) => {
transformCode(require(transform), filename, sourceCode, options || {}, callback);
};
module.exports.transformCode = transformCode; // for easier testing
exports.minify = (filename, code, sourceMap, callback) => {
var result;
try {
result = minify(filename, code, sourceMap);
} catch (error) {
callback(error);
}
callback(null, result);
};
exports.transformCode = transformCode; // for easier testing

View File

@ -89,6 +89,8 @@ function inline(filename, transformResult, options) {
filename,
plugins: [[plugin, options]],
inputSourceMap: transformResult.map,
sourceMaps: true,
sourceFileName: filename,
code: false,
babelrc: false,
compact: true,

View File

@ -10,63 +10,10 @@
const uglify = require('uglify-js');
const MAGIC_MARKER = '\u0002\ueffe\ue277\uead5';
const MAGIC_MARKER_SPLITTER =
/(?:\x02|\\u0002|\\x02)(?:\ueffe|\\ueffe)(?:\ue277|\\ue277)(?:\uead5|\\uead5)/;
// IIFE = "immediately invoked function expression"
// we wrap modules in functions to allow the minifier to mangle local variables
function wrapCodeInIIFE(code, moduleLocals) {
return `(function(${moduleLocals.join(',')}){${code}}());`;
}
function extractCodeFromIIFE(code) {
return code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'));
}
function extractModuleLocalsFromIIFE(code) {
return code.substring(code.indexOf('(', 1) + 1, code.indexOf(')')).split(',');
}
function splitFirstElementAt(array, offset) {
const first = array.shift();
array.unshift(first.slice(0, offset + 1), first.slice(offset + 1));
return array;
}
function insertMarkers(code, dependencyOffsets) {
return dependencyOffsets
.reduceRight(splitFirstElementAt, [code])
.join(MAGIC_MARKER);
}
function extractMarkers(codeWithMarkers) {
const dependencyOffsets = [];
const codeBits = codeWithMarkers.split(MAGIC_MARKER_SPLITTER);
var offset = 0;
for (var i = 0, max = codeBits.length - 1; i < max; i++) {
offset += codeBits[i].length;
dependencyOffsets.push(offset - 1);
}
return {code: codeBits.join(''), dependencyOffsets};
}
function minify(filename, code, map, dependencyOffsets, moduleLocals) {
// before minifying, code is wrapped in an immediately invoked function
// expression, so that top level variables can be shortened safely
code = wrapCodeInIIFE(
// since we don't know where the strings specifying dependencies will be
// located in the minified code, we mark them with a special marker string
// and extract them afterwards.
// That way, post-processing code can use these positions
insertMarkers(code, dependencyOffsets),
moduleLocals
);
function minify(filename, code, sourceMap) {
const minifyResult = uglify.minify(code, {
fromString: true,
inSourceMap: map,
inSourceMap: sourceMap,
outSourceMap: filename,
output: {
ascii_only: true,
@ -74,15 +21,9 @@ function minify(filename, code, map, dependencyOffsets, moduleLocals) {
},
});
const minifiedModuleLocals = extractModuleLocalsFromIIFE(minifyResult.code);
const codeWithMarkers = extractCodeFromIIFE(minifyResult.code);
const result = extractMarkers(codeWithMarkers);
result.map = minifyResult.map;
result.moduleLocals = {};
moduleLocals.forEach(
(key, i) => result.moduleLocals[key] = minifiedModuleLocals[i]);
return result;
minifyResult.map = JSON.parse(minifyResult.map);
minifyResult.map.sources = [filename];
return minifyResult;
}
module.exports = minify;

View File

@ -31,6 +31,7 @@ describe('Resolver', function() {
this.getName = jest.genMockFn();
this.getDependencies = jest.genMockFn();
this.isPolyfill = jest.genMockFn().mockReturnValue(false);
this.isJSON = jest.genMockFn().mockReturnValue(false);
});
Polyfill = jest.genMockFn().mockImpl(function() {
var polyfill = new Module();
@ -61,6 +62,10 @@ describe('Resolver', function() {
finalize() {
return Promise.resolve(this);
}
getResolvedDependencyPairs() {
return [];
}
}
function createModule(id, dependencies) {
@ -70,6 +75,12 @@ describe('Resolver', function() {
return module;
}
function createJsonModule(id) {
const module = createModule(id, []);
module.isJSON.mockReturnValue(true);
return module;
}
function createPolyfill(id, dependencies) {
var polyfill = new Polyfill({});
polyfill.getName = jest.genMockFn().mockImpl(() => Promise.resolve(id));
@ -79,6 +90,23 @@ describe('Resolver', function() {
}
describe('getDependencies', function() {
it('forwards transform options to the dependency graph', function() {
const transformOptions = {arbitrary: 'options'};
const platform = 'ios';
const entry = '/root/index.js';
DependencyGraph.prototype.getDependencies.mockImplementation(
() => Promise.reject());
new Resolver({projectRoot: '/root', })
.getDependencies(entry, {platform}, transformOptions);
expect(DependencyGraph.prototype.getDependencies).toBeCalledWith({
entryPath: entry,
platform: platform,
transformOptions: transformOptions,
recursive: true,
});
});
pit('should get dependencies with polyfills', function() {
var module = createModule('index');
var deps = [module];
@ -242,388 +270,8 @@ describe('Resolver', function() {
projectRoot: '/root',
});
var dependencies = ['x', 'y', 'z', 'a', 'b'];
/*eslint-disable */
var code = [
// single line import
"import'x';",
"import 'x';",
"import 'x' ;",
"import Default from 'x';",
"import * as All from 'x';",
"import {} from 'x';",
"import { } from 'x';",
"import {Foo} from 'x';",
"import { Foo } from 'x';",
"import { Foo, } from 'x';",
"import {Foo as Bar} from 'x';",
"import { Foo as Bar } from 'x';",
"import { Foo as Bar, } from 'x';",
"import { Foo, Bar } from 'x';",
"import { Foo, Bar, } from 'x';",
"import { Foo as Bar, Baz } from 'x';",
"import { Foo as Bar, Baz, } from 'x';",
"import { Foo, Bar as Baz } from 'x';",
"import { Foo, Bar as Baz, } from 'x';",
"import { Foo as Bar, Baz as Qux } from 'x';",
"import { Foo as Bar, Baz as Qux, } from 'x';",
"import { Foo, Bar, Baz } from 'x';",
"import { Foo, Bar, Baz, } from 'x';",
"import { Foo as Bar, Baz, Qux } from 'x';",
"import { Foo as Bar, Baz, Qux, } from 'x';",
"import { Foo, Bar as Baz, Qux } from 'x';",
"import { Foo, Bar as Baz, Qux, } from 'x';",
"import { Foo, Bar, Baz as Qux } from 'x';",
"import { Foo, Bar, Baz as Qux, } from 'x';",
"import { Foo as Bar, Baz as Qux, Norf } from 'x';",
"import { Foo as Bar, Baz as Qux, Norf, } from 'x';",
"import { Foo as Bar, Baz, Qux as Norf } from 'x';",
"import { Foo as Bar, Baz, Qux as Norf, } from 'x';",
"import { Foo, Bar as Baz, Qux as Norf } from 'x';",
"import { Foo, Bar as Baz, Qux as Norf, } from 'x';",
"import { Foo as Bar, Baz as Qux, Norf as Enuf } from 'x';",
"import { Foo as Bar, Baz as Qux, Norf as Enuf, } from 'x';",
"import Default, * as All from 'x';",
"import Default, { } from 'x';",
"import Default, { Foo } from 'x';",
"import Default, { Foo, } from 'x';",
"import Default, { Foo as Bar } from 'x';",
"import Default, { Foo as Bar, } from 'x';",
"import Default, { Foo, Bar } from 'x';",
"import Default, { Foo, Bar, } from 'x';",
"import Default, { Foo as Bar, Baz } from 'x';",
"import Default, { Foo as Bar, Baz, } from 'x';",
"import Default, { Foo, Bar as Baz } from 'x';",
"import Default, { Foo, Bar as Baz, } from 'x';",
"import Default, { Foo as Bar, Baz as Qux } from 'x';",
"import Default, { Foo as Bar, Baz as Qux, } from 'x';",
"import Default, { Foo, Bar, Baz } from 'x';",
"import Default, { Foo, Bar, Baz, } from 'x';",
"import Default, { Foo as Bar, Baz, Qux } from 'x';",
"import Default, { Foo as Bar, Baz, Qux, } from 'x';",
"import Default, { Foo, Bar as Baz, Qux } from 'x';",
"import Default, { Foo, Bar as Baz, Qux, } from 'x';",
"import Default, { Foo, Bar, Baz as Qux } from 'x';",
"import Default, { Foo, Bar, Baz as Qux, } from 'x';",
"import Default, { Foo as Bar, Baz as Qux, Norf } from 'x';",
"import Default, { Foo as Bar, Baz as Qux, Norf, } from 'x';",
"import Default, { Foo as Bar, Baz, Qux as Norf } from 'x';",
"import Default, { Foo as Bar, Baz, Qux as Norf, } from 'x';",
"import Default, { Foo, Bar as Baz, Qux as Norf } from 'x';",
"import Default, { Foo, Bar as Baz, Qux as Norf, } from 'x';",
"import Default, { Foo as Bar, Baz as Qux, Norf as NoMore } from 'x';",
"import Default, { Foo as Bar, Baz as Qux, Norf as NoMore, } from 'x';",
"import Default , { } from 'x';",
'import "x";',
'import Default from "x";',
'import * as All from "x";',
'import { } from "x";',
'import { Foo } from "x";',
'import { Foo, } from "x";',
'import { Foo as Bar } from "x";',
'import { Foo as Bar, } from "x";',
'import { Foo, Bar } from "x";',
'import { Foo, Bar, } from "x";',
'import { Foo as Bar, Baz } from "x";',
'import { Foo as Bar, Baz, } from "x";',
'import { Foo, Bar as Baz } from "x";',
'import { Foo, Bar as Baz, } from "x";',
'import { Foo as Bar, Baz as Qux } from "x";',
'import { Foo as Bar, Baz as Qux, } from "x";',
'import { Foo, Bar, Baz } from "x";',
'import { Foo, Bar, Baz, } from "x";',
'import { Foo as Bar, Baz, Qux } from "x";',
'import { Foo as Bar, Baz, Qux, } from "x";',
'import { Foo, Bar as Baz, Qux } from "x";',
'import { Foo, Bar as Baz, Qux, } from "x";',
'import { Foo, Bar, Baz as Qux } from "x";',
'import { Foo, Bar, Baz as Qux, } from "x";',
'import { Foo as Bar, Baz as Qux, Norf } from "x";',
'import { Foo as Bar, Baz as Qux, Norf, } from "x";',
'import { Foo as Bar, Baz, Qux as Norf } from "x";',
'import { Foo as Bar, Baz, Qux as Norf, } from "x";',
'import { Foo, Bar as Baz, Qux as Norf } from "x";',
'import { Foo, Bar as Baz, Qux as Norf, } from "x";',
'import { Foo as Bar, Baz as Qux, Norf as NoMore } from "x";',
'import { Foo as Bar, Baz as Qux, Norf as NoMore, } from "x";',
'import Default, * as All from "x";',
'import Default, { } from "x";',
'import Default, { Foo } from "x";',
'import Default, { Foo, } from "x";',
'import Default, { Foo as Bar } from "x";',
'import Default, { Foo as Bar, } from "x";',
'import Default, { Foo, Bar } from "x";',
'import Default, { Foo, Bar, } from "x";',
'import Default, { Foo as Bar, Baz } from "x";',
'import Default, { Foo as Bar, Baz, } from "x";',
'import Default, { Foo, Bar as Baz } from "x";',
'import Default, { Foo, Bar as Baz, } from "x";',
'import Default, { Foo as Bar, Baz as Qux } from "x";',
'import Default, { Foo as Bar, Baz as Qux, } from "x";',
'import Default, { Foo, Bar, Baz } from "x";',
'import Default, { Foo, Bar, Baz, } from "x";',
'import Default, { Foo as Bar, Baz, Qux } from "x";',
'import Default, { Foo as Bar, Baz, Qux, } from "x";',
'import Default, { Foo, Bar as Baz, Qux } from "x";',
'import Default, { Foo, Bar as Baz, Qux, } from "x";',
'import Default, { Foo, Bar, Baz as Qux } from "x";',
'import Default, { Foo, Bar, Baz as Qux, } from "x";',
'import Default, { Foo as Bar, Baz as Qux, Norf } from "x";',
'import Default, { Foo as Bar, Baz as Qux, Norf, } from "x";',
'import Default, { Foo as Bar, Baz, Qux as Norf } from "x";',
'import Default, { Foo as Bar, Baz, Qux as Norf, } from "x";',
'import Default, { Foo, Bar as Baz, Qux as Norf } from "x";',
'import Default, { Foo, Bar as Baz, Qux as Norf, } from "x";',
'import Default, { Foo as Bar, Baz as Qux, Norf as Enuf } from "x";',
'import Default, { Foo as Bar, Baz as Qux, Norf as Enuf, } from "x";',
'import Default from "y";',
'import * as All from \'z\';',
// import with support for new lines
"import { Foo,\n Bar }\n from 'x';",
"import { \nFoo,\nBar,\n }\n from 'x';",
"import { Foo as Bar,\n Baz\n }\n from 'x';",
"import { \nFoo as Bar,\n Baz\n, }\n from 'x';",
"import { Foo,\n Bar as Baz\n }\n from 'x';",
"import { Foo,\n Bar as Baz,\n }\n from 'x';",
"import { Foo as Bar,\n Baz as Qux\n }\n from 'x';",
"import { Foo as Bar,\n Baz as Qux,\n }\n from 'x';",
"import { Foo,\n Bar,\n Baz }\n from 'x';",
"import { Foo,\n Bar,\n Baz,\n }\n from 'x';",
"import { Foo as Bar,\n Baz,\n Qux\n }\n from 'x';",
"import { Foo as Bar,\n Baz,\n Qux,\n }\n from 'x';",
"import { Foo,\n Bar as Baz,\n Qux\n }\n from 'x';",
"import { Foo,\n Bar as Baz,\n Qux,\n }\n from 'x';",
"import { Foo,\n Bar,\n Baz as Qux\n }\n from 'x';",
"import { Foo,\n Bar,\n Baz as Qux,\n }\n from 'x';",
"import { Foo as Bar,\n Baz as Qux,\n Norf\n }\n from 'x';",
"import { Foo as Bar,\n Baz as Qux,\n Norf,\n }\n from 'x';",
"import { Foo as Bar,\n Baz,\n Qux as Norf\n }\n from 'x';",
"import { Foo as Bar,\n Baz,\n Qux as Norf,\n }\n from 'x';",
"import { Foo,\n Bar as Baz,\n Qux as Norf\n }\n from 'x';",
"import { Foo,\n Bar as Baz,\n Qux as Norf,\n }\n from 'x';",
"import { Foo as Bar,\n Baz as Qux,\n Norf as Enuf\n }\n from 'x';",
"import { Foo as Bar,\n Baz as Qux,\n Norf as Enuf,\n }\n from 'x';",
"import Default,\n * as All from 'x';",
"import Default,\n { } from 'x';",
"import Default,\n { Foo\n }\n from 'x';",
"import Default,\n { Foo,\n }\n from 'x';",
"import Default,\n { Foo as Bar\n }\n from 'x';",
"import Default,\n { Foo as Bar,\n }\n from 'x';",
"import Default,\n { Foo,\n Bar\n } from\n 'x';",
"import Default,\n { Foo,\n Bar,\n } from\n 'x';",
"import Default,\n { Foo as Bar,\n Baz\n }\n from 'x';",
"import Default,\n { Foo as Bar,\n Baz,\n }\n from 'x';",
"import Default,\n { Foo,\n Bar as Baz\n }\n from 'x';",
"import Default,\n { Foo,\n Bar as Baz,\n }\n from 'x';",
"import Default,\n { Foo as Bar,\n Baz as Qux\n }\n from 'x';",
"import Default,\n { Foo as Bar,\n Baz as Qux,\n }\n from 'x';",
"import Default,\n { Foo,\n Bar,\n Baz\n }\n from 'x';",
"import Default,\n { Foo,\n Bar,\n Baz,\n }\n from 'x';",
"import Default,\n { Foo as Bar,\n Baz,\n Qux\n }\n from 'x';",
"import Default,\n { Foo as Bar,\n Baz,\n Qux,\n }\n from 'x';",
"import Default,\n { Foo,\n Bar as Baz,\n Qux\n }\n from 'x';",
"import Default,\n { Foo,\n Bar as Baz,\n Qux,\n }\n from 'x';",
"import Default,\n { Foo,\n Bar,\n Baz as Qux\n }\n from 'x';",
"import Default,\n { Foo,\n Bar,\n Baz as Qux,\n }\n from 'x';",
"import Default,\n { Foo as Bar,\n Baz as Qux,\n Norf\n }\n from 'x';",
"import Default,\n { Foo as Bar,\n Baz as Qux,\n Norf,\n }\n from 'x';",
"import Default,\n { Foo as Bar,\n Baz,\n Qux as Norf }\n from 'x';",
"import Default,\n { Foo as Bar,\n Baz,\n Qux as Norf, }\n from 'x';",
"import Default,\n { Foo, Bar as Baz,\n Qux as Norf }\n from 'x';",
"import Default,\n { Foo, Bar as Baz,\n Qux as Norf, }\n from 'x';",
"import Default,\n { Foo as Bar,\n Baz as Qux,\n Norf as NoMore\n }\n from 'x';",
"import Default,\n { Foo as Bar,\n Baz as Qux,\n Norf as NoMore,\n }\n from 'x';",
"import Default\n , { } from 'x';",
// single line export
"export'x';",
"export 'x';",
"export 'x' ;",
"export Default from 'x';",
"export * as All from 'x';",
"export {} from 'x';",
"export { } from 'x';",
"export {Foo} from 'x';",
"export { Foo } from 'x';",
"export { Foo, } from 'x';",
"export {Foo as Bar} from 'x';",
"export { Foo as Bar } from 'x';",
"export { Foo as Bar, } from 'x';",
"export { Foo, Bar } from 'x';",
"export { Foo, Bar, } from 'x';",
"export { Foo as Bar, Baz } from 'x';",
"export { Foo as Bar, Baz, } from 'x';",
"export { Foo, Bar as Baz } from 'x';",
"export { Foo, Bar as Baz, } from 'x';",
"export { Foo as Bar, Baz as Qux } from 'x';",
"export { Foo as Bar, Baz as Qux, } from 'x';",
"export { Foo, Bar, Baz } from 'x';",
"export { Foo, Bar, Baz, } from 'x';",
"export { Foo as Bar, Baz, Qux } from 'x';",
"export { Foo as Bar, Baz, Qux, } from 'x';",
"export { Foo, Bar as Baz, Qux } from 'x';",
"export { Foo, Bar as Baz, Qux, } from 'x';",
"export { Foo, Bar, Baz as Qux } from 'x';",
"export { Foo, Bar, Baz as Qux, } from 'x';",
"export { Foo as Bar, Baz as Qux, Norf } from 'x';",
"export { Foo as Bar, Baz as Qux, Norf, } from 'x';",
"export { Foo as Bar, Baz, Qux as Norf } from 'x';",
"export { Foo as Bar, Baz, Qux as Norf, } from 'x';",
"export { Foo, Bar as Baz, Qux as Norf } from 'x';",
"export { Foo, Bar as Baz, Qux as Norf, } from 'x';",
"export { Foo as Bar, Baz as Qux, Norf as Enuf } from 'x';",
"export { Foo as Bar, Baz as Qux, Norf as Enuf, } from 'x';",
"export Default, * as All from 'x';",
"export Default, { } from 'x';",
"export Default, { Foo } from 'x';",
"export Default, { Foo, } from 'x';",
"export Default, { Foo as Bar } from 'x';",
"export Default, { Foo as Bar, } from 'x';",
"export Default, { Foo, Bar } from 'x';",
"export Default, { Foo, Bar, } from 'x';",
"export Default, { Foo as Bar, Baz } from 'x';",
"export Default, { Foo as Bar, Baz, } from 'x';",
"export Default, { Foo, Bar as Baz } from 'x';",
"export Default, { Foo, Bar as Baz, } from 'x';",
"export Default, { Foo as Bar, Baz as Qux } from 'x';",
"export Default, { Foo as Bar, Baz as Qux, } from 'x';",
"export Default, { Foo, Bar, Baz } from 'x';",
"export Default, { Foo, Bar, Baz, } from 'x';",
"export Default, { Foo as Bar, Baz, Qux } from 'x';",
"export Default, { Foo as Bar, Baz, Qux, } from 'x';",
"export Default, { Foo, Bar as Baz, Qux } from 'x';",
"export Default, { Foo, Bar as Baz, Qux, } from 'x';",
"export Default, { Foo, Bar, Baz as Qux } from 'x';",
"export Default, { Foo, Bar, Baz as Qux, } from 'x';",
"export Default, { Foo as Bar, Baz as Qux, Norf } from 'x';",
"export Default, { Foo as Bar, Baz as Qux, Norf, } from 'x';",
"export Default, { Foo as Bar, Baz, Qux as Norf } from 'x';",
"export Default, { Foo as Bar, Baz, Qux as Norf, } from 'x';",
"export Default, { Foo, Bar as Baz, Qux as Norf } from 'x';",
"export Default, { Foo, Bar as Baz, Qux as Norf, } from 'x';",
"export Default, { Foo as Bar, Baz as Qux, Norf as NoMore } from 'x';",
"export Default, { Foo as Bar, Baz as Qux, Norf as NoMore, } from 'x';",
"export Default , { } from 'x';",
'export "x";',
'export Default from "x";',
'export * as All from "x";',
'export { } from "x";',
'export { Foo } from "x";',
'export { Foo, } from "x";',
'export { Foo as Bar } from "x";',
'export { Foo as Bar, } from "x";',
'export { Foo, Bar } from "x";',
'export { Foo, Bar, } from "x";',
'export { Foo as Bar, Baz } from "x";',
'export { Foo as Bar, Baz, } from "x";',
'export { Foo, Bar as Baz } from "x";',
'export { Foo, Bar as Baz, } from "x";',
'export { Foo as Bar, Baz as Qux } from "x";',
'export { Foo as Bar, Baz as Qux, } from "x";',
'export { Foo, Bar, Baz } from "x";',
'export { Foo, Bar, Baz, } from "x";',
'export { Foo as Bar, Baz, Qux } from "x";',
'export { Foo as Bar, Baz, Qux, } from "x";',
'export { Foo, Bar as Baz, Qux } from "x";',
'export { Foo, Bar as Baz, Qux, } from "x";',
'export { Foo, Bar, Baz as Qux } from "x";',
'export { Foo, Bar, Baz as Qux, } from "x";',
'export { Foo as Bar, Baz as Qux, Norf } from "x";',
'export { Foo as Bar, Baz as Qux, Norf, } from "x";',
'export { Foo as Bar, Baz, Qux as Norf } from "x";',
'export { Foo as Bar, Baz, Qux as Norf, } from "x";',
'export { Foo, Bar as Baz, Qux as Norf } from "x";',
'export { Foo, Bar as Baz, Qux as Norf, } from "x";',
'export { Foo as Bar, Baz as Qux, Norf as NoMore } from "x";',
'export { Foo as Bar, Baz as Qux, Norf as NoMore, } from "x";',
'export Default, * as All from "x";',
'export Default, { } from "x";',
'export Default, { Foo } from "x";',
'export Default, { Foo, } from "x";',
'export Default, { Foo as Bar } from "x";',
'export Default, { Foo as Bar, } from "x";',
'export Default, { Foo, Bar } from "x";',
'export Default, { Foo, Bar, } from "x";',
'export Default, { Foo as Bar, Baz } from "x";',
'export Default, { Foo as Bar, Baz, } from "x";',
'export Default, { Foo, Bar as Baz } from "x";',
'export Default, { Foo, Bar as Baz, } from "x";',
'export Default, { Foo as Bar, Baz as Qux } from "x";',
'export Default, { Foo as Bar, Baz as Qux, } from "x";',
'export Default, { Foo, Bar, Baz } from "x";',
'export Default, { Foo, Bar, Baz, } from "x";',
'export Default, { Foo as Bar, Baz, Qux } from "x";',
'export Default, { Foo as Bar, Baz, Qux, } from "x";',
'export Default, { Foo, Bar as Baz, Qux } from "x";',
'export Default, { Foo, Bar as Baz, Qux, } from "x";',
'export Default, { Foo, Bar, Baz as Qux } from "x";',
'export Default, { Foo, Bar, Baz as Qux, } from "x";',
'export Default, { Foo as Bar, Baz as Qux, Norf } from "x";',
'export Default, { Foo as Bar, Baz as Qux, Norf, } from "x";',
'export Default, { Foo as Bar, Baz, Qux as Norf } from "x";',
'export Default, { Foo as Bar, Baz, Qux as Norf, } from "x";',
'export Default, { Foo, Bar as Baz, Qux as Norf } from "x";',
'export Default, { Foo, Bar as Baz, Qux as Norf, } from "x";',
'export Default, { Foo as Bar, Baz as Qux, Norf as Enuf } from "x";',
'export Default, { Foo as Bar, Baz as Qux, Norf as Enuf, } from "x";',
'export Default from "y";',
'export * as All from \'z\';',
// export with support for new lines
"export { Foo,\n Bar }\n from 'x';",
"export { \nFoo,\nBar,\n }\n from 'x';",
"export { Foo as Bar,\n Baz\n }\n from 'x';",
"export { \nFoo as Bar,\n Baz\n, }\n from 'x';",
"export { Foo,\n Bar as Baz\n }\n from 'x';",
"export { Foo,\n Bar as Baz,\n }\n from 'x';",
"export { Foo as Bar,\n Baz as Qux\n }\n from 'x';",
"export { Foo as Bar,\n Baz as Qux,\n }\n from 'x';",
"export { Foo,\n Bar,\n Baz }\n from 'x';",
"export { Foo,\n Bar,\n Baz,\n }\n from 'x';",
"export { Foo as Bar,\n Baz,\n Qux\n }\n from 'x';",
"export { Foo as Bar,\n Baz,\n Qux,\n }\n from 'x';",
"export { Foo,\n Bar as Baz,\n Qux\n }\n from 'x';",
"export { Foo,\n Bar as Baz,\n Qux,\n }\n from 'x';",
"export { Foo,\n Bar,\n Baz as Qux\n }\n from 'x';",
"export { Foo,\n Bar,\n Baz as Qux,\n }\n from 'x';",
"export { Foo as Bar,\n Baz as Qux,\n Norf\n }\n from 'x';",
"export { Foo as Bar,\n Baz as Qux,\n Norf,\n }\n from 'x';",
"export { Foo as Bar,\n Baz,\n Qux as Norf\n }\n from 'x';",
"export { Foo as Bar,\n Baz,\n Qux as Norf,\n }\n from 'x';",
"export { Foo,\n Bar as Baz,\n Qux as Norf\n }\n from 'x';",
"export { Foo,\n Bar as Baz,\n Qux as Norf,\n }\n from 'x';",
"export { Foo as Bar,\n Baz as Qux,\n Norf as Enuf\n }\n from 'x';",
"export { Foo as Bar,\n Baz as Qux,\n Norf as Enuf,\n }\n from 'x';",
"export Default,\n * as All from 'x';",
"export Default,\n { } from 'x';",
"export Default,\n { Foo\n }\n from 'x';",
"export Default,\n { Foo,\n }\n from 'x';",
"export Default,\n { Foo as Bar\n }\n from 'x';",
"export Default,\n { Foo as Bar,\n }\n from 'x';",
"export Default,\n { Foo,\n Bar\n } from\n 'x';",
"export Default,\n { Foo,\n Bar,\n } from\n 'x';",
"export Default,\n { Foo as Bar,\n Baz\n }\n from 'x';",
"export Default,\n { Foo as Bar,\n Baz,\n }\n from 'x';",
"export Default,\n { Foo,\n Bar as Baz\n }\n from 'x';",
"export Default,\n { Foo,\n Bar as Baz,\n }\n from 'x';",
"export Default,\n { Foo as Bar,\n Baz as Qux\n }\n from 'x';",
"export Default,\n { Foo as Bar,\n Baz as Qux,\n }\n from 'x';",
"export Default,\n { Foo,\n Bar,\n Baz\n }\n from 'x';",
"export Default,\n { Foo,\n Bar,\n Baz,\n }\n from 'x';",
"export Default,\n { Foo as Bar,\n Baz,\n Qux\n }\n from 'x';",
"export Default,\n { Foo as Bar,\n Baz,\n Qux,\n }\n from 'x';",
"export Default,\n { Foo,\n Bar as Baz,\n Qux\n }\n from 'x';",
"export Default,\n { Foo,\n Bar as Baz,\n Qux,\n }\n from 'x';",
"export Default,\n { Foo,\n Bar,\n Baz as Qux\n }\n from 'x';",
"export Default,\n { Foo,\n Bar,\n Baz as Qux,\n }\n from 'x';",
"export Default,\n { Foo as Bar,\n Baz as Qux,\n Norf\n }\n from 'x';",
"export Default,\n { Foo as Bar,\n Baz as Qux,\n Norf,\n }\n from 'x';",
"export Default,\n { Foo as Bar,\n Baz,\n Qux as Norf }\n from 'x';",
"export Default,\n { Foo as Bar,\n Baz,\n Qux as Norf, }\n from 'x';",
"export Default,\n { Foo, Bar as Baz,\n Qux as Norf }\n from 'x';",
"export Default,\n { Foo, Bar as Baz,\n Qux as Norf, }\n from 'x';",
"export Default,\n { Foo as Bar,\n Baz as Qux,\n Norf as NoMore\n }\n from 'x';",
"export Default,\n { Foo as Bar,\n Baz as Qux,\n Norf as NoMore,\n }\n from 'x';",
"export Default\n , { } from 'x';",
// require
'require("x")',
'require("y")',
@ -633,8 +281,16 @@ describe('Resolver', function() {
].join('\n');
/*eslint-disable */
const module = createModule('test module', ['x', 'y']);
function *findDependencyOffsets() {
const re = /(['"']).*?\1/g;
let match;
while ((match = re.exec(code))) {
yield match.index;
}
}
const dependencyOffsets = Array.from(findDependencyOffsets());
const module = createModule('test module', ['x', 'y']);
const resolutionResponse = new ResolutionResponseMock({
dependencies: [module],
mainModuleId: 'test module',
@ -647,393 +303,15 @@ describe('Resolver', function() {
];
}
return depResolver.wrapModule(
return depResolver.wrapModule({
resolutionResponse,
createModule('test module', ['x', 'y']),
code
).then(processedCode => {
expect(processedCode.name).toEqual('test module');
expect(processedCode.code).toEqual([
'__d(\'test module\',function(global, require,' +
' module, exports) { ' +
// single line import
"import'x';",
"import 'changed';",
"import 'changed' ;",
"import Default from 'changed';",
"import * as All from 'changed';",
"import {} from 'changed';",
"import { } from 'changed';",
"import {Foo} from 'changed';",
"import { Foo } from 'changed';",
"import { Foo, } from 'changed';",
"import {Foo as Bar} from 'changed';",
"import { Foo as Bar } from 'changed';",
"import { Foo as Bar, } from 'changed';",
"import { Foo, Bar } from 'changed';",
"import { Foo, Bar, } from 'changed';",
"import { Foo as Bar, Baz } from 'changed';",
"import { Foo as Bar, Baz, } from 'changed';",
"import { Foo, Bar as Baz } from 'changed';",
"import { Foo, Bar as Baz, } from 'changed';",
"import { Foo as Bar, Baz as Qux } from 'changed';",
"import { Foo as Bar, Baz as Qux, } from 'changed';",
"import { Foo, Bar, Baz } from 'changed';",
"import { Foo, Bar, Baz, } from 'changed';",
"import { Foo as Bar, Baz, Qux } from 'changed';",
"import { Foo as Bar, Baz, Qux, } from 'changed';",
"import { Foo, Bar as Baz, Qux } from 'changed';",
"import { Foo, Bar as Baz, Qux, } from 'changed';",
"import { Foo, Bar, Baz as Qux } from 'changed';",
"import { Foo, Bar, Baz as Qux, } from 'changed';",
"import { Foo as Bar, Baz as Qux, Norf } from 'changed';",
"import { Foo as Bar, Baz as Qux, Norf, } from 'changed';",
"import { Foo as Bar, Baz, Qux as Norf } from 'changed';",
"import { Foo as Bar, Baz, Qux as Norf, } from 'changed';",
"import { Foo, Bar as Baz, Qux as Norf } from 'changed';",
"import { Foo, Bar as Baz, Qux as Norf, } from 'changed';",
"import { Foo as Bar, Baz as Qux, Norf as Enuf } from 'changed';",
"import { Foo as Bar, Baz as Qux, Norf as Enuf, } from 'changed';",
"import Default, * as All from 'changed';",
"import Default, { } from 'changed';",
"import Default, { Foo } from 'changed';",
"import Default, { Foo, } from 'changed';",
"import Default, { Foo as Bar } from 'changed';",
"import Default, { Foo as Bar, } from 'changed';",
"import Default, { Foo, Bar } from 'changed';",
"import Default, { Foo, Bar, } from 'changed';",
"import Default, { Foo as Bar, Baz } from 'changed';",
"import Default, { Foo as Bar, Baz, } from 'changed';",
"import Default, { Foo, Bar as Baz } from 'changed';",
"import Default, { Foo, Bar as Baz, } from 'changed';",
"import Default, { Foo as Bar, Baz as Qux } from 'changed';",
"import Default, { Foo as Bar, Baz as Qux, } from 'changed';",
"import Default, { Foo, Bar, Baz } from 'changed';",
"import Default, { Foo, Bar, Baz, } from 'changed';",
"import Default, { Foo as Bar, Baz, Qux } from 'changed';",
"import Default, { Foo as Bar, Baz, Qux, } from 'changed';",
"import Default, { Foo, Bar as Baz, Qux } from 'changed';",
"import Default, { Foo, Bar as Baz, Qux, } from 'changed';",
"import Default, { Foo, Bar, Baz as Qux } from 'changed';",
"import Default, { Foo, Bar, Baz as Qux, } from 'changed';",
"import Default, { Foo as Bar, Baz as Qux, Norf } from 'changed';",
"import Default, { Foo as Bar, Baz as Qux, Norf, } from 'changed';",
"import Default, { Foo as Bar, Baz, Qux as Norf } from 'changed';",
"import Default, { Foo as Bar, Baz, Qux as Norf, } from 'changed';",
"import Default, { Foo, Bar as Baz, Qux as Norf } from 'changed';",
"import Default, { Foo, Bar as Baz, Qux as Norf, } from 'changed';",
"import Default, { Foo as Bar, Baz as Qux, Norf as NoMore } from 'changed';",
"import Default, { Foo as Bar, Baz as Qux, Norf as NoMore, } from 'changed';",
"import Default , { } from 'changed';",
'import "changed";',
'import Default from "changed";',
'import * as All from "changed";',
'import { } from "changed";',
'import { Foo } from "changed";',
'import { Foo, } from "changed";',
'import { Foo as Bar } from "changed";',
'import { Foo as Bar, } from "changed";',
'import { Foo, Bar } from "changed";',
'import { Foo, Bar, } from "changed";',
'import { Foo as Bar, Baz } from "changed";',
'import { Foo as Bar, Baz, } from "changed";',
'import { Foo, Bar as Baz } from "changed";',
'import { Foo, Bar as Baz, } from "changed";',
'import { Foo as Bar, Baz as Qux } from "changed";',
'import { Foo as Bar, Baz as Qux, } from "changed";',
'import { Foo, Bar, Baz } from "changed";',
'import { Foo, Bar, Baz, } from "changed";',
'import { Foo as Bar, Baz, Qux } from "changed";',
'import { Foo as Bar, Baz, Qux, } from "changed";',
'import { Foo, Bar as Baz, Qux } from "changed";',
'import { Foo, Bar as Baz, Qux, } from "changed";',
'import { Foo, Bar, Baz as Qux } from "changed";',
'import { Foo, Bar, Baz as Qux, } from "changed";',
'import { Foo as Bar, Baz as Qux, Norf } from "changed";',
'import { Foo as Bar, Baz as Qux, Norf, } from "changed";',
'import { Foo as Bar, Baz, Qux as Norf } from "changed";',
'import { Foo as Bar, Baz, Qux as Norf, } from "changed";',
'import { Foo, Bar as Baz, Qux as Norf } from "changed";',
'import { Foo, Bar as Baz, Qux as Norf, } from "changed";',
'import { Foo as Bar, Baz as Qux, Norf as NoMore } from "changed";',
'import { Foo as Bar, Baz as Qux, Norf as NoMore, } from "changed";',
'import Default, * as All from "changed";',
'import Default, { } from "changed";',
'import Default, { Foo } from "changed";',
'import Default, { Foo, } from "changed";',
'import Default, { Foo as Bar } from "changed";',
'import Default, { Foo as Bar, } from "changed";',
'import Default, { Foo, Bar } from "changed";',
'import Default, { Foo, Bar, } from "changed";',
'import Default, { Foo as Bar, Baz } from "changed";',
'import Default, { Foo as Bar, Baz, } from "changed";',
'import Default, { Foo, Bar as Baz } from "changed";',
'import Default, { Foo, Bar as Baz, } from "changed";',
'import Default, { Foo as Bar, Baz as Qux } from "changed";',
'import Default, { Foo as Bar, Baz as Qux, } from "changed";',
'import Default, { Foo, Bar, Baz } from "changed";',
'import Default, { Foo, Bar, Baz, } from "changed";',
'import Default, { Foo as Bar, Baz, Qux } from "changed";',
'import Default, { Foo as Bar, Baz, Qux, } from "changed";',
'import Default, { Foo, Bar as Baz, Qux } from "changed";',
'import Default, { Foo, Bar as Baz, Qux, } from "changed";',
'import Default, { Foo, Bar, Baz as Qux } from "changed";',
'import Default, { Foo, Bar, Baz as Qux, } from "changed";',
'import Default, { Foo as Bar, Baz as Qux, Norf } from "changed";',
'import Default, { Foo as Bar, Baz as Qux, Norf, } from "changed";',
'import Default, { Foo as Bar, Baz, Qux as Norf } from "changed";',
'import Default, { Foo as Bar, Baz, Qux as Norf, } from "changed";',
'import Default, { Foo, Bar as Baz, Qux as Norf } from "changed";',
'import Default, { Foo, Bar as Baz, Qux as Norf, } from "changed";',
'import Default, { Foo as Bar, Baz as Qux, Norf as Enuf } from "changed";',
'import Default, { Foo as Bar, Baz as Qux, Norf as Enuf, } from "changed";',
'import Default from "Y";',
'import * as All from \'z\';',
// import with support for new lines
"import { Foo,\n Bar }\n from 'changed';",
"import { \nFoo,\nBar,\n }\n from 'changed';",
"import { Foo as Bar,\n Baz\n }\n from 'changed';",
"import { \nFoo as Bar,\n Baz\n, }\n from 'changed';",
"import { Foo,\n Bar as Baz\n }\n from 'changed';",
"import { Foo,\n Bar as Baz,\n }\n from 'changed';",
"import { Foo as Bar,\n Baz as Qux\n }\n from 'changed';",
"import { Foo as Bar,\n Baz as Qux,\n }\n from 'changed';",
"import { Foo,\n Bar,\n Baz }\n from 'changed';",
"import { Foo,\n Bar,\n Baz,\n }\n from 'changed';",
"import { Foo as Bar,\n Baz,\n Qux\n }\n from 'changed';",
"import { Foo as Bar,\n Baz,\n Qux,\n }\n from 'changed';",
"import { Foo,\n Bar as Baz,\n Qux\n }\n from 'changed';",
"import { Foo,\n Bar as Baz,\n Qux,\n }\n from 'changed';",
"import { Foo,\n Bar,\n Baz as Qux\n }\n from 'changed';",
"import { Foo,\n Bar,\n Baz as Qux,\n }\n from 'changed';",
"import { Foo as Bar,\n Baz as Qux,\n Norf\n }\n from 'changed';",
"import { Foo as Bar,\n Baz as Qux,\n Norf,\n }\n from 'changed';",
"import { Foo as Bar,\n Baz,\n Qux as Norf\n }\n from 'changed';",
"import { Foo as Bar,\n Baz,\n Qux as Norf,\n }\n from 'changed';",
"import { Foo,\n Bar as Baz,\n Qux as Norf\n }\n from 'changed';",
"import { Foo,\n Bar as Baz,\n Qux as Norf,\n }\n from 'changed';",
"import { Foo as Bar,\n Baz as Qux,\n Norf as Enuf\n }\n from 'changed';",
"import { Foo as Bar,\n Baz as Qux,\n Norf as Enuf,\n }\n from 'changed';",
"import Default,\n * as All from 'changed';",
"import Default,\n { } from 'changed';",
"import Default,\n { Foo\n }\n from 'changed';",
"import Default,\n { Foo,\n }\n from 'changed';",
"import Default,\n { Foo as Bar\n }\n from 'changed';",
"import Default,\n { Foo as Bar,\n }\n from 'changed';",
"import Default,\n { Foo,\n Bar\n } from\n 'changed';",
"import Default,\n { Foo,\n Bar,\n } from\n 'changed';",
"import Default,\n { Foo as Bar,\n Baz\n }\n from 'changed';",
"import Default,\n { Foo as Bar,\n Baz,\n }\n from 'changed';",
"import Default,\n { Foo,\n Bar as Baz\n }\n from 'changed';",
"import Default,\n { Foo,\n Bar as Baz,\n }\n from 'changed';",
"import Default,\n { Foo as Bar,\n Baz as Qux\n }\n from 'changed';",
"import Default,\n { Foo as Bar,\n Baz as Qux,\n }\n from 'changed';",
"import Default,\n { Foo,\n Bar,\n Baz\n }\n from 'changed';",
"import Default,\n { Foo,\n Bar,\n Baz,\n }\n from 'changed';",
"import Default,\n { Foo as Bar,\n Baz,\n Qux\n }\n from 'changed';",
"import Default,\n { Foo as Bar,\n Baz,\n Qux,\n }\n from 'changed';",
"import Default,\n { Foo,\n Bar as Baz,\n Qux\n }\n from 'changed';",
"import Default,\n { Foo,\n Bar as Baz,\n Qux,\n }\n from 'changed';",
"import Default,\n { Foo,\n Bar,\n Baz as Qux\n }\n from 'changed';",
"import Default,\n { Foo,\n Bar,\n Baz as Qux,\n }\n from 'changed';",
"import Default,\n { Foo as Bar,\n Baz as Qux,\n Norf\n }\n from 'changed';",
"import Default,\n { Foo as Bar,\n Baz as Qux,\n Norf,\n }\n from 'changed';",
"import Default,\n { Foo as Bar,\n Baz,\n Qux as Norf }\n from 'changed';",
"import Default,\n { Foo as Bar,\n Baz,\n Qux as Norf, }\n from 'changed';",
"import Default,\n { Foo, Bar as Baz,\n Qux as Norf }\n from 'changed';",
"import Default,\n { Foo, Bar as Baz,\n Qux as Norf, }\n from 'changed';",
"import Default,\n { Foo as Bar,\n Baz as Qux,\n Norf as NoMore\n }\n from 'changed';",
"import Default,\n { Foo as Bar,\n Baz as Qux,\n Norf as NoMore,\n }\n from 'changed';",
"import Default\n , { } from 'changed';",
// single line export
"export'x';",
"export 'changed';",
"export 'changed' ;",
"export Default from 'changed';",
"export * as All from 'changed';",
"export {} from 'changed';",
"export { } from 'changed';",
"export {Foo} from 'changed';",
"export { Foo } from 'changed';",
"export { Foo, } from 'changed';",
"export {Foo as Bar} from 'changed';",
"export { Foo as Bar } from 'changed';",
"export { Foo as Bar, } from 'changed';",
"export { Foo, Bar } from 'changed';",
"export { Foo, Bar, } from 'changed';",
"export { Foo as Bar, Baz } from 'changed';",
"export { Foo as Bar, Baz, } from 'changed';",
"export { Foo, Bar as Baz } from 'changed';",
"export { Foo, Bar as Baz, } from 'changed';",
"export { Foo as Bar, Baz as Qux } from 'changed';",
"export { Foo as Bar, Baz as Qux, } from 'changed';",
"export { Foo, Bar, Baz } from 'changed';",
"export { Foo, Bar, Baz, } from 'changed';",
"export { Foo as Bar, Baz, Qux } from 'changed';",
"export { Foo as Bar, Baz, Qux, } from 'changed';",
"export { Foo, Bar as Baz, Qux } from 'changed';",
"export { Foo, Bar as Baz, Qux, } from 'changed';",
"export { Foo, Bar, Baz as Qux } from 'changed';",
"export { Foo, Bar, Baz as Qux, } from 'changed';",
"export { Foo as Bar, Baz as Qux, Norf } from 'changed';",
"export { Foo as Bar, Baz as Qux, Norf, } from 'changed';",
"export { Foo as Bar, Baz, Qux as Norf } from 'changed';",
"export { Foo as Bar, Baz, Qux as Norf, } from 'changed';",
"export { Foo, Bar as Baz, Qux as Norf } from 'changed';",
"export { Foo, Bar as Baz, Qux as Norf, } from 'changed';",
"export { Foo as Bar, Baz as Qux, Norf as Enuf } from 'changed';",
"export { Foo as Bar, Baz as Qux, Norf as Enuf, } from 'changed';",
"export Default, * as All from 'changed';",
"export Default, { } from 'changed';",
"export Default, { Foo } from 'changed';",
"export Default, { Foo, } from 'changed';",
"export Default, { Foo as Bar } from 'changed';",
"export Default, { Foo as Bar, } from 'changed';",
"export Default, { Foo, Bar } from 'changed';",
"export Default, { Foo, Bar, } from 'changed';",
"export Default, { Foo as Bar, Baz } from 'changed';",
"export Default, { Foo as Bar, Baz, } from 'changed';",
"export Default, { Foo, Bar as Baz } from 'changed';",
"export Default, { Foo, Bar as Baz, } from 'changed';",
"export Default, { Foo as Bar, Baz as Qux } from 'changed';",
"export Default, { Foo as Bar, Baz as Qux, } from 'changed';",
"export Default, { Foo, Bar, Baz } from 'changed';",
"export Default, { Foo, Bar, Baz, } from 'changed';",
"export Default, { Foo as Bar, Baz, Qux } from 'changed';",
"export Default, { Foo as Bar, Baz, Qux, } from 'changed';",
"export Default, { Foo, Bar as Baz, Qux } from 'changed';",
"export Default, { Foo, Bar as Baz, Qux, } from 'changed';",
"export Default, { Foo, Bar, Baz as Qux } from 'changed';",
"export Default, { Foo, Bar, Baz as Qux, } from 'changed';",
"export Default, { Foo as Bar, Baz as Qux, Norf } from 'changed';",
"export Default, { Foo as Bar, Baz as Qux, Norf, } from 'changed';",
"export Default, { Foo as Bar, Baz, Qux as Norf } from 'changed';",
"export Default, { Foo as Bar, Baz, Qux as Norf, } from 'changed';",
"export Default, { Foo, Bar as Baz, Qux as Norf } from 'changed';",
"export Default, { Foo, Bar as Baz, Qux as Norf, } from 'changed';",
"export Default, { Foo as Bar, Baz as Qux, Norf as NoMore } from 'changed';",
"export Default, { Foo as Bar, Baz as Qux, Norf as NoMore, } from 'changed';",
"export Default , { } from 'changed';",
'export "changed";',
'export Default from "changed";',
'export * as All from "changed";',
'export { } from "changed";',
'export { Foo } from "changed";',
'export { Foo, } from "changed";',
'export { Foo as Bar } from "changed";',
'export { Foo as Bar, } from "changed";',
'export { Foo, Bar } from "changed";',
'export { Foo, Bar, } from "changed";',
'export { Foo as Bar, Baz } from "changed";',
'export { Foo as Bar, Baz, } from "changed";',
'export { Foo, Bar as Baz } from "changed";',
'export { Foo, Bar as Baz, } from "changed";',
'export { Foo as Bar, Baz as Qux } from "changed";',
'export { Foo as Bar, Baz as Qux, } from "changed";',
'export { Foo, Bar, Baz } from "changed";',
'export { Foo, Bar, Baz, } from "changed";',
'export { Foo as Bar, Baz, Qux } from "changed";',
'export { Foo as Bar, Baz, Qux, } from "changed";',
'export { Foo, Bar as Baz, Qux } from "changed";',
'export { Foo, Bar as Baz, Qux, } from "changed";',
'export { Foo, Bar, Baz as Qux } from "changed";',
'export { Foo, Bar, Baz as Qux, } from "changed";',
'export { Foo as Bar, Baz as Qux, Norf } from "changed";',
'export { Foo as Bar, Baz as Qux, Norf, } from "changed";',
'export { Foo as Bar, Baz, Qux as Norf } from "changed";',
'export { Foo as Bar, Baz, Qux as Norf, } from "changed";',
'export { Foo, Bar as Baz, Qux as Norf } from "changed";',
'export { Foo, Bar as Baz, Qux as Norf, } from "changed";',
'export { Foo as Bar, Baz as Qux, Norf as NoMore } from "changed";',
'export { Foo as Bar, Baz as Qux, Norf as NoMore, } from "changed";',
'export Default, * as All from "changed";',
'export Default, { } from "changed";',
'export Default, { Foo } from "changed";',
'export Default, { Foo, } from "changed";',
'export Default, { Foo as Bar } from "changed";',
'export Default, { Foo as Bar, } from "changed";',
'export Default, { Foo, Bar } from "changed";',
'export Default, { Foo, Bar, } from "changed";',
'export Default, { Foo as Bar, Baz } from "changed";',
'export Default, { Foo as Bar, Baz, } from "changed";',
'export Default, { Foo, Bar as Baz } from "changed";',
'export Default, { Foo, Bar as Baz, } from "changed";',
'export Default, { Foo as Bar, Baz as Qux } from "changed";',
'export Default, { Foo as Bar, Baz as Qux, } from "changed";',
'export Default, { Foo, Bar, Baz } from "changed";',
'export Default, { Foo, Bar, Baz, } from "changed";',
'export Default, { Foo as Bar, Baz, Qux } from "changed";',
'export Default, { Foo as Bar, Baz, Qux, } from "changed";',
'export Default, { Foo, Bar as Baz, Qux } from "changed";',
'export Default, { Foo, Bar as Baz, Qux, } from "changed";',
'export Default, { Foo, Bar, Baz as Qux } from "changed";',
'export Default, { Foo, Bar, Baz as Qux, } from "changed";',
'export Default, { Foo as Bar, Baz as Qux, Norf } from "changed";',
'export Default, { Foo as Bar, Baz as Qux, Norf, } from "changed";',
'export Default, { Foo as Bar, Baz, Qux as Norf } from "changed";',
'export Default, { Foo as Bar, Baz, Qux as Norf, } from "changed";',
'export Default, { Foo, Bar as Baz, Qux as Norf } from "changed";',
'export Default, { Foo, Bar as Baz, Qux as Norf, } from "changed";',
'export Default, { Foo as Bar, Baz as Qux, Norf as Enuf } from "changed";',
'export Default, { Foo as Bar, Baz as Qux, Norf as Enuf, } from "changed";',
'export Default from "Y";',
'export * as All from \'z\';',
// export with support for new lines
"export { Foo,\n Bar }\n from 'changed';",
"export { \nFoo,\nBar,\n }\n from 'changed';",
"export { Foo as Bar,\n Baz\n }\n from 'changed';",
"export { \nFoo as Bar,\n Baz\n, }\n from 'changed';",
"export { Foo,\n Bar as Baz\n }\n from 'changed';",
"export { Foo,\n Bar as Baz,\n }\n from 'changed';",
"export { Foo as Bar,\n Baz as Qux\n }\n from 'changed';",
"export { Foo as Bar,\n Baz as Qux,\n }\n from 'changed';",
"export { Foo,\n Bar,\n Baz }\n from 'changed';",
"export { Foo,\n Bar,\n Baz,\n }\n from 'changed';",
"export { Foo as Bar,\n Baz,\n Qux\n }\n from 'changed';",
"export { Foo as Bar,\n Baz,\n Qux,\n }\n from 'changed';",
"export { Foo,\n Bar as Baz,\n Qux\n }\n from 'changed';",
"export { Foo,\n Bar as Baz,\n Qux,\n }\n from 'changed';",
"export { Foo,\n Bar,\n Baz as Qux\n }\n from 'changed';",
"export { Foo,\n Bar,\n Baz as Qux,\n }\n from 'changed';",
"export { Foo as Bar,\n Baz as Qux,\n Norf\n }\n from 'changed';",
"export { Foo as Bar,\n Baz as Qux,\n Norf,\n }\n from 'changed';",
"export { Foo as Bar,\n Baz,\n Qux as Norf\n }\n from 'changed';",
"export { Foo as Bar,\n Baz,\n Qux as Norf,\n }\n from 'changed';",
"export { Foo,\n Bar as Baz,\n Qux as Norf\n }\n from 'changed';",
"export { Foo,\n Bar as Baz,\n Qux as Norf,\n }\n from 'changed';",
"export { Foo as Bar,\n Baz as Qux,\n Norf as Enuf\n }\n from 'changed';",
"export { Foo as Bar,\n Baz as Qux,\n Norf as Enuf,\n }\n from 'changed';",
"export Default,\n * as All from 'changed';",
"export Default,\n { } from 'changed';",
"export Default,\n { Foo\n }\n from 'changed';",
"export Default,\n { Foo,\n }\n from 'changed';",
"export Default,\n { Foo as Bar\n }\n from 'changed';",
"export Default,\n { Foo as Bar,\n }\n from 'changed';",
"export Default,\n { Foo,\n Bar\n } from\n 'changed';",
"export Default,\n { Foo,\n Bar,\n } from\n 'changed';",
"export Default,\n { Foo as Bar,\n Baz\n }\n from 'changed';",
"export Default,\n { Foo as Bar,\n Baz,\n }\n from 'changed';",
"export Default,\n { Foo,\n Bar as Baz\n }\n from 'changed';",
"export Default,\n { Foo,\n Bar as Baz,\n }\n from 'changed';",
"export Default,\n { Foo as Bar,\n Baz as Qux\n }\n from 'changed';",
"export Default,\n { Foo as Bar,\n Baz as Qux,\n }\n from 'changed';",
"export Default,\n { Foo,\n Bar,\n Baz\n }\n from 'changed';",
"export Default,\n { Foo,\n Bar,\n Baz,\n }\n from 'changed';",
"export Default,\n { Foo as Bar,\n Baz,\n Qux\n }\n from 'changed';",
"export Default,\n { Foo as Bar,\n Baz,\n Qux,\n }\n from 'changed';",
"export Default,\n { Foo,\n Bar as Baz,\n Qux\n }\n from 'changed';",
"export Default,\n { Foo,\n Bar as Baz,\n Qux,\n }\n from 'changed';",
"export Default,\n { Foo,\n Bar,\n Baz as Qux\n }\n from 'changed';",
"export Default,\n { Foo,\n Bar,\n Baz as Qux,\n }\n from 'changed';",
"export Default,\n { Foo as Bar,\n Baz as Qux,\n Norf\n }\n from 'changed';",
"export Default,\n { Foo as Bar,\n Baz as Qux,\n Norf,\n }\n from 'changed';",
"export Default,\n { Foo as Bar,\n Baz,\n Qux as Norf }\n from 'changed';",
"export Default,\n { Foo as Bar,\n Baz,\n Qux as Norf, }\n from 'changed';",
"export Default,\n { Foo, Bar as Baz,\n Qux as Norf }\n from 'changed';",
"export Default,\n { Foo, Bar as Baz,\n Qux as Norf, }\n from 'changed';",
"export Default,\n { Foo as Bar,\n Baz as Qux,\n Norf as NoMore\n }\n from 'changed';",
"export Default,\n { Foo as Bar,\n Baz as Qux,\n Norf as NoMore,\n }\n from 'changed';",
"export Default\n , { } from 'changed';",
module: createModule('test module', ['x', 'y']),
name: 'test module',
code,
meta: {dependencyOffsets}
}).then(({code: processedCode}) => {
expect(processedCode).toEqual([
'__d("test module", function(global, require, module, exports) {' +
// require
'require("changed")',
'require("Y")',
@ -1045,6 +323,22 @@ describe('Resolver', function() {
});
});
pit('should pass through passed-in source maps', () => {
const module = createModule('test module');
const resolutionResponse = new ResolutionResponseMock({
dependencies: [module],
mainModuleId: 'test module',
});
const inputMap = {version: 3, mappings: 'ARBITRARY'};
return new Resolver({projectRoot: '/root'}).wrapModule({
resolutionResponse,
module,
name: 'test module',
code: 'arbitrary(code)',
map: inputMap,
}).then(({map}) => expect(map).toBe(inputMap));
});
pit('should resolve polyfills', function () {
const depResolver = new Resolver({
projectRoot: '/root',
@ -1053,17 +347,90 @@ describe('Resolver', function() {
const code = [
'global.fetch = () => 1;',
].join('');
return depResolver.wrapModule(
null,
polyfill,
return depResolver.wrapModule({
module: polyfill,
code
).then(processedCode => {
expect(processedCode.code).toEqual([
}).then(({code: processedCode}) => {
expect(processedCode).toEqual([
'(function(global) {',
'global.fetch = () => 1;',
"\n})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this);",
].join(''));
});
});
describe('JSON files:', () => {
const code = JSON.stringify({arbitrary: "data"});
const id = 'arbitrary.json';
let depResolver, module, resolutionResponse;
beforeEach(() => {
depResolver = new Resolver({projectRoot: '/root'});
module = createJsonModule(id);
resolutionResponse = new ResolutionResponseMock({
dependencies: [module],
mainModuleId: id,
});
});
pit('should prefix JSON files with `module.exports=`', () => {
return depResolver
.wrapModule({resolutionResponse, module, name: id, code})
.then(({code: processedCode}) =>
expect(processedCode).toEqual([
`__d(${JSON.stringify(id)}, function(global, require, module, exports) {`,
`module.exports = ${code}\n});`,
].join('')));
});
});
describe('minification:', () => {
const code ='arbitrary(code)';
const id = 'arbitrary.js';
let depResolver, minifyCode, module, resolutionResponse, sourceMap;
beforeEach(() => {
minifyCode = jest.genMockFn().mockImpl((filename, code, map) =>
Promise.resolve({code, map}));
depResolver = new Resolver({
projectRoot: '/root',
minifyCode
});
module = createModule(id);
module.path = '/arbitrary/path.js';
resolutionResponse = new ResolutionResponseMock({
dependencies: [module],
mainModuleId: id,
});
sourceMap = {version: 3, sources: ['input'], mappings: 'whatever'};
});
pit('should invoke the minifier with the wrapped code', () => {
const wrappedCode = `__d("${id}", function(global, require, module, exports) {${code}\n});`
return depResolver
.wrapModule({
resolutionResponse,
module,
name: id,
code,
map: sourceMap,
minify: true
}).then(() => {
expect(minifyCode).toBeCalledWith(module.path, wrappedCode, sourceMap);
});
});
pit('should use minified code', () => {
const minifiedCode = 'minified(code)';
const minifiedMap = {version: 3, file: ['minified']};
minifyCode.mockReturnValue(Promise.resolve({code: minifiedCode, map: minifiedMap}));
return depResolver
.wrapModule({resolutionResponse, module, name: id, code, minify: true})
.then(({code, map}) => {
expect(code).toEqual(minifiedCode);
expect(map).toEqual(minifiedMap);
});
});
});
});
});

View File

@ -12,7 +12,6 @@
const path = require('path');
const Activity = require('../Activity');
const DependencyGraph = require('node-haste');
const replacePatterns = require('node-haste').replacePatterns;
const declareOpts = require('../lib/declareOpts');
const Promise = require('promise');
@ -48,6 +47,12 @@ const validateOpts = declareOpts({
type: 'object',
required: true,
},
transformCode: {
type: 'function',
},
minifyCode: {
type: 'function',
},
});
const getDependenciesValidateOpts = declareOpts({
@ -97,8 +102,10 @@ class Resolver {
fileWatcher: opts.fileWatcher,
cache: opts.cache,
shouldThrowOnUnresolvedErrors: (_, platform) => platform === 'ios',
transformCode: opts.transformCode,
});
this._minifyCode = opts.minifyCode;
this._polyfillModuleNames = opts.polyfillModuleNames || [];
this._depGraph.load().catch(err => {
@ -119,12 +126,14 @@ class Resolver {
return this._depGraph.getModuleForPath(entryFile);
}
getDependencies(entryPath, options) {
getDependencies(entryPath, options, transformOptions, onProgress) {
const {platform, recursive} = getDependenciesValidateOpts(options);
return this._depGraph.getDependencies({
entryPath,
platform,
transformOptions,
recursive,
onProgress,
}).then(resolutionResponse => {
this._getPolyfillDependencies().reverse().forEach(
polyfill => resolutionResponse.prependDependency(polyfill)
@ -176,16 +185,14 @@ class Resolver {
);
}
resolveRequires(resolutionResponse, module, code) {
resolveRequires(resolutionResponse, module, code, dependencyOffsets = []) {
return Promise.resolve().then(() => {
if (module.isPolyfill()) {
return Promise.resolve({code});
}
const resolvedDeps = Object.create(null);
const resolvedDepsArr = [];
return Promise.all(
// here, we build a map of all require strings (relative and absolute)
// to the canonical name of the module they reference
resolutionResponse.getResolvedDependencyPairs(module).map(
([depName, depModule]) => {
if (depModule) {
@ -197,59 +204,81 @@ class Resolver {
}
)
).then(() => {
const relativizeCode = (codeMatch, pre, quot, depName, post) => {
const relativizeCode = (codeMatch, quot, depName) => {
// if we have a canonical name for the module imported here,
// we use it, so that require() is always called with the same
// id for every module.
// Example:
// -- in a/b.js:
// require('./c') => require('a/c');
// -- in b/index.js:
// require('../a/c') => require('a/c');
const depId = resolvedDeps[depName];
if (depId) {
return pre + quot + depId + post;
return quot + depId + quot;
} else {
return codeMatch;
}
};
code = code
.replace(replacePatterns.IMPORT_RE, relativizeCode)
.replace(replacePatterns.EXPORT_RE, relativizeCode)
.replace(replacePatterns.REQUIRE_RE, relativizeCode);
code = dependencyOffsets.reduceRight((codeBits, offset) => {
const first = codeBits.shift();
codeBits.unshift(
first.slice(0, offset),
first.slice(offset).replace(/(['"])([^'"']*)\1/, relativizeCode),
);
return codeBits;
}, [code]);
return module.getName().then(name => {
return {name, code};
});
return code.join('');
});
});
}
wrapModule(resolutionResponse, module, code) {
if (module.isPolyfill()) {
return Promise.resolve({
code: definePolyfillCode(code),
});
wrapModule({
resolutionResponse,
module,
name,
map,
code,
meta = {},
minify = false
}) {
if (module.isJSON()) {
code = `module.exports = ${code}`;
}
const result = module.isPolyfill()
? Promise.resolve({code: definePolyfillCode(code)})
: this.resolveRequires(
resolutionResponse,
module,
code,
meta.dependencyOffsets
).then(code => ({code: defineModuleCode(name, code), map}));
return this.resolveRequires(resolutionResponse, module, code).then(
({name, code}) => {
return {name, code: defineModuleCode(name, code)};
});
return minify
? result.then(({code, map}) => this._minifyCode(module.path, code, map))
: result;
}
getDebugInfo() {
return this._depGraph.getDebugInfo();
}
}
function defineModuleCode(moduleName, code) {
return [
`__d(`,
`'${moduleName}',`,
'function(global, require, module, exports) {',
` ${code}`,
`${JSON.stringify(moduleName)}, `,
`function(global, require, module, exports) {`,
`${code}`,
'\n});',
].join('');
}
function definePolyfillCode(code) {
function definePolyfillCode(code,) {
return [
'(function(global) {',
`(function(global) {`,
code,
`\n})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this);`,
].join('');

View File

@ -73,10 +73,6 @@ const validateOpts = declareOpts({
type: 'string',
required: false,
},
disableInternalTransforms: {
type: 'boolean',
default: false,
},
});
const bundleOpts = declareOpts({
@ -146,6 +142,10 @@ const dependencyOpts = declareOpts({
type: 'boolean',
default: true,
},
hot: {
type: 'boolean',
default: false,
},
});
class Server {
@ -259,12 +259,7 @@ class Server {
}
const opts = dependencyOpts(options);
return this._bundler.getDependencies(
opts.entryFile,
opts.dev,
opts.platform,
opts.recursive,
);
return this._bundler.getDependencies(opts);
});
}

View File

@ -21,11 +21,7 @@ function ModuleTransport(data) {
this.sourcePath = data.sourcePath;
this.virtual = data.virtual;
if (this.virtual && data.map) {
throw new Error('Virtual modules cannot have source maps');
}
this.meta = data.meta;
this.map = data.map;
Object.freeze(this);

View File

@ -1,115 +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();
var deadModuleElimintation = require('../dead-module-elimination');
var babel = require('babel-core');
const compile = (code) =>
babel.transform(code, {
plugins: [deadModuleElimintation],
}).code;
const compare = (source, output) => {
const out = trim(compile(source))
// workaround babel/source map bug
.replace(/^false;/, '');
expect(out).toEqual(trim(output));
};
const trim = (str) =>
str.replace(/\s/g, '');
describe('dead-module-elimination', () => {
it('should inline __DEV__', () => {
compare(
`global.__DEV__ = false;
var foo = __DEV__;`,
`var foo = false;`
);
});
it('should accept unary operators with literals', () => {
compare(
`global.__DEV__ = !1;
var foo = __DEV__;`,
`var foo = false;`
);
});
it('should kill dead branches', () => {
compare(
`global.__DEV__ = false;
if (__DEV__) {
doSomething();
}`,
``
);
});
it('should kill unreferenced modules', () => {
compare(
`__d('foo', function() {})`,
``
);
});
it('should kill unreferenced modules at multiple levels', () => {
compare(
`__d('bar', function() {});
__d('foo', function() { require('bar'); });`,
``
);
});
it('should kill modules referenced only from dead branches', () => {
compare(
`global.__DEV__ = false;
__d('bar', function() {});
if (__DEV__) { require('bar'); }`,
``
);
});
it('should replace logical expressions with the result', () => {
compare(
`global.__DEV__ = false;
__d('bar', function() {});
__DEV__ && require('bar');`,
`false;`
);
});
it('should keep if result branch', () => {
compare(
`global.__DEV__ = false;
__d('bar', function() {});
if (__DEV__) {
killWithFire();
} else {
require('bar');
}`,
`__d('bar', function() {});
require('bar');`
);
});
it('should replace falsy ternaries with alternate expression', () => {
compare(
`global.__DEV__ = false;
__DEV__ ? foo() : bar();
`,
`bar();`
);
});
});

View File

@ -1,148 +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 t = require('babel-types');
var globals = Object.create(null);
var requires = Object.create(null);
var _requires;
const hasDeadModules = modules =>
Object.keys(modules).some(key => modules[key] === 0);
function CallExpression(path) {
const { node } = path;
const fnName = node.callee.name;
if (fnName === 'require' || fnName === '__d') {
var moduleName = node.arguments[0].value;
if (fnName === '__d' && _requires && !_requires[moduleName]) {
path.remove();
} else if (fnName === '__d'){
requires[moduleName] = requires[moduleName] || 0;
} else {
requires[moduleName] = (requires[moduleName] || 0) + 1;
}
}
}
function IfStatement(path) {
const { node } = path;
if (node.test.type === 'Identifier' && node.test.name in globals) {
if (globals[node.test.name]) {
if (node.consequent.type === 'BlockStatement') {
path.replaceWithMultiple(node.consequent.body);
} else {
path.replaceWith(node.consequent);
}
} else if (node.alternate) {
if (node.alternate.type === 'BlockStatement') {
path.replaceWithMultiple(node.alternate.body);
} else {
path.replaceWith(node.alternate);
}
} else {
path.remove();
}
}
}
module.exports = function () {
var firstPass = {
AssignmentExpression(path) {
const { node } = path;
if (
node.left.type === 'MemberExpression' &&
node.left.object.name === 'global' &&
node.left.property.type === 'Identifier' &&
node.left.property.name === '__DEV__'
) {
var value;
if (node.right.type === 'BooleanLiteral') {
value = node.right.value;
} else if (
node.right.type === 'UnaryExpression' &&
node.right.operator === '!' &&
node.right.argument.type === 'NumericLiteral'
) {
value = !node.right.argument.value;
} else {
return;
}
globals[node.left.property.name] = value;
// workaround babel/source map bug - the minifier should strip it
path.replaceWith(t.booleanLiteral(value));
//path.remove();
//scope.removeBinding(node.left.name);
}
},
IfStatement,
ConditionalExpression: IfStatement,
Identifier(path) {
const { node } = path;
var parent = path.parent;
if (parent.type === 'AssignmentExpression' && parent.left === node) {
return;
}
if (node.name in globals) {
path.replaceWith(t.booleanLiteral(globals[node.name]));
}
},
CallExpression,
LogicalExpression(path) {
const { node } = path;
if (node.left.type === 'Identifier' && node.left.name in globals) {
const value = globals[node.left.name];
if (node.operator === '&&') {
if (value) {
path.replaceWith(node.right);
} else {
path.replaceWith(t.booleanLiteral(value));
}
} else if (node.operator === '||') {
if (value) {
path.replaceWith(t.booleanLiteral(value));
} else {
path.replaceWith(node.right);
}
}
}
}
};
var secondPass = {
CallExpression,
};
return {
visitor: {
Program(path) {
path.traverse(firstPass);
var counter = 0;
while (hasDeadModules(requires) && counter < 3) {
_requires = requires;
requires = {};
path.traverse(secondPass);
counter++;
}
}
}
};
};

View File

@ -1,14 +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';
// Return the list of plugins use for Whole Program Optimisations
module.exports = [
require('./dead-module-elimination'),
];

View File

@ -61,7 +61,7 @@ const getBabelRC = (function() {
}
return babelRC;
}
};
})();
/**
@ -81,14 +81,16 @@ function buildBabelConfig(filename, options) {
// Add extra plugins
const extraPlugins = [externalHelpersPlugin];
if (options.inlineRequires) {
var inlineRequires = options.inlineRequires;
var blacklist = inlineRequires && inlineRequires.blacklist;
if (inlineRequires && !(blacklist && filename in blacklist)) {
extraPlugins.push(inlineRequiresPlugin);
}
config.plugins = extraPlugins.concat(config.plugins);
if (options.hot) {
const hmrConfig = makeHMRConfig(options);
const hmrConfig = makeHMRConfig(options, filename);
config = Object.assign({}, config, hmrConfig);
}
@ -102,7 +104,9 @@ function transform(src, filename, options) {
const result = babel.transform(src, babelConfig);
return {
ast: result.ast,
code: result.code,
map: result.map,
filename: filename,
};
}