Browse Source

Implemented `export * from "internal";`

gh-109
Oskar Segersvärd 10 years ago
parent
commit
98cf636e2e
  1. 18
      src/Bundle.js
  2. 81
      src/Module.js
  3. 4
      src/Scope.js
  4. 4
      src/Statement.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

@ -112,8 +112,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

@ -200,7 +200,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 )
@ -211,7 +211,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;

Loading…
Cancel
Save