Comparing version 0.11.4 to 0.12.0
@@ -12,4 +12,6 @@ rollup version <%= version %> | ||
-o, --output <output> Output (if absent, prints to stdout) | ||
-f, --format [umd] Type of output (amd, cjs, es6, iife, umd) | ||
-f, --format [es6] Type of output (amd, cjs, es6, iife, umd) | ||
-e, --external Comma-separate list of module IDs to exclude | ||
-g, --globals Comma-separate list of `module ID:Global` pairs | ||
Any module IDs defined here are added to external | ||
-n, --name Name for UMD export | ||
@@ -20,6 +22,8 @@ -u, --id ID for AMD module (default is anonymous) | ||
Example: | ||
Examples: | ||
rollup --format=cjs --output=bundle.js -- src/main.js | ||
rollup -f iife --globals jquery:jQuery,angular:ng \ | ||
-i src/app.js -o build/app.js -m build/app.js.map | ||
@@ -26,0 +30,0 @@ Notes: |
require( 'source-map-support' ).install(); | ||
var path = require( 'path' ); | ||
var sander = require( 'sander' ); | ||
var Promise = sander.Promise; | ||
var handleError = require( './handleError' ); | ||
@@ -22,2 +19,22 @@ var rollup = require( '../' ); | ||
var external = options.external ? options.external.split( ',' ) : []; | ||
if ( options.globals ) { | ||
var globals = Object.create( null ); | ||
options.globals.split( ',' ).forEach(function ( str ) { | ||
var names = str.split( ':' ); | ||
globals[ names[0] ] = names[1]; | ||
// Add missing Module IDs to external. | ||
if ( external.indexOf( names[0] ) === -1 ) { | ||
external.push( names[0] ); | ||
} | ||
}); | ||
options.globals = globals; | ||
} | ||
options.external = external; | ||
try { | ||
@@ -31,4 +48,2 @@ bundle( options ).catch( handleError ); | ||
function bundle ( options, method ) { | ||
var bundleOptions, file; | ||
if ( !options.input ) { | ||
@@ -40,3 +55,3 @@ handleError({ code: 'MISSING_INPUT_OPTION' }); | ||
entry: options.input, | ||
external: options.external && options.external.split( ',' ) | ||
external: options.external | ||
}).then( function ( bundle ) { | ||
@@ -46,2 +61,3 @@ var generateOptions = { | ||
format: options.format, | ||
globals: options.globals, | ||
moduleId: options.id, | ||
@@ -62,3 +78,4 @@ moduleName: options.name, | ||
var code = result.code; | ||
var code = result.code, | ||
map = result.map; | ||
@@ -65,0 +82,0 @@ if ( options.sourcemap === 'inline' ) { |
# rollup changelog | ||
## 0.12.0 | ||
* Internal re-architecting, resulting in more efficient bundling with reduced memory usage | ||
* Shorthand properties are expanded if necessary ([#61](https://github.com/rollup/rollup/issues/61)) | ||
* Fixed various bugs with bundle external dependencies, particularly when generating ES6 bundles ([#59](https://github.com/rollup/rollup/issues/59)) | ||
* Add `--globals` option to CLI ([#60](https://github.com/rollup/rollup/pull/60)) | ||
* Allow imports of external modules for side-effects ([#55](https://github.com/rollup/rollup/pull/55)) | ||
* Prevent Rollup hanging on non-existent external module ([#54](https://github.com/rollup/rollup/pull/54)) | ||
## 0.11.4 | ||
@@ -4,0 +13,0 @@ |
{ | ||
"name": "rollup", | ||
"version": "0.11.4", | ||
"version": "0.12.0", | ||
"description": "Next-generation ES6 module bundler", | ||
@@ -49,3 +49,3 @@ "main": "dist/rollup.js", | ||
"chalk": "^1.0.0", | ||
"magic-string": "^0.6.2", | ||
"magic-string": "^0.6.4", | ||
"minimist": "^1.1.1", | ||
@@ -52,0 +52,0 @@ "sander": "^0.3.3", |
@@ -16,11 +16,2 @@ import { basename, extname } from './utils/path'; | ||
function isEmptyExportedVarDeclaration ( node, module, allBundleExports ) { | ||
if ( node.type !== 'VariableDeclaration' || node.declarations[0].init ) return false; | ||
const name = node.declarations[0].id.name; | ||
const canonicalName = module.getCanonicalName( name ); | ||
return canonicalName in allBundleExports; | ||
} | ||
export default class Bundle { | ||
@@ -94,8 +85,7 @@ constructor ( options ) { | ||
.then( () => { | ||
this.statements = this.sort(); | ||
this.deconflict(); | ||
this.orderedModules = this.sort(); | ||
}); | ||
} | ||
deconflict () { | ||
deconflict ( es6 ) { | ||
let definers = blank(); | ||
@@ -121,30 +111,31 @@ let conflicts = blank(); | ||
// 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' ); | ||
// 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 ) !== 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 ); | ||
}); | ||
}); | ||
@@ -220,6 +211,56 @@ }); | ||
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' ); | ||
@@ -250,3 +291,3 @@ // If we have named exports from the bundle, and those exports | ||
if ( originalDeclaration && originalDeclaration.type === 'VariableDeclaration' ) { | ||
const canonicalName = this.entryModule.getCanonicalName( exportDeclaration.localName ); | ||
const canonicalName = this.entryModule.getCanonicalName( exportDeclaration.localName, false ); | ||
@@ -264,109 +305,10 @@ allBundleExports[ canonicalName ] = `exports.${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 ) ) 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 ) ) return; | ||
let replacements = blank(); | ||
let bundleExports = blank(); | ||
keys( statement.dependsOn ) | ||
.concat( keys( statement.defines ) ) | ||
.forEach( name => { | ||
const canonicalName = statement.module.getCanonicalName( name ); | ||
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' ); | ||
if ( statement.node.declaration.type === 'Identifier' && canonicalName === module.getCanonicalName( statement.node.declaration.name ) ) { | ||
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; | ||
}); | ||
@@ -379,4 +321,4 @@ | ||
return `var ${module.getCanonicalName('*')} = {\n` + | ||
exportKeys.map( key => `${indentString}get ${key} () { return ${module.getCanonicalName(key)}; }` ).join( ',\n' ) + | ||
return `var ${module.getCanonicalName('*', format === 'es6')} = {\n` + | ||
exportKeys.map( key => `${indentString}get ${key} () { return ${module.getCanonicalName(key, format === 'es6')}; }` ).join( ',\n' ) + | ||
`\n};\n\n`; | ||
@@ -418,53 +360,2 @@ }).join( '' ); | ||
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 () { | ||
@@ -551,12 +442,4 @@ let seen = {}; | ||
let statements = []; | ||
ordered.forEach( module => { | ||
module.statements.forEach( statement => { | ||
if ( statement.isIncluded ) statements.push( statement ); | ||
}); | ||
}); | ||
return statements; | ||
return ordered; | ||
} | ||
} |
@@ -15,3 +15,10 @@ import { blank } from './utils/object'; | ||
this.needsDefault = false; | ||
// Invariant: needsNamed and needsAll are never both true at once. | ||
// Because an import with both a namespace and named import is invalid: | ||
// | ||
// import * as ns, { a } from '...' | ||
// | ||
this.needsNamed = false; | ||
this.needsAll = false; | ||
} | ||
@@ -23,13 +30,12 @@ | ||
getCanonicalName ( name ) { | ||
getCanonicalName ( name, es6 ) { | ||
if ( name === 'default' ) { | ||
return this.needsNamed ? `${this.name}__default` : this.name; | ||
return this.needsNamed && !es6 ? `${this.name}__default` : this.name; | ||
} | ||
if ( name === '*' ) { | ||
return this.name; | ||
return this.name; // TODO is this correct in ES6? | ||
} | ||
// TODO this depends on the output format... works for CJS etc but not ES6 | ||
return `${this.name}.${name}`; | ||
return es6 ? ( this.canonicalNames[ name ] || name ) : `${this.name}.${name}`; | ||
} | ||
@@ -36,0 +42,0 @@ |
import { getName, quoteId } from '../utils/map-helpers'; | ||
import getInteropBlock from './shared/getInteropBlock'; | ||
@@ -18,2 +19,6 @@ export default function amd ( bundle, magicString, { exportMode, indentString }, options ) { | ||
// var foo__default = 'default' in foo ? foo['default'] : foo; | ||
const interopBlock = getInteropBlock( bundle ); | ||
if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' ); | ||
const exports = bundle.entryModule.exports; | ||
@@ -31,5 +36,3 @@ | ||
if ( exportBlock ) { | ||
magicString.append( '\n\n' + exportBlock ); | ||
} | ||
if ( exportBlock ) magicString.append( '\n\n' + exportBlock ); | ||
@@ -36,0 +39,0 @@ return magicString |
import { keys } from '../utils/object'; | ||
export default function es6 ( bundle, magicString, { exportMode }, options ) { | ||
const introBlock = ''; // TODO... | ||
const importBlock = bundle.externalModules | ||
.map( module => { | ||
const specifiers = []; | ||
if ( module.needsDefault ) { | ||
specifiers.push( module.importedByBundle.filter( declaration => | ||
declaration.name === 'default' )[0].localName ); | ||
} | ||
if ( module.needsAll ) { | ||
specifiers.push( '* as ' + module.importedByBundle.filter( declaration => | ||
declaration.name === '*' )[0].localName ); | ||
} | ||
if ( module.needsNamed ) { | ||
specifiers.push( '{ ' + module.importedByBundle | ||
.filter( declaration => !/^(default|\*)$/.test( declaration.name ) ) | ||
.map( ({ name, localName }) => | ||
name === localName ? name : `${name} as ${localName}` ) | ||
.join( ', ' ) + ' }' ); | ||
} | ||
return specifiers.length ? | ||
`import ${specifiers.join( ', ' )} from '${module.id}';` : | ||
`import '${module.id}';`; | ||
}) | ||
.join( '\n' ); | ||
if ( importBlock ) { | ||
magicString.prepend( importBlock + '\n\n' ); | ||
} | ||
const exports = bundle.entryModule.exports; | ||
@@ -7,0 +37,0 @@ const exportBlock = keys( exports ).map( exportedName => { |
import { blank } from '../utils/object'; | ||
import { getName } from '../utils/map-helpers'; | ||
import getInteropBlock from './shared/getInteropBlock'; | ||
@@ -25,2 +26,6 @@ export default function iife ( bundle, magicString, { exportMode, indentString }, options ) { | ||
// var foo__default = 'default' in foo ? foo['default'] : foo; | ||
const interopBlock = getInteropBlock( bundle ); | ||
if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' ); | ||
if ( exportMode === 'default' ) { | ||
@@ -27,0 +32,0 @@ intro = `var ${options.moduleName} = ${intro}`; |
import { blank } from '../utils/object'; | ||
import { getName, quoteId, req } from '../utils/map-helpers'; | ||
import getInteropBlock from './shared/getInteropBlock'; | ||
@@ -14,3 +15,3 @@ export default function umd ( bundle, magicString, { exportMode, indentString }, options ) { | ||
let globalDeps = bundle.externalModules.map( module => { | ||
return globalNames[ module.id ] || module.name; | ||
return 'global.' + (globalNames[ module.id ] || module.name); | ||
}); | ||
@@ -44,2 +45,6 @@ | ||
// var foo__default = 'default' in foo ? foo['default'] : foo; | ||
const interopBlock = getInteropBlock( bundle ); | ||
if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' ); | ||
const exports = bundle.entryModule.exports; | ||
@@ -46,0 +51,0 @@ |
@@ -7,6 +7,4 @@ import { dirname } from './utils/path'; | ||
import walk from './ast/walk'; | ||
import analyse from './ast/analyse'; | ||
import { blank, keys } from './utils/object'; | ||
import { first, sequence } from './utils/promise'; | ||
import { isImportDeclaration, isExportDeclaration } from './utils/map-helpers'; | ||
import getLocation from './utils/getLocation'; | ||
@@ -25,2 +23,11 @@ import makeLegalIdentifier from './utils/makeLegalIdentifier'; | ||
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 Module { | ||
@@ -174,10 +181,8 @@ constructor ({ id, source, bundle }) { | ||
this.statements.forEach( statement => { | ||
if ( isImportDeclaration( statement ) ) this.addImport( statement ); | ||
else if ( isExportDeclaration( statement ) ) this.addExport( statement ); | ||
}); | ||
if ( statement.isImportDeclaration ) this.addImport( statement ); | ||
else if ( statement.isExportDeclaration ) this.addExport( statement ); | ||
analyse( this.magicString, this ); | ||
statement.analyse(); | ||
// consolidate names that are defined/modified in this module | ||
this.statements.forEach( statement => { | ||
// consolidate names that are defined/modified in this module | ||
keys( statement.defines ).forEach( name => { | ||
@@ -207,3 +212,3 @@ this.definitions[ name ] = statement; | ||
this.statements.forEach( statement => { | ||
if ( statement.isImportDeclaration && !statement.node.specifiers.length ) { | ||
if ( statement.isImportDeclaration && !statement.node.specifiers.length && !statement.module.isExternal ) { | ||
// include module for its side-effects | ||
@@ -288,3 +293,3 @@ strongDependencies[ statement.module.id ] = statement.module; // TODO is this right? `statement.module` should be `this`, surely? | ||
getCanonicalName ( localName ) { | ||
getCanonicalName ( localName, es6 ) { | ||
// Special case | ||
@@ -300,3 +305,5 @@ if ( localName === 'default' && ( this.exports.default.isModified || !this.suggestedNames.default ) ) { | ||
if ( !this.canonicalNames[ localName ] ) { | ||
const id = localName + ( es6 ? '-es6' : '' ); // TODO ugh this seems like a terrible hack | ||
if ( !this.canonicalNames[ id ] ) { | ||
let canonicalName; | ||
@@ -326,3 +333,3 @@ | ||
canonicalName = module.getCanonicalName( exporterLocalName ); | ||
canonicalName = module.getCanonicalName( exporterLocalName, es6 ); | ||
} | ||
@@ -335,6 +342,6 @@ } | ||
this.canonicalNames[ localName ] = canonicalName; | ||
this.canonicalNames[ id ] = canonicalName; | ||
} | ||
return this.canonicalNames[ localName ]; | ||
return this.canonicalNames[ id ]; | ||
} | ||
@@ -380,2 +387,4 @@ | ||
module.needsDefault = true; | ||
} else if ( importDeclaration.name === '*' ) { | ||
module.needsAll = true; | ||
} else { | ||
@@ -487,2 +496,5 @@ module.needsNamed = true; | ||
statement.module = module; | ||
if ( module.isExternal ) { | ||
return; | ||
} | ||
return module.markAllStatements(); | ||
@@ -536,4 +548,6 @@ }); | ||
let statements = []; | ||
let lastChar = 0; | ||
let commentIndex = 0; | ||
ast.body.map( node => { | ||
ast.body.forEach( node => { | ||
// special case - top-level var declarations with multiple declarators | ||
@@ -543,27 +557,48 @@ // should be split up. Otherwise, we may end up including code we | ||
if ( node.type === 'VariableDeclaration' && node.declarations.length > 1 ) { | ||
node.declarations.forEach( declarator => { | ||
const magicString = this.magicString.snip( declarator.start, declarator.end ).trim(); | ||
magicString.prepend( `${node.kind} ` ).append( ';' ); | ||
// remove the leading var/let/const | ||
this.magicString.remove( node.start, node.declarations[0].start ); | ||
node.declarations.forEach( ( declarator, i ) => { | ||
const { start, end } = declarator; | ||
const syntheticNode = { | ||
type: 'VariableDeclaration', | ||
kind: node.kind, | ||
start: node.start, | ||
end: node.end, | ||
declarations: [ declarator ] | ||
start, | ||
end, | ||
declarations: [ declarator ], | ||
isSynthetic: true | ||
}; | ||
const statement = new Statement( syntheticNode, magicString, this, statements.length ); | ||
const statement = new Statement( syntheticNode, this, start, end ); | ||
statements.push( statement ); | ||
}); | ||
lastChar = node.end; // TODO account for trailing line comment | ||
} | ||
else { | ||
const magicString = this.magicString.snip( node.start, node.end ).trim(); | ||
const statement = new Statement( node, magicString, this, statements.length ); | ||
let comment; | ||
do { | ||
comment = this.comments[ commentIndex ]; | ||
if ( !comment ) break; | ||
if ( comment.start > node.start ) break; | ||
commentIndex += 1; | ||
} while ( comment.end < lastChar ); | ||
const start = comment ? Math.min( comment.start, node.start ) : node.start; | ||
const end = node.end; // TODO account for trailing line comment | ||
const statement = new Statement( node, this, start, end ); | ||
statements.push( statement ); | ||
lastChar = end; | ||
} | ||
}); | ||
statements.forEach( ( statement, i ) => { | ||
const nextStatement = statements[ i + 1 ]; | ||
statement.next = nextStatement ? nextStatement.start : statement.end; | ||
}); | ||
return statements; | ||
@@ -573,5 +608,102 @@ } | ||
rename ( name, replacement ) { | ||
this.canonicalNames[ name ] = replacement; | ||
// TODO again, hacky... | ||
this.canonicalNames[ name ] = this.canonicalNames[ name + '-es6' ] = replacement; | ||
} | ||
render ( allBundleExports, format ) { | ||
let magicString = this.magicString.clone(); | ||
let previousIndex = -1; | ||
let previousMargin = 0; | ||
this.statements.forEach( ( statement, i ) => { | ||
if ( !statement.isIncluded ) { | ||
magicString.remove( statement.start, statement.next ); | ||
return; | ||
} | ||
// skip `export { foo, bar, baz }` | ||
if ( statement.node.type === 'ExportNamedDeclaration' ) { | ||
// skip `export { foo, bar, baz }` | ||
if ( statement.node.specifiers.length ) { | ||
magicString.remove( statement.start, statement.next ); | ||
return; | ||
}; | ||
// skip `export var foo;` if foo is exported | ||
if ( isEmptyExportedVarDeclaration( statement.node.declaration, statement.module, allBundleExports, format === 'es6' ) ) { | ||
magicString.remove( statement.start, statement.next ); | ||
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' ) ) { | ||
magicString.remove( statement.start, statement.next ); | ||
return; | ||
} | ||
// split up/remove var declarations as necessary | ||
if ( statement.node.isSynthetic ) { | ||
magicString.insert( statement.start, `${statement.node.kind} ` ); | ||
magicString.overwrite( statement.end, statement.next, ';\n' ); // TODO account for trailing newlines | ||
} | ||
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; | ||
} | ||
}); | ||
statement.replaceIdentifiers( magicString, 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' ) { | ||
magicString.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 ) { | ||
magicString.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' ) ) { | ||
magicString.remove( statement.start, statement.next ); | ||
return; | ||
} | ||
// anonymous functions should be converted into declarations | ||
if ( statement.node.declaration.type === 'FunctionExpression' ) { | ||
magicString.overwrite( statement.node.start, statement.node.declaration.start + 8, `function ${canonicalName}` ); | ||
} else { | ||
magicString.overwrite( statement.node.start, statement.node.declaration.start, `var ${canonicalName} = ` ); | ||
} | ||
} | ||
else { | ||
throw new Error( 'Unhandled export' ); | ||
} | ||
} | ||
}); | ||
return magicString.trim(); | ||
} | ||
suggestName ( defaultOrBatch, suggestion ) { | ||
@@ -578,0 +710,0 @@ // deconflict anonymous default exports with this module's definitions |
@@ -17,3 +17,3 @@ import { basename } from './utils/path'; | ||
return { | ||
generate: options => bundle.generate( options ), | ||
generate: options => bundle.render( options ), | ||
write: options => { | ||
@@ -25,3 +25,3 @@ if ( !options || !options.dest ) { | ||
const dest = options.dest; | ||
let { code, map } = bundle.generate( options ); | ||
let { code, map } = bundle.render( options ); | ||
@@ -28,0 +28,0 @@ let promises = []; |
@@ -12,8 +12,8 @@ import { blank, keys } from './utils/object'; | ||
export default class Statement { | ||
constructor ( node, magicString, module, index ) { | ||
constructor ( node, module, start, end ) { | ||
this.node = node; | ||
this.module = module; | ||
this.magicString = magicString; | ||
this.index = index; | ||
this.id = module.id + '#' + index; | ||
this.start = start; | ||
this.end = end; | ||
this.next = null; // filled in later | ||
@@ -28,10 +28,4 @@ this.scope = new Scope(); | ||
this.leadingComments = []; | ||
this.trailingComment = null; | ||
this.margin = [ 0, 0 ]; | ||
// some facts about this statement... | ||
this.isImportDeclaration = node.type === 'ImportDeclaration'; | ||
this.isExportDeclaration = /^Export/.test( node.type ); | ||
this.isExportAllDeclaration = /^ExportAll/.test( node.type ); | ||
} | ||
@@ -42,5 +36,2 @@ | ||
const statement = this; // TODO use arrow functions instead | ||
const magicString = this.magicString; | ||
let scope = this.scope; | ||
@@ -52,4 +43,2 @@ | ||
magicString.addSourcemapLocation( node.start ); | ||
switch ( node.type ) { | ||
@@ -153,3 +142,3 @@ case 'FunctionExpression': | ||
keys( scope.declarations ).forEach( name => { | ||
statement.defines[ name ] = true; | ||
this.defines[ name ] = true; | ||
}); | ||
@@ -255,4 +244,3 @@ } | ||
replaceIdentifiers ( names, bundleExports ) { | ||
const magicString = this.magicString.clone(); | ||
replaceIdentifiers ( magicString, names, bundleExports ) { | ||
const replacementStack = [ names ]; | ||
@@ -301,7 +289,9 @@ const nameList = keys( names ); | ||
// TODO clean this up | ||
try { | ||
magicString.insert( node.end, exportInitialisers ); | ||
} catch ( err ) { | ||
magicString.append( exportInitialisers ); | ||
if ( exportInitialisers ) { | ||
// TODO clean this up | ||
try { | ||
magicString.insert( node.end, exportInitialisers ); | ||
} catch ( err ) { | ||
magicString.append( exportInitialisers ); | ||
} | ||
} | ||
@@ -347,4 +337,17 @@ } | ||
// We want to rewrite identifiers (that aren't property names etc) | ||
if ( node.type !== 'Identifier' ) return; | ||
// if there's no replacement, or it's the same, there's nothing more to do | ||
const name = names[ node.name ]; | ||
if ( !name || name === node.name ) return; | ||
// shorthand properties (`obj = { foo }`) need to be expanded | ||
if ( parent.type === 'Property' && parent.shorthand ) { | ||
magicString.insert( node.end, `: ${name}` ); | ||
parent.key._skip = true; | ||
parent.value._skip = true; // redundant, but defensive | ||
return; | ||
} | ||
// property names etc can be disregarded | ||
if ( parent.type === 'MemberExpression' && !parent.computed && node !== parent.object ) return; | ||
@@ -355,7 +358,4 @@ if ( parent.type === 'Property' && node !== parent.value ) return; | ||
const name = names[ node.name ]; | ||
if ( name && name !== node.name ) { | ||
magicString.overwrite( node.start, node.end, name ); | ||
} | ||
// all other identifiers should be overwritten | ||
magicString.overwrite( node.start, node.end, name ); | ||
}, | ||
@@ -362,0 +362,0 @@ |
@@ -12,9 +12,1 @@ export function getName ( x ) { | ||
} | ||
export function isImportDeclaration ( statement ) { | ||
return statement.isImportDeclaration; | ||
} | ||
export function isExportDeclaration ( statement ) { | ||
return statement.isExportDeclaration; | ||
} |
@@ -14,5 +14,9 @@ // TODO does this all work on windows? | ||
export function dirname ( path ) { | ||
const match = /(\/|\\)[^\/\\]+$/.exec( path ); | ||
const match = /(\/|\\)[^\/\\]*$/.exec( path ); | ||
if ( !match ) return '.'; | ||
return path.slice( 0, -match[0].length ); | ||
const dir = path.slice( 0, -match[0].length ); | ||
// If `dir` is the empty string, we're at root. | ||
return dir ? dir : '/'; | ||
} | ||
@@ -23,3 +27,3 @@ | ||
if ( !match ) return ''; | ||
return match[0] | ||
return match[0]; | ||
} | ||
@@ -26,0 +30,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
898143
11265
Updatedmagic-string@^0.6.4