diff --git a/src/Bundle.js b/src/Bundle.js index 728cec8..25831b8 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -62,6 +62,7 @@ export default class Bundle { // mark all export statements entryModule.getExports().forEach( name => { const declaration = entryModule.traceExport( name ); + declaration.isExported = true; declaration.statement.mark(); }); diff --git a/src/ExternalModule.js b/src/ExternalModule.js index 396dcc9..81a1a17 100644 --- a/src/ExternalModule.js +++ b/src/ExternalModule.js @@ -18,7 +18,7 @@ class ExternalDeclaration { } } - getName ( es6 ) { + render ( es6 ) { if ( this.name === '*' ) { return this.module.name; } diff --git a/src/Module.js b/src/Module.js index 55316e4..6cc27da 100644 --- a/src/Module.js +++ b/src/Module.js @@ -21,6 +21,10 @@ class SyntheticDefaultDeclaration { reference.declaration = this; this.name = reference.name; } + + render () { + return this.name; + } } export default class Module { @@ -298,78 +302,6 @@ export default class Module { }); } - markAllStatements ( isEntryModule ) { - this.statements.forEach( statement => { - if ( statement.isIncluded ) return; // TODO can this happen? probably not... - - // skip import declarations... - if ( statement.isImportDeclaration ) { - // ...unless they're empty, in which case assume we're importing them for the side-effects - // THIS IS NOT FOOLPROOF. Probably need /*rollup: include */ or similar - if ( !statement.node.specifiers.length ) { - const id = this.resolvedIds[ statement.node.source.value ]; - const otherModule = this.bundle.moduleById[ id ]; - - if ( !otherModule.isExternal ) otherModule.markAllStatements(); - } - } - - // skip `export { foo, bar, baz }`... - else if ( statement.node.type === 'ExportNamedDeclaration' && statement.node.specifiers.length ) { - // ...but ensure they are defined, if this is the entry module - if ( isEntryModule ) statement.mark(); - } - - // include everything else - else { - statement.mark(); - } - }); - } - - markExport ( name, suggestedName, importer ) { - const reexport = this.reexports[ name ]; - const exportDeclaration = this.exports[ name ]; - - if ( reexport ) { - reexport.isUsed = true; - reexport.module.markExport( reexport.localName, suggestedName, this ); - } - - else if ( exportDeclaration ) { - exportDeclaration.isUsed = true; - if ( name === 'default' ) { - this.needsDefault = true; - this.suggestName( 'default', suggestedName ); - return exportDeclaration.statement.mark(); - } - - this.mark( exportDeclaration.localName ); - } - - else { - // See if there exists an export delegate that defines `name`. - let i; - for ( i = 0; i < this.exportAlls.length; i += 1 ) { - const declaration = this.exportAlls[i]; - - if ( declaration.module.exports[ name ] ) { - // It's found! This module exports `name` through declaration. - // It is however not imported into this scope. - this.exportDelegates[ name ] = declaration; - declaration.module.markExport( name ); - - declaration.statement.dependsOn[ name ] = - declaration.statement.stronglyDependsOn[ name ] = true; - - return; - } - } - - throw new Error( `Module ${this.id} does not export ${name} (imported by ${importer.id})` ); - } - } - parse ( ast ) { // The ast can be supplied programmatically (but usually won't be) if ( !ast ) { @@ -482,9 +414,7 @@ export default class Module { if ( reference.declaration ) { const { start } = reference.node; - const name = declaration.isExternal ? - declaration.getName( es6 ) : - declaration.name; + const name = declaration.render( es6 ); if ( reference.name !== name ) { magicString.overwrite( start, start + reference.name.length, name ); @@ -496,7 +426,14 @@ export default class Module { if ( statement.isExportDeclaration ) { // remove `export` from `export var foo = 42` if ( statement.node.type === 'ExportNamedDeclaration' && statement.node.declaration.type === 'VariableDeclaration' ) { - magicString.remove( statement.node.start, statement.node.declaration.start ); + const name = statement.node.declaration.declarations[0].id.name; + const declaration = this.declarations[ name ]; + + const end = declaration.isExported && declaration.isReassigned ? + statement.node.declaration.declarations[0].start : + statement.node.declaration.start; + + magicString.remove( statement.node.start, end ); } else if ( statement.node.type === 'ExportAllDeclaration' ) { diff --git a/src/Statement.js b/src/Statement.js index b15d723..357f2a4 100644 --- a/src/Statement.js +++ b/src/Statement.js @@ -92,6 +92,18 @@ export default class Statement { const reference = new Reference( node, scope ); references.push( reference ); + if ( node.type === 'Identifier' ) { + // `foo = bar` + if ( parent.type === 'AssignmentExpression' && node === parent.left ) { + reference.isReassignment = true; + } + + // `foo++` + if ( parent.type === 'UpdateExpression' && node === parent.argument ) { + reference.isReassignment = true; + } + } + this.skip(); // don't descend from `foo.bar.baz` into `foo.bar` } }, diff --git a/src/ast/Scope.js b/src/ast/Scope.js index ed3419a..2e6a543 100644 --- a/src/ast/Scope.js +++ b/src/ast/Scope.js @@ -38,11 +38,22 @@ class Declaration { this.references = []; this.statement = null; this.name = null; + + this.isReassigned = false; } addReference ( reference ) { reference.declaration = this; this.name = reference.name; // TODO handle differences of opinion + + if ( reference.isReassignment ) this.isReassigned = true; + } + + render ( es6 ) { + if ( es6 ) return this.name; + if ( !this.isReassigned || !this.isExported ) return this.name; + + return `exports.${this.name}`; } } diff --git a/src/finalisers/shared/getExportBlock.js b/src/finalisers/shared/getExportBlock.js index 3480074..d5f7740 100644 --- a/src/finalisers/shared/getExportBlock.js +++ b/src/finalisers/shared/getExportBlock.js @@ -7,7 +7,10 @@ export default function getExportBlock ( bundle, exportMode, mechanism = 'return .map( name => { const prop = name === 'default' ? `['default']` : `.${name}`; const declaration = bundle.entryModule.traceExport( name ); - return `exports${prop} = ${declaration.name};`; + + if ( declaration.isReassigned ) return null; + return `exports${prop} = ${declaration.render( false )};`; }) + .filter( Boolean ) .join( '\n' ); } diff --git a/test/function/assignment-to-exports/_config.js b/test/function/assignment-to-exports/_config.js index fac0cb3..c958adb 100644 --- a/test/function/assignment-to-exports/_config.js +++ b/test/function/assignment-to-exports/_config.js @@ -6,6 +6,5 @@ module.exports = { assert.equal( exports.count, 0 ); exports.incr(); assert.equal( exports.count, 1 ); - }, - // solo: true + } };