Browse Source

Merge branch 'Victorystick-export-all-from-bundle'

gh-109
Rich-Harris 9 years ago
parent
commit
187babc93a
  1. 18
      src/Bundle.js
  2. 81
      src/Module.js
  3. 4
      src/Scope.js
  4. 4
      src/Statement.js
  5. 2
      src/finalisers/es6.js
  6. 6
      test/form/export-all-from-internal/_config.js
  7. 11
      test/form/export-all-from-internal/_expected/amd.js
  8. 9
      test/form/export-all-from-internal/_expected/cjs.js
  9. 6
      test/form/export-all-from-internal/_expected/es6.js
  10. 11
      test/form/export-all-from-internal/_expected/iife.js
  11. 15
      test/form/export-all-from-internal/_expected/umd.js
  12. 3
      test/form/export-all-from-internal/internal.js
  13. 1
      test/form/export-all-from-internal/main.js
  14. 16
      test/test.js

18
src/Bundle.js

@ -49,7 +49,6 @@ export default class Bundle {
this.statements = null;
this.externalModules = [];
this.internalNamespaceModules = [];
}
build () {
@ -60,13 +59,10 @@ export default class Bundle {
this.exports = entryModule.exports;
entryModule.markAllStatements( true );
this.orderedModules = this.sort();
entryModule.markAllExports();
this.exports.localIds().forEach( ([ , id ]) => {
// If the export is a module (namespace), we need
// all its exports dynamically accessible.
if ( id.module === id ) id.dynamicAccess();
});
// Sort the modules.
this.orderedModules = this.sort();
// As a last step, deconflict all identifier names, once.
this.scope.deconflict();
@ -213,9 +209,11 @@ export default class Bundle {
// prepend bundle with internal namespaces
const indentString = getIndentString( magicString, options );
const namespaceBlock = this.internalNamespaceModules.map( module => {
const exports = module.exports.localIds().map( ( [ name, id ] ) =>
`${indentString}get ${name} () { return ${id.name}; }`);
const namespaceBlock = this.modules.filter( module => module.needsDynamicAccess ).map( module => {
const exports = module.exports.getNames().map( name => {
const id = module.exports.lookup( name );
return `${indentString}get ${name} () { return ${id.name}; }`;
});
return `var ${module.name} = {\n` +
exports.join( ',\n' ) +

81
src/Module.js

@ -26,7 +26,7 @@ function removeSourceMappingURLComments ( source, magicString ) {
}
function assign ( target, source ) {
for ( var key in source ) target[ key ] = source[ key ];
for ( let key in source ) target[ key ] = source[ key ];
}
class Id {
@ -92,9 +92,9 @@ export default class Module {
return reference.call( this.exports, name );
}
// ... otherwise search exportAlls
for ( let i = 0; i < this.exportAlls.length; i += 1 ) {
const module = this.exportAlls[i];
// ... otherwise search allExportsFrom
for ( let i = 0; i < this.allExportsFrom.length; i += 1 ) {
const module = this.allExportsFrom[i];
if ( module.exports.inScope( name ) ) {
return module.exports.reference( name );
}
@ -107,7 +107,7 @@ export default class Module {
this.exports.inScope = name => {
if ( inScope.call( this.exports, name ) ) return true;
return this.exportAlls.some( module => module.exports.inScope( name ) );
return this.allExportsFrom.some( module => module.exports.inScope( name ) );
};
// Create a unique virtual scope for references to the module.
@ -115,7 +115,9 @@ export default class Module {
// unique.define( this.name, this );
// this.reference = unique.reference( this.name );
this.exportAlls = [];
// As far as we know, all our exported bindings have been resolved.
this.allExportsResolved = true;
this.allExportsFrom = [];
this.reassignments = [];
@ -138,10 +140,18 @@ export default class Module {
// When an unknown import is encountered, we see if one of them can satisfy it.
if ( module.isExternal ) {
throw new Error( `Cannot trace 'export *' references through external modules.` );
let err = new Error( `Cannot trace 'export *' references through external modules.` );
err.file = this.id;
err.loc = getLocation( this.source, node.start );
throw err;
}
this.exportAlls.push( module );
// It seems like we must re-export all exports from another module...
this.allExportsResolved = false;
if ( !~this.allExportsFrom.indexOf( module ) ) {
this.allExportsFrom.push( module );
}
}
else {
@ -266,6 +276,23 @@ export default class Module {
});
});
// If all exports aren't resolved, but all our delegate modules are...
if ( !this.allExportsResolved && this.allExportsFrom.every( module => module.allExportsResolved )) {
// .. then all our exports should be as well.
this.allExportsResolved = true;
// For all modules we export all from, iterate through its exported names.
// If we don't already define the binding 'name',
// bind the name to the other module's reference.
this.allExportsFrom.forEach( module => {
module.exports.getNames().forEach( name => {
if ( !this.exports.defines( name ) ) {
this.exports.bind( name, module.exports.reference( name ) );
}
});
});
}
// discover variables that are reassigned inside function
// bodies, so we can keep bindings live, e.g.
//
@ -375,8 +402,12 @@ export default class Module {
});
});
this.locals.getNames().forEach( name => {
const id = this.locals.lookup( name );
// Go through all our local and exported ids and make us depend on
// the defining modules as well as
this.exports.getIds().concat(this.locals.getIds()).forEach( id => {
if ( id.module && !id.module.isExternal ) {
weakDependencies[ id.module.id ] = id.module;
}
if ( !id.modifierStatements ) return;
@ -394,24 +425,16 @@ export default class Module {
return { strongDependencies, weakDependencies };
}
// Enforce dynamic access of the module's properties.
dynamicAccess () {
if ( this.needsDynamicAccess ) return;
this.needsDynamicAccess = true;
this.markAllExportStatements();
if ( !~this.bundle.internalNamespaceModules.indexOf( this ) ) {
this.bundle.internalNamespaceModules.push( this );
}
}
getModule ( source ) {
return this.bundle.moduleById[ this.resolvedIds[ source ] ];
}
// If a module is marked, enforce dynamic access of its properties.
mark () {
this.dynamicAccess();
if ( this.needsDynamicAccess ) return;
this.needsDynamicAccess = true;
this.markAllExports();
}
markAllStatements ( isEntryModule ) {
@ -447,10 +470,9 @@ export default class Module {
});
}
markAllExportStatements () {
this.statements.forEach( statement => {
if ( statement.isExportDeclaration ) statement.mark();
});
// Marks all exported identifiers.
markAllExports () {
this.exports.getIds().forEach( id => id.mark() );
}
parse ( ast ) {
@ -621,6 +643,11 @@ export default class Module {
magicString.remove( statement.node.start, statement.node.declaration.start );
}
else if ( statement.node.type === 'ExportAllDeclaration' ) {
// TODO: remove once `export * from 'external'` is supported.
magicString.remove( statement.start, statement.next );
}
// remove `export` from `export class Foo {...}` or `export default Foo`
// TODO default exports need different treatment
else if ( statement.node.declaration.id ) {

4
src/Scope.js

@ -118,8 +118,8 @@ export default class Scope {
}
// Returns a list of `[ name, identifier ]` tuples.
localIds () {
return keys( this.names ).map( name => [ name, this.lookup( name ) ] );
getIds () {
return keys( this.names ).map( name => this.lookup( name ) );
}
// Lookup the identifier referred to by `name`.

4
src/Statement.js

@ -206,7 +206,7 @@ export default class Statement {
( node.property.type === 'Literal' ? String( node.property.value ) : null );
// If we can't resolve the name being accessed statically,
// we require the namespace to be dynamically accessible.
// we mark the whole namespace for inclusion in the bundle.
//
// // resolvable
// console.log( javascript.keywords.for )
@ -217,7 +217,7 @@ export default class Statement {
// console.log( javascript.keywords[ index ] )
// console.log( javascript.keywords[ 1 + 5 ] )
if ( name === null ) {
namespace.dynamicAccess();
namespace.mark();
namespace = null;
currentMemberExpression = null;

2
src/finalisers/es6.js

@ -58,7 +58,7 @@ export default function es6 ( bundle, magicString ) {
const defaultExport = module.exports.lookup( 'default' );
if ( defaultExport ) {
exportBlock += `export default ${ defaultExport.name };`;
exportBlock += `\nexport default ${ defaultExport.name };`;
}
if ( exportBlock ) {

6
test/form/export-all-from-internal/_config.js

@ -0,0 +1,6 @@
module.exports = {
description: 'should be able to export * from the bundle',
options: {
moduleName: 'exposedInternals'
}
};

11
test/form/export-all-from-internal/_expected/amd.js

@ -0,0 +1,11 @@
define(['exports'], function (exports) { 'use strict';
const a = 1;
const b = 2;
var internal = 42;
exports.a = a;
exports.b = b;
exports['default'] = internal;
});

9
test/form/export-all-from-internal/_expected/cjs.js

@ -0,0 +1,9 @@
'use strict';
const a = 1;
const b = 2;
var internal = 42;
exports.a = a;
exports.b = b;
exports['default'] = internal;

6
test/form/export-all-from-internal/_expected/es6.js

@ -0,0 +1,6 @@
const a = 1;
const b = 2;
var internal = 42;
export { a, b };
export default internal;

11
test/form/export-all-from-internal/_expected/iife.js

@ -0,0 +1,11 @@
(function (exports) { 'use strict';
const a = 1;
const b = 2;
var internal = 42;
exports.a = a;
exports.b = b;
exports['default'] = internal;
})((this.exposedInternals = {}));

15
test/form/export-all-from-internal/_expected/umd.js

@ -0,0 +1,15 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
factory((global.exposedInternals = {}));
}(this, function (exports) { 'use strict';
const a = 1;
const b = 2;
var internal = 42;
exports.a = a;
exports.b = b;
exports['default'] = internal;
}));

3
test/form/export-all-from-internal/internal.js

@ -0,0 +1,3 @@
export const a = 1;
export const b = 2;
export default 42;

1
test/form/export-all-from-internal/main.js

@ -0,0 +1 @@
export * from './internal.js';

16
test/test.js

@ -161,17 +161,15 @@ describe( 'rollup', function () {
sander.readdirSync( FORM ).sort().forEach( function ( dir ) {
if ( dir[0] === '.' ) return; // .DS_Store...
describe( dir, function () {
var config = require( FORM + '/' + dir + '/_config' );
var config = require( FORM + '/' + dir + '/_config' );
var options = extend( {}, config.options, {
entry: FORM + '/' + dir + '/main.js'
});
var options = extend( {}, config.options, {
entry: FORM + '/' + dir + '/main.js'
});
( config.skip ? describe.skip : config.solo ? describe.only : describe)( dir, function () {
PROFILES.forEach( function ( profile ) {
( config.skip ? it.skip : config.solo ? it.only : it )( 'generates ' + profile.format, function () {
if ( config.solo ) console.group( dir );
it( 'generates ' + profile.format, function () {
return rollup.rollup( options ).then( function ( bundle ) {
var options = extend( {}, config.options, {
dest: FORM + '/' + dir + '/_actual/' + profile.format + '.js',
@ -202,8 +200,6 @@ describe( 'rollup', function () {
assert.equal( actualCode, expectedCode );
assert.deepEqual( actualMap, expectedMap );
if ( config.solo ) console.groupEnd();
});
});
});

Loading…
Cancel
Save