|
|
@ -205,201 +205,6 @@ export default class Bundle { |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
generate ( options = {} ) { |
|
|
|
let magicString = new MagicString.Bundle({ separator: '' }); |
|
|
|
|
|
|
|
const format = options.format || 'es6'; |
|
|
|
this.deconflict( format === 'es6' ); |
|
|
|
|
|
|
|
// If we have named exports from the bundle, and those exports
|
|
|
|
// are assigned to *within* the bundle, we may need to rewrite e.g.
|
|
|
|
//
|
|
|
|
// export let count = 0;
|
|
|
|
// export function incr () { count++ }
|
|
|
|
//
|
|
|
|
// might become...
|
|
|
|
//
|
|
|
|
// exports.count = 0;
|
|
|
|
// function incr () {
|
|
|
|
// exports.count += 1;
|
|
|
|
// }
|
|
|
|
// exports.incr = incr;
|
|
|
|
//
|
|
|
|
// This doesn't apply if the bundle is exported as ES6!
|
|
|
|
let allBundleExports = blank(); |
|
|
|
|
|
|
|
if ( format !== 'es6' ) { |
|
|
|
keys( this.entryModule.exports ).forEach( key => { |
|
|
|
const exportDeclaration = this.entryModule.exports[ key ]; |
|
|
|
|
|
|
|
const originalDeclaration = this.entryModule.findDeclaration( exportDeclaration.localName ); |
|
|
|
|
|
|
|
if ( originalDeclaration && originalDeclaration.type === 'VariableDeclaration' ) { |
|
|
|
const canonicalName = this.entryModule.getCanonicalName( exportDeclaration.localName, false ); |
|
|
|
|
|
|
|
allBundleExports[ canonicalName ] = `exports.${key}`; |
|
|
|
this.varExports[ key ] = true; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
// since we're rewriting variable exports, we want to
|
|
|
|
// ensure we don't try and export them again at the bottom
|
|
|
|
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; |
|
|
|
|
|
|
|
// skip `export var foo;` if foo is exported
|
|
|
|
if ( isEmptyExportedVarDeclaration( statement.node.declaration, statement.module, allBundleExports, format === 'es6' ) ) return; |
|
|
|
} |
|
|
|
|
|
|
|
// 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
|
|
|
|
const indentString = magicString.getIndentString(); |
|
|
|
const namespaceBlock = this.internalNamespaceModules.map( module => { |
|
|
|
const exportKeys = keys( module.exports ); |
|
|
|
|
|
|
|
return `var ${module.getCanonicalName('*', format === 'es6')} = {\n` + |
|
|
|
exportKeys.map( key => `${indentString}get ${key} () { return ${module.getCanonicalName(key, format === 'es6')}; }` ).join( ',\n' ) + |
|
|
|
`\n};\n\n`; |
|
|
|
}).join( '' ); |
|
|
|
|
|
|
|
magicString.prepend( namespaceBlock ); |
|
|
|
|
|
|
|
const finalise = finalisers[ format ]; |
|
|
|
|
|
|
|
if ( !finalise ) { |
|
|
|
throw new Error( `You must specify an output type - valid options are ${keys( finalisers ).join( ', ' )}` ); |
|
|
|
} |
|
|
|
|
|
|
|
magicString = finalise( this, magicString.trim(), { |
|
|
|
// Determine export mode - 'default', 'named', 'none'
|
|
|
|
exportMode: getExportMode( this, options.exports ), |
|
|
|
|
|
|
|
// Determine indentation
|
|
|
|
indentString: getIndentString( magicString, options ) |
|
|
|
}, options ); |
|
|
|
|
|
|
|
const code = magicString.toString(); |
|
|
|
let map = null; |
|
|
|
|
|
|
|
if ( options.sourceMap ) { |
|
|
|
const file = options.sourceMapFile || options.dest; |
|
|
|
map = magicString.generateMap({ |
|
|
|
includeContent: true, |
|
|
|
file |
|
|
|
// TODO
|
|
|
|
}); |
|
|
|
|
|
|
|
map.sources = map.sources.map( unixizePath ); |
|
|
|
} |
|
|
|
|
|
|
|
return { code, map }; |
|
|
|
} |
|
|
|
|
|
|
|
markAllModifierStatements () { |
|
|
|
let settled = true; |
|
|
|
let promises = []; |
|
|
|