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

81
src/Module.js

@ -26,7 +26,7 @@ function removeSourceMappingURLComments ( source, magicString ) {
} }
function assign ( target, source ) { function assign ( target, source ) {
for ( var key in source ) target[ key ] = source[ key ]; for ( let key in source ) target[ key ] = source[ key ];
} }
class Id { class Id {
@ -92,9 +92,9 @@ export default class Module {
return reference.call( this.exports, name ); return reference.call( this.exports, name );
} }
// ... otherwise search exportAlls // ... otherwise search allExportsFrom
for ( let i = 0; i < this.exportAlls.length; i += 1 ) { for ( let i = 0; i < this.allExportsFrom.length; i += 1 ) {
const module = this.exportAlls[i]; const module = this.allExportsFrom[i];
if ( module.exports.inScope( name ) ) { if ( module.exports.inScope( name ) ) {
return module.exports.reference( name ); return module.exports.reference( name );
} }
@ -107,7 +107,7 @@ export default class Module {
this.exports.inScope = name => { this.exports.inScope = name => {
if ( inScope.call( this.exports, name ) ) return true; 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. // Create a unique virtual scope for references to the module.
@ -115,7 +115,9 @@ export default class Module {
// unique.define( this.name, this ); // unique.define( this.name, this );
// this.reference = unique.reference( this.name ); // 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 = []; 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. // When an unknown import is encountered, we see if one of them can satisfy it.
if ( module.isExternal ) { 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 { 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 // discover variables that are reassigned inside function
// bodies, so we can keep bindings live, e.g. // bodies, so we can keep bindings live, e.g.
// //
@ -375,8 +402,12 @@ export default class Module {
}); });
}); });
this.locals.getNames().forEach( name => { // Go through all our local and exported ids and make us depend on
const id = this.locals.lookup( name ); // 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; if ( !id.modifierStatements ) return;
@ -394,24 +425,16 @@ export default class Module {
return { strongDependencies, weakDependencies }; 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 ) { getModule ( source ) {
return this.bundle.moduleById[ this.resolvedIds[ source ] ]; return this.bundle.moduleById[ this.resolvedIds[ source ] ];
} }
// If a module is marked, enforce dynamic access of its properties.
mark () { mark () {
this.dynamicAccess(); if ( this.needsDynamicAccess ) return;
this.needsDynamicAccess = true;
this.markAllExports();
} }
markAllStatements ( isEntryModule ) { markAllStatements ( isEntryModule ) {
@ -447,10 +470,9 @@ export default class Module {
}); });
} }
markAllExportStatements () { // Marks all exported identifiers.
this.statements.forEach( statement => { markAllExports () {
if ( statement.isExportDeclaration ) statement.mark(); this.exports.getIds().forEach( id => id.mark() );
});
} }
parse ( ast ) { parse ( ast ) {
@ -621,6 +643,11 @@ export default class Module {
magicString.remove( statement.node.start, statement.node.declaration.start ); 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` // remove `export` from `export class Foo {...}` or `export default Foo`
// TODO default exports need different treatment // TODO default exports need different treatment
else if ( statement.node.declaration.id ) { 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. // Returns a list of `[ name, identifier ]` tuples.
localIds () { getIds () {
return keys( this.names ).map( name => [ name, this.lookup( name ) ] ); return keys( this.names ).map( name => this.lookup( name ) );
} }
// Lookup the identifier referred to by `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 ); ( node.property.type === 'Literal' ? String( node.property.value ) : null );
// If we can't resolve the name being accessed statically, // 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 // // resolvable
// console.log( javascript.keywords.for ) // console.log( javascript.keywords.for )
@ -211,7 +211,7 @@ export default class Statement {
// console.log( javascript.keywords[ index ] ) // console.log( javascript.keywords[ index ] )
// console.log( javascript.keywords[ 1 + 5 ] ) // console.log( javascript.keywords[ 1 + 5 ] )
if ( name === null ) { if ( name === null ) {
namespace.dynamicAccess(); namespace.mark();
namespace = null; namespace = null;
currentMemberExpression = null; currentMemberExpression = null;

Loading…
Cancel
Save