|
|
@ -13,15 +13,6 @@ import getExportMode from './utils/getExportMode'; |
|
|
|
import getIndentString from './utils/getIndentString'; |
|
|
|
import { unixizePath } from './utils/normalizePlatform.js'; |
|
|
|
|
|
|
|
function isEmptyExportedVarDeclaration ( node, module, allBundleExports, es6 ) { |
|
|
|
if ( node.type !== 'VariableDeclaration' || node.declarations[0].init ) return false; |
|
|
|
|
|
|
|
const name = node.declarations[0].id.name; |
|
|
|
const canonicalName = module.getCanonicalName( name, es6 ); |
|
|
|
|
|
|
|
return canonicalName in allBundleExports; |
|
|
|
} |
|
|
|
|
|
|
|
export default class Bundle { |
|
|
|
constructor ( options ) { |
|
|
|
this.entry = options.entry; |
|
|
@ -90,7 +81,7 @@ export default class Bundle { |
|
|
|
return this.markAllModifierStatements(); |
|
|
|
}) |
|
|
|
.then( () => { |
|
|
|
this.statements = this.sort(); |
|
|
|
this.orderedModules = this.sort(); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
@ -115,34 +106,35 @@ export default class Bundle { |
|
|
|
}); |
|
|
|
|
|
|
|
// Discover conflicts (i.e. two statements in separate modules both define `foo`)
|
|
|
|
this.statements.forEach( statement => { |
|
|
|
const module = statement.module; |
|
|
|
const names = keys( statement.defines ); |
|
|
|
this.orderedModules.forEach( module => { |
|
|
|
module.statements.forEach( statement => { |
|
|
|
const names = keys( statement.defines ); |
|
|
|
|
|
|
|
// with default exports that are expressions (`export default 42`),
|
|
|
|
// we need to ensure that the name chosen for the expression does
|
|
|
|
// not conflict
|
|
|
|
if ( statement.node.type === 'ExportDefaultDeclaration' ) { |
|
|
|
const name = module.getCanonicalName( 'default', es6 ); |
|
|
|
// with default exports that are expressions (`export default 42`),
|
|
|
|
// we need to ensure that the name chosen for the expression does
|
|
|
|
// not conflict
|
|
|
|
if ( statement.node.type === 'ExportDefaultDeclaration' ) { |
|
|
|
const name = module.getCanonicalName( 'default', es6 ); |
|
|
|
|
|
|
|
const isProxy = statement.node.declaration && statement.node.declaration.type === 'Identifier'; |
|
|
|
const shouldDeconflict = !isProxy || ( module.getCanonicalName( statement.node.declaration.name, es6 ) !== name ); |
|
|
|
const isProxy = statement.node.declaration && statement.node.declaration.type === 'Identifier'; |
|
|
|
const shouldDeconflict = !isProxy || ( module.getCanonicalName( statement.node.declaration.name, es6 ) !== name ); |
|
|
|
|
|
|
|
if ( shouldDeconflict && !~names.indexOf( name ) ) { |
|
|
|
names.push( name ); |
|
|
|
if ( shouldDeconflict && !~names.indexOf( name ) ) { |
|
|
|
names.push( name ); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
names.forEach( name => { |
|
|
|
if ( definers[ name ] ) { |
|
|
|
conflicts[ name ] = true; |
|
|
|
} else { |
|
|
|
definers[ name ] = []; |
|
|
|
} |
|
|
|
names.forEach( name => { |
|
|
|
if ( definers[ name ] ) { |
|
|
|
conflicts[ name ] = true; |
|
|
|
} else { |
|
|
|
definers[ name ] = []; |
|
|
|
} |
|
|
|
|
|
|
|
// TODO in good js, there shouldn't be duplicate definitions
|
|
|
|
// per module... but some people write bad js
|
|
|
|
definers[ name ].push( module ); |
|
|
|
// TODO in good js, there shouldn't be duplicate definitions
|
|
|
|
// per module... but some people write bad js
|
|
|
|
definers[ name ].push( module ); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
@ -213,9 +205,58 @@ export default class Bundle { |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
generate ( options = {} ) { |
|
|
|
let magicString = new MagicString.Bundle({ separator: '' }); |
|
|
|
markAllModifierStatements () { |
|
|
|
let settled = true; |
|
|
|
let promises = []; |
|
|
|
|
|
|
|
this.modules.forEach( module => { |
|
|
|
module.statements.forEach( statement => { |
|
|
|
if ( statement.isIncluded ) return; |
|
|
|
|
|
|
|
keys( statement.modifies ).forEach( name => { |
|
|
|
const definingStatement = module.definitions[ name ]; |
|
|
|
const exportDeclaration = module.exports[ name ] || ( |
|
|
|
module.exports.default && module.exports.default.identifier === name && module.exports.default |
|
|
|
); |
|
|
|
|
|
|
|
const shouldMark = ( definingStatement && definingStatement.isIncluded ) || |
|
|
|
( exportDeclaration && exportDeclaration.isUsed ); |
|
|
|
|
|
|
|
if ( shouldMark ) { |
|
|
|
settled = false; |
|
|
|
promises.push( statement.mark() ); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// special case - https://github.com/rollup/rollup/pull/40
|
|
|
|
const importDeclaration = module.imports[ name ]; |
|
|
|
if ( !importDeclaration ) return; |
|
|
|
|
|
|
|
const promise = Promise.resolve( importDeclaration.module || this.fetchModule( importDeclaration.source, module.id ) ) |
|
|
|
.then( module => { |
|
|
|
importDeclaration.module = module; |
|
|
|
const exportDeclaration = module.exports[ importDeclaration.name ]; |
|
|
|
// TODO things like `export default a + b` don't apply here... right?
|
|
|
|
return module.findDefiningStatement( exportDeclaration.localName ); |
|
|
|
}) |
|
|
|
.then( definingStatement => { |
|
|
|
if ( !definingStatement ) return; |
|
|
|
|
|
|
|
settled = false; |
|
|
|
return statement.mark(); |
|
|
|
}); |
|
|
|
|
|
|
|
promises.push( promise ); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
return Promise.all( promises ).then( () => { |
|
|
|
if ( !settled ) return this.markAllModifierStatements(); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
render ( options = {} ) { |
|
|
|
const format = options.format || 'es6'; |
|
|
|
this.deconflict( format === 'es6' ); |
|
|
|
|
|
|
@ -256,113 +297,14 @@ export default class Bundle { |
|
|
|
this.toExport = keys( this.entryModule.exports ) |
|
|
|
.filter( key => !this.varExports[ key ] ); |
|
|
|
|
|
|
|
// Apply new names and add to the output bundle
|
|
|
|
let previousModule = null; |
|
|
|
let previousIndex = -1; |
|
|
|
let previousMargin = 0; |
|
|
|
|
|
|
|
this.statements.forEach( statement => { |
|
|
|
// skip `export { foo, bar, baz }`
|
|
|
|
if ( statement.node.type === 'ExportNamedDeclaration' ) { |
|
|
|
// skip `export { foo, bar, baz }`
|
|
|
|
if ( statement.node.specifiers.length ) return; |
|
|
|
let magicString = new MagicString.Bundle({ separator: '\n\n' }); |
|
|
|
|
|
|
|
// skip `export var foo;` if foo is exported
|
|
|
|
if ( isEmptyExportedVarDeclaration( statement.node.declaration, statement.module, allBundleExports, format === 'es6' ) ) return; |
|
|
|
this.orderedModules.forEach( module => { |
|
|
|
const source = module.render( allBundleExports, format ); |
|
|
|
if ( source.toString().length ) { |
|
|
|
magicString.addSource( source ); |
|
|
|
} |
|
|
|
|
|
|
|
// skip empty var declarations for exported bindings
|
|
|
|
// (otherwise we're left with `exports.foo;`, which is useless)
|
|
|
|
if ( isEmptyExportedVarDeclaration( statement.node, statement.module, allBundleExports, format === 'es6' ) ) return; |
|
|
|
|
|
|
|
let replacements = blank(); |
|
|
|
let bundleExports = blank(); |
|
|
|
|
|
|
|
keys( statement.dependsOn ) |
|
|
|
.concat( keys( statement.defines ) ) |
|
|
|
.forEach( name => { |
|
|
|
const canonicalName = statement.module.getCanonicalName( name, format === 'es6' ); |
|
|
|
|
|
|
|
if ( allBundleExports[ canonicalName ] ) { |
|
|
|
bundleExports[ name ] = replacements[ name ] = allBundleExports[ canonicalName ]; |
|
|
|
} else if ( name !== canonicalName ) { |
|
|
|
replacements[ name ] = canonicalName; |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
const source = statement.replaceIdentifiers( replacements, bundleExports ); |
|
|
|
|
|
|
|
// modify exports as necessary
|
|
|
|
if ( statement.isExportDeclaration ) { |
|
|
|
// remove `export` from `export var foo = 42`
|
|
|
|
if ( statement.node.type === 'ExportNamedDeclaration' && statement.node.declaration.type === 'VariableDeclaration' ) { |
|
|
|
source.remove( statement.node.start, statement.node.declaration.start ); |
|
|
|
} |
|
|
|
|
|
|
|
// remove `export` from `export class Foo {...}` or `export default Foo`
|
|
|
|
// TODO default exports need different treatment
|
|
|
|
else if ( statement.node.declaration.id ) { |
|
|
|
source.remove( statement.node.start, statement.node.declaration.start ); |
|
|
|
} |
|
|
|
|
|
|
|
else if ( statement.node.type === 'ExportDefaultDeclaration' ) { |
|
|
|
const module = statement.module; |
|
|
|
const canonicalName = module.getCanonicalName( 'default', format === 'es6' ); |
|
|
|
|
|
|
|
if ( statement.node.declaration.type === 'Identifier' && canonicalName === module.getCanonicalName( statement.node.declaration.name, format === 'es6' ) ) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// anonymous functions should be converted into declarations
|
|
|
|
if ( statement.node.declaration.type === 'FunctionExpression' ) { |
|
|
|
source.overwrite( statement.node.start, statement.node.declaration.start + 8, `function ${canonicalName}` ); |
|
|
|
} else { |
|
|
|
source.overwrite( statement.node.start, statement.node.declaration.start, `var ${canonicalName} = ` ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
throw new Error( 'Unhandled export' ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// ensure there is always a newline between statements, and add
|
|
|
|
// additional newlines as necessary to reflect original source
|
|
|
|
const minSeparation = ( previousModule !== statement.module ) || ( statement.index !== previousIndex + 1 ) ? 3 : 2; |
|
|
|
const margin = Math.max( minSeparation, statement.margin[0], previousMargin ); |
|
|
|
let newLines = new Array( margin ).join( '\n' ); |
|
|
|
|
|
|
|
// add leading comments
|
|
|
|
if ( statement.leadingComments.length ) { |
|
|
|
const commentBlock = newLines + statement.leadingComments.map( ({ separator, comment }) => { |
|
|
|
return separator + ( comment.block ? |
|
|
|
`/*${comment.text}*/` : |
|
|
|
`//${comment.text}` ); |
|
|
|
}).join( '' ); |
|
|
|
|
|
|
|
magicString.addSource( new MagicString( commentBlock ) ); |
|
|
|
newLines = new Array( statement.margin[0] ).join( '\n' ); // TODO handle gaps between comment block and statement
|
|
|
|
} |
|
|
|
|
|
|
|
// add the statement itself
|
|
|
|
magicString.addSource({ |
|
|
|
content: source, |
|
|
|
separator: newLines |
|
|
|
}); |
|
|
|
|
|
|
|
// add trailing comments
|
|
|
|
const comment = statement.trailingComment; |
|
|
|
if ( comment ) { |
|
|
|
const commentBlock = comment.block ? |
|
|
|
` /*${comment.text}*/` : |
|
|
|
` //${comment.text}`; |
|
|
|
|
|
|
|
magicString.append( commentBlock ); |
|
|
|
} |
|
|
|
|
|
|
|
previousMargin = statement.margin[1]; |
|
|
|
previousModule = statement.module; |
|
|
|
previousIndex = statement.index; |
|
|
|
}); |
|
|
|
|
|
|
|
// prepend bundle with internal namespaces
|
|
|
@ -408,57 +350,6 @@ export default class Bundle { |
|
|
|
return { code, map }; |
|
|
|
} |
|
|
|
|
|
|
|
markAllModifierStatements () { |
|
|
|
let settled = true; |
|
|
|
let promises = []; |
|
|
|
|
|
|
|
this.modules.forEach( module => { |
|
|
|
module.statements.forEach( statement => { |
|
|
|
if ( statement.isIncluded ) return; |
|
|
|
|
|
|
|
keys( statement.modifies ).forEach( name => { |
|
|
|
const definingStatement = module.definitions[ name ]; |
|
|
|
const exportDeclaration = module.exports[ name ] || ( |
|
|
|
module.exports.default && module.exports.default.identifier === name && module.exports.default |
|
|
|
); |
|
|
|
|
|
|
|
const shouldMark = ( definingStatement && definingStatement.isIncluded ) || |
|
|
|
( exportDeclaration && exportDeclaration.isUsed ); |
|
|
|
|
|
|
|
if ( shouldMark ) { |
|
|
|
settled = false; |
|
|
|
promises.push( statement.mark() ); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// special case - https://github.com/rollup/rollup/pull/40
|
|
|
|
const importDeclaration = module.imports[ name ]; |
|
|
|
if ( !importDeclaration ) return; |
|
|
|
|
|
|
|
const promise = Promise.resolve( importDeclaration.module || this.fetchModule( importDeclaration.source, module.id ) ) |
|
|
|
.then( module => { |
|
|
|
importDeclaration.module = module; |
|
|
|
const exportDeclaration = module.exports[ importDeclaration.name ]; |
|
|
|
// TODO things like `export default a + b` don't apply here... right?
|
|
|
|
return module.findDefiningStatement( exportDeclaration.localName ); |
|
|
|
}) |
|
|
|
.then( definingStatement => { |
|
|
|
if ( !definingStatement ) return; |
|
|
|
|
|
|
|
settled = false; |
|
|
|
return statement.mark(); |
|
|
|
}); |
|
|
|
|
|
|
|
promises.push( promise ); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
return Promise.all( promises ).then( () => { |
|
|
|
if ( !settled ) return this.markAllModifierStatements(); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
sort () { |
|
|
|
let seen = {}; |
|
|
|
let ordered = []; |
|
|
@ -540,14 +431,6 @@ export default class Bundle { |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
let statements = []; |
|
|
|
|
|
|
|
ordered.forEach( module => { |
|
|
|
module.statements.forEach( statement => { |
|
|
|
if ( statement.isIncluded ) statements.push( statement ); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
return statements; |
|
|
|
return ordered; |
|
|
|
} |
|
|
|
} |
|
|
|