Comparing version 0.20.5 to 0.21.0
@@ -7,2 +7,5 @@ require( 'source-map-support' ).install(); | ||
// log to stderr to keep `rollup main.js > bundle.js` from breaking | ||
var log = console.error.bind(console); | ||
module.exports = function ( command ) { | ||
@@ -28,3 +31,3 @@ if ( command._.length > 1 ) { | ||
entry: config, | ||
onwarn: function () {} | ||
onwarn: log | ||
}).then( function ( bundle ) { | ||
@@ -54,3 +57,4 @@ var code = bundle.generate({ | ||
require.extensions[ '.js' ] = defaultLoader; | ||
}); | ||
}) | ||
.catch(log); | ||
} else { | ||
@@ -90,2 +94,4 @@ execute( {}, command ); | ||
options.onwarn = options.onwarn || log; | ||
options.external = external; | ||
@@ -92,0 +98,0 @@ options.indent = command.indent !== false; |
# rollup changelog | ||
## 0.21.0 | ||
* Only include statements whose side-effects are relevant (i.e. contribute to exports or affect global state) ([#253](https://github.com/rollup/rollup/pull/253)) ([#253](https://github.com/rollup/rollup/pull/253)) | ||
* Exclude dead branches from analysis and inclusion ([#249](https://github.com/rollup/rollup/pull/249)) | ||
* Add `aggressive: true` option to eliminate all side-effects outside entry module | ||
* More informative error when re-exporting non-existent binding ([#274](https://github.com/rollup/rollup/issues/274)) | ||
* Fix infinite recursion bug ([#291](https://github.com/rollup/rollup/issues/291)) | ||
* Log errors when using `rollup --config` ([#288](https://github.com/rollup/rollup/pull/288)) | ||
* Return rejected promises on startup instead of throwing error, if options are invalid ([#303](https://github.com/rollup/rollup/pull/303)) | ||
## 0.20.5 | ||
@@ -4,0 +14,0 @@ |
{ | ||
"name": "rollup", | ||
"version": "0.20.5", | ||
"version": "0.21.0", | ||
"description": "Next-generation ES6 module bundler", | ||
@@ -11,3 +11,3 @@ "main": "dist/rollup.js", | ||
"scripts": { | ||
"pretest": "npm run build", | ||
"pretest": "npm run lint && npm run build", | ||
"test": "mocha", | ||
@@ -21,3 +21,3 @@ "pretest-coverage": "npm run build", | ||
"prepublish": "npm test && npm run build:browser", | ||
"lint": "eslint src" | ||
"lint": "eslint src browser" | ||
}, | ||
@@ -45,3 +45,3 @@ "repository": { | ||
"devDependencies": { | ||
"acorn": "^2.5.0", | ||
"acorn": "^2.6.4", | ||
"babel-core": "^5.8.32", | ||
@@ -48,0 +48,0 @@ "codecov.io": "^0.1.6", |
@@ -5,3 +5,3 @@ # Rollup | ||
<a href="https://travis-ci.org/rollup/rollup"> | ||
<img src="http://img.shields.io/travis/rollup/rollup.svg" | ||
<img src="https://api.travis-ci.org/rollup/rollup.svg?branch=master" | ||
alt="build status"> | ||
@@ -8,0 +8,0 @@ </a> |
@@ -23,3 +23,6 @@ import { walk } from 'estree-walker'; | ||
const isBlockDeclaration = blockDeclarations[ node.kind ]; | ||
node.declarations.forEach( declaration => scope.addDeclaration( declaration, isBlockDeclaration, true ) ); | ||
node.declarations.forEach( declarator => { | ||
scope.addDeclaration( declarator, isBlockDeclaration, true ); | ||
}); | ||
} | ||
@@ -26,0 +29,0 @@ |
import { blank, keys } from '../utils/object.js'; | ||
import Declaration from '../Declaration.js'; | ||
@@ -36,37 +37,2 @@ const extractors = { | ||
class Declaration { | ||
constructor () { | ||
this.statement = null; | ||
this.name = null; | ||
this.isReassigned = false; | ||
this.aliases = []; | ||
} | ||
addAlias ( declaration ) { | ||
this.aliases.push( declaration ); | ||
} | ||
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}`; | ||
} | ||
use () { | ||
this.isUsed = true; | ||
if ( this.statement ) this.statement.mark(); | ||
this.aliases.forEach( alias => alias.use() ); | ||
} | ||
} | ||
export default class Scope { | ||
@@ -78,2 +44,3 @@ constructor ( options ) { | ||
this.isBlockScope = !!options.block; | ||
this.isTopLevel = !this.parent || ( this.parent.isTopLevel && this.isBlockScope ); | ||
@@ -85,3 +52,3 @@ this.declarations = blank(); | ||
extractNames( param ).forEach( name => { | ||
this.declarations[ name ] = new Declaration( name ); | ||
this.declarations[ name ] = new Declaration( param, true ); | ||
}); | ||
@@ -99,3 +66,3 @@ }); | ||
extractNames( node.id ).forEach( name => { | ||
this.declarations[ name ] = new Declaration( name ); | ||
this.declarations[ name ] = new Declaration( node ); | ||
}); | ||
@@ -102,0 +69,0 @@ } |
@@ -42,3 +42,2 @@ import Promise from 'es6-promise/lib/es6-promise/promise.js'; | ||
this.pending = blank(); | ||
this.moduleById = blank(); | ||
@@ -54,2 +53,3 @@ this.modules = []; | ||
this.onwarn = options.onwarn || onwarn; | ||
this.aggressive = options.aggressive; | ||
@@ -61,2 +61,5 @@ // TODO strictly speaking, this only applies with non-ES6, non-default-only bundles | ||
build () { | ||
// Phase 1 – discovery. We load the entry module and find which | ||
// modules it imports, and import those, until we have all | ||
// of the entry module's dependencies | ||
return Promise.resolve( this.resolveId( this.entry, undefined ) ) | ||
@@ -67,2 +70,4 @@ .then( id => this.fetchModule( id, undefined ) ) | ||
// Phase 2 – binding. We link references to their declarations | ||
// to generate a complete picture of the bundle | ||
this.modules.forEach( module => module.bindImportSpecifiers() ); | ||
@@ -72,2 +77,5 @@ this.modules.forEach( module => module.bindAliases() ); | ||
// Phase 3 – marking. We 'run' each statement to see which ones | ||
// need to be included in the generated bundle | ||
// mark all export statements | ||
@@ -81,2 +89,3 @@ entryModule.getExports().forEach( name => { | ||
// mark statements that should appear in the bundle | ||
let settled = false; | ||
@@ -86,7 +95,14 @@ while ( !settled ) { | ||
this.modules.forEach( module => { | ||
if ( module.markAllSideEffects() ) settled = false; | ||
}); | ||
if ( this.aggressive ) { | ||
settled = !entryModule.run(); | ||
} else { | ||
this.modules.forEach( module => { | ||
if ( module.run() ) settled = false; | ||
}); | ||
} | ||
} | ||
// Phase 4 – final preparation. We order the modules with an | ||
// enhanced topological sort that accounts for cycles, then | ||
// ensure that names are deconflicted throughout the bundle | ||
this.orderedModules = this.sort(); | ||
@@ -131,4 +147,4 @@ this.deconflict(); | ||
// short-circuit cycles | ||
if ( this.pending[ id ] ) return null; | ||
this.pending[ id ] = true; | ||
if ( id in this.moduleById ) return null; | ||
this.moduleById[ id ] = null; | ||
@@ -269,8 +285,6 @@ return Promise.resolve( this.load( id ) ) | ||
keys( strongDependencies ).forEach( id => { | ||
const imported = strongDependencies[ id ]; | ||
strongDependencies.forEach( imported => { | ||
strongDeps[ module.id ].push( imported ); | ||
if ( seen[ id ] ) { | ||
if ( seen[ imported.id ] ) { | ||
// we need to prevent an infinite loop, and note that | ||
@@ -285,6 +299,4 @@ // we need to check for strong/weak dependency relationships | ||
keys( weakDependencies ).forEach( id => { | ||
const imported = weakDependencies[ id ]; | ||
if ( seen[ id ] ) { | ||
weakDependencies.forEach( imported => { | ||
if ( seen[ imported.id ] ) { | ||
// we need to prevent an infinite loop, and note that | ||
@@ -291,0 +303,0 @@ // we need to check for strong/weak dependency relationships |
import { blank } from './utils/object.js'; | ||
import makeLegalIdentifier from './utils/makeLegalIdentifier.js'; | ||
import { ExternalDeclaration } from './Declaration.js'; | ||
class ExternalDeclaration { | ||
constructor ( module, name ) { | ||
this.module = module; | ||
this.name = name; | ||
this.isExternal = true; | ||
} | ||
addAlias () { | ||
// noop | ||
} | ||
addReference ( reference ) { | ||
reference.declaration = this; | ||
if ( this.name === 'default' || this.name === '*' ) { | ||
this.module.suggestName( reference.name ); | ||
} | ||
} | ||
render ( es6 ) { | ||
if ( this.name === '*' ) { | ||
return this.module.name; | ||
} | ||
if ( this.name === 'default' ) { | ||
return !es6 && this.module.exportsNames ? | ||
`${this.module.name}__default` : | ||
this.module.name; | ||
} | ||
return es6 ? this.name : `${this.module.name}.${this.name}`; | ||
} | ||
use () { | ||
// noop? | ||
} | ||
} | ||
export default class ExternalModule { | ||
@@ -43,0 +6,0 @@ constructor ( id ) { |
@@ -10,124 +10,6 @@ import { parse } from 'acorn/src/index.js'; | ||
import SOURCEMAPPING_URL from './utils/sourceMappingURL.js'; | ||
import { SyntheticDefaultDeclaration, SyntheticNamespaceDeclaration } from './Declaration.js'; | ||
import { isFalsy, isTruthy } from './ast/conditions.js'; | ||
import { emptyBlockStatement } from './ast/create.js'; | ||
class SyntheticDefaultDeclaration { | ||
constructor ( node, statement, name ) { | ||
this.node = node; | ||
this.statement = statement; | ||
this.name = name; | ||
this.original = null; | ||
this.isExported = false; | ||
this.aliases = []; | ||
} | ||
addAlias ( declaration ) { | ||
this.aliases.push( declaration ); | ||
} | ||
addReference ( reference ) { | ||
// Don't change the name to `default`; it's not a valid identifier name. | ||
if ( reference.name === 'default' ) return; | ||
reference.declaration = this; | ||
this.name = reference.name; | ||
} | ||
bind ( declaration ) { | ||
this.original = declaration; | ||
} | ||
render () { | ||
return !this.original || this.original.isReassigned ? | ||
this.name : | ||
this.original.render(); | ||
} | ||
use () { | ||
this.isUsed = true; | ||
this.statement.mark(); | ||
if ( this.original ) this.original.use(); | ||
this.aliases.forEach( alias => alias.use() ); | ||
} | ||
} | ||
class SyntheticNamespaceDeclaration { | ||
constructor ( module ) { | ||
this.module = module; | ||
this.name = null; | ||
this.needsNamespaceBlock = false; | ||
this.aliases = []; | ||
this.originals = blank(); | ||
module.getExports().forEach( name => { | ||
this.originals[ name ] = module.traceExport( name ); | ||
}); | ||
} | ||
addAlias ( declaration ) { | ||
this.aliases.push( declaration ); | ||
} | ||
addReference ( reference ) { | ||
// if we have e.g. `foo.bar`, we can optimise | ||
// the reference by pointing directly to `bar` | ||
if ( reference.parts.length ) { | ||
reference.name = reference.parts.shift(); | ||
reference.end += reference.name.length + 1; // TODO this is brittle | ||
const original = this.originals[ reference.name ]; | ||
// throw with an informative error message if the reference doesn't exist. | ||
if ( !original ) { | ||
this.module.bundle.onwarn( `Export '${reference.name}' is not defined by '${this.module.id}'` ); | ||
reference.isUndefined = true; | ||
return; | ||
} | ||
original.addReference( reference ); | ||
return; | ||
} | ||
// otherwise we're accessing the namespace directly, | ||
// which means we need to mark all of this module's | ||
// exports and render a namespace block in the bundle | ||
if ( !this.needsNamespaceBlock ) { | ||
this.needsNamespaceBlock = true; | ||
this.module.bundle.internalNamespaces.push( this ); | ||
} | ||
reference.declaration = this; | ||
this.name = reference.name; | ||
} | ||
renderBlock ( indentString ) { | ||
const members = keys( this.originals ).map( name => { | ||
const original = this.originals[ name ]; | ||
if ( original.isReassigned ) { | ||
return `${indentString}get ${name} () { return ${original.render()}; }`; | ||
} | ||
return `${indentString}${name}: ${original.render()}`; | ||
}); | ||
return `var ${this.render()} = Object.freeze({\n${members.join( ',\n' )}\n});\n\n`; | ||
} | ||
render () { | ||
return this.name; | ||
} | ||
use () { | ||
keys( this.originals ).forEach( name => { | ||
this.originals[ name ].use(); | ||
}); | ||
this.aliases.forEach( alias => alias.use() ); | ||
} | ||
} | ||
export default class Module { | ||
@@ -173,2 +55,4 @@ constructor ({ id, code, originalCode, ast, sourceMapChain, bundle }) { | ||
this.analyse(); | ||
this.strongDependencies = []; | ||
} | ||
@@ -193,2 +77,3 @@ | ||
this.reexports[ specifier.exported.name ] = { | ||
start: specifier.start, | ||
source, | ||
@@ -280,3 +165,3 @@ localName: specifier.local.name, | ||
statement.analyse(); | ||
statement.firstPass(); | ||
@@ -305,4 +190,7 @@ statement.scope.eachDeclaration( ( name, declaration ) => { | ||
const init = statement.node.declarations[0].init; | ||
if ( !init || init.type === 'FunctionExpression' ) return; | ||
statement.references.forEach( reference => { | ||
if ( reference.name === name || !reference.isImmediatelyUsed ) return; | ||
if ( reference.name === name ) return; | ||
@@ -361,4 +249,4 @@ const otherDeclaration = this.trace( reference.name ); | ||
consolidateDependencies () { | ||
let strongDependencies = blank(); | ||
let weakDependencies = blank(); | ||
let strongDependencies = []; | ||
let weakDependencies = []; | ||
@@ -370,24 +258,10 @@ // treat all imports as weak dependencies | ||
if ( !dependency.isExternal ) { | ||
weakDependencies[ dependency.id ] = dependency; | ||
if ( !dependency.isExternal && !~weakDependencies.indexOf( dependency ) ) { | ||
weakDependencies.push( dependency ); | ||
} | ||
}); | ||
// identify strong dependencies to break ties in case of cycles | ||
this.statements.forEach( statement => { | ||
statement.references.forEach( reference => { | ||
const declaration = reference.declaration; | ||
strongDependencies = this.strongDependencies | ||
.filter( module => module !== this ); | ||
if ( declaration && declaration.statement ) { | ||
const module = declaration.statement.module; | ||
if ( module === this ) return; | ||
// TODO disregard function declarations | ||
if ( reference.isImmediatelyUsed ) { | ||
strongDependencies[ module.id ] = module; | ||
} | ||
} | ||
}); | ||
}); | ||
return { strongDependencies, weakDependencies }; | ||
@@ -416,12 +290,2 @@ } | ||
markAllSideEffects () { | ||
let hasSideEffect = false; | ||
this.statements.forEach( statement => { | ||
if ( statement.markSideEffect() ) hasSideEffect = true; | ||
}); | ||
return hasSideEffect; | ||
} | ||
namespace () { | ||
@@ -457,2 +321,13 @@ if ( !this.declarations['*'] ) { | ||
enter: node => { | ||
// eliminate dead branches early | ||
if ( node.type === 'IfStatement' ) { | ||
if ( isFalsy( node.test ) ) { | ||
this.magicString.overwrite( node.consequent.start, node.consequent.end, '{}' ); | ||
node.consequent = emptyBlockStatement( node.consequent.start, node.consequent.end ); | ||
} else if ( node.alternate && isTruthy( node.test ) ) { | ||
this.magicString.overwrite( node.alternate.start, node.alternate.end, '{}' ); | ||
node.alternate = emptyBlockStatement( node.alternate.start, node.alternate.end ); | ||
} | ||
} | ||
this.magicString.addSourcemapLocation( node.start ); | ||
@@ -696,2 +571,12 @@ this.magicString.addSourcemapLocation( node.end ); | ||
run () { | ||
let marked = false; | ||
this.statements.forEach( statement => { | ||
marked = marked || statement.run( this.strongDependencies ); | ||
}); | ||
return marked; | ||
} | ||
trace ( name ) { | ||
@@ -720,3 +605,12 @@ if ( name in this.declarations ) return this.declarations[ name ]; | ||
if ( reexportDeclaration ) { | ||
return reexportDeclaration.module.traceExport( reexportDeclaration.localName ); | ||
const declaration = reexportDeclaration.module.traceExport( reexportDeclaration.localName ); | ||
if ( !declaration ) { | ||
const err = new Error( `'${reexportDeclaration.localName}' is not exported by '${reexportDeclaration.module.id}' (imported by '${this.id}')` ); | ||
err.file = this.id; | ||
err.loc = getLocation( this.code, reexportDeclaration.start ); | ||
throw err; | ||
} | ||
return declaration; | ||
} | ||
@@ -723,0 +617,0 @@ |
@@ -12,7 +12,7 @@ import Promise from 'es6-promise/lib/es6-promise/promise.js'; | ||
if ( !options || !options.entry ) { | ||
throw new Error( 'You must supply options.entry to rollup' ); | ||
return Promise.reject( new Error( 'You must supply options.entry to rollup' ) ); | ||
} | ||
if ( options.transform || options.load || options.resolveId || options.resolveExternal ) { | ||
throw new Error( 'The `transform`, `load`, `resolveId` and `resolveExternal` options are deprecated in favour of a unified plugin API. See https://github.com/rollup/rollup/wiki/Plugins for details' ); | ||
return Promise.reject( new Error( 'The `transform`, `load`, `resolveId` and `resolveExternal` options are deprecated in favour of a unified plugin API. See https://github.com/rollup/rollup/wiki/Plugins for details' ) ); | ||
} | ||
@@ -19,0 +19,0 @@ |
import { walk } from 'estree-walker'; | ||
import Scope from './ast/Scope.js'; | ||
import attachScopes from './ast/attachScopes.js'; | ||
import modifierNodes from './ast/modifierNodes.js'; | ||
import isFunctionDeclaration from './ast/isFunctionDeclaration.js'; | ||
import isReference from './ast/isReference.js'; | ||
import getLocation from './utils/getLocation.js'; | ||
import run from './utils/run.js'; | ||
const modifierNodes = { | ||
AssignmentExpression: 'left', | ||
UpdateExpression: 'argument' | ||
}; | ||
function isIife ( node, parent ) { | ||
return parent && parent.type === 'CallExpression' && node === parent.callee; | ||
} | ||
function isReference ( node, parent ) { | ||
if ( node.type === 'MemberExpression' ) { | ||
return !node.computed && isReference( node.object, node ); | ||
} | ||
if ( node.type === 'Identifier' ) { | ||
// TODO is this right? | ||
if ( parent.type === 'MemberExpression' ) return parent.computed || node === parent.object; | ||
// disregard the `bar` in { bar: foo } | ||
if ( parent.type === 'Property' && node !== parent.value ) return false; | ||
// disregard the `bar` in `class Foo { bar () {...} }` | ||
if ( parent.type === 'MethodDefinition' ) return false; | ||
// disregard the `bar` in `export { foo as bar }` | ||
if ( parent.type === 'ExportSpecifier' && node !== parent.local ) return; | ||
return true; | ||
} | ||
} | ||
class Reference { | ||
constructor ( node, scope ) { | ||
constructor ( node, scope, statement ) { | ||
this.node = node; | ||
this.scope = scope; | ||
this.statement = statement; | ||
@@ -74,2 +48,3 @@ this.declaration = null; // bound later | ||
this.isIncluded = false; | ||
this.ran = false; | ||
@@ -79,5 +54,8 @@ this.isImportDeclaration = node.type === 'ImportDeclaration'; | ||
this.isReexportDeclaration = this.isExportDeclaration && !!node.source; | ||
this.isFunctionDeclaration = isFunctionDeclaration( node ) || | ||
this.isExportDeclaration && isFunctionDeclaration( node.declaration ); | ||
} | ||
analyse () { | ||
firstPass () { | ||
if ( this.isImportDeclaration ) return; // nothing to analyse | ||
@@ -95,2 +73,3 @@ | ||
// find references | ||
const statement = this; | ||
let { module, references, scope, stringLiteralRanges } = this; | ||
@@ -115,3 +94,3 @@ let readDepth = 0; | ||
if ( node._scope ) scope = node._scope; | ||
if ( /Function/.test( node.type ) && !isIife( node, parent ) ) readDepth += 1; | ||
if ( /Function/.test( node.type ) ) readDepth += 1; | ||
@@ -163,14 +142,13 @@ // special case – shorthand properties. because node.key === node.value, | ||
const reference = new Reference( node, referenceScope ); | ||
const reference = new Reference( node, referenceScope, statement ); | ||
reference.isReassignment = isReassignment; | ||
references.push( reference ); | ||
reference.isImmediatelyUsed = !readDepth; | ||
reference.isReassignment = isReassignment; | ||
this.skip(); // don't descend from `foo.bar.baz` into `foo.bar` | ||
} | ||
}, | ||
leave ( node, parent ) { | ||
leave ( node ) { | ||
if ( node._scope ) scope = scope.parent; | ||
if ( /Function/.test( node.type ) && !isIife( node, parent ) ) readDepth -= 1; | ||
if ( /Function/.test( node.type ) ) readDepth -= 1; | ||
} | ||
@@ -189,35 +167,10 @@ }); | ||
markSideEffect () { | ||
if ( this.isIncluded ) return; | ||
run ( strongDependencies ) { | ||
if ( ( this.ran && this.isIncluded ) || this.isImportDeclaration || this.isFunctionDeclaration ) return; | ||
this.ran = true; | ||
const statement = this; | ||
let hasSideEffect = false; | ||
walk( this.node, { | ||
enter ( node, parent ) { | ||
if ( /Function/.test( node.type ) && !isIife( node, parent ) ) return this.skip(); | ||
// If this is a top-level call expression, or an assignment to a global, | ||
// this statement will need to be marked | ||
if ( node.type === 'CallExpression' || node.type === 'NewExpression' ) { | ||
hasSideEffect = true; | ||
} | ||
else if ( node.type in modifierNodes ) { | ||
let subject = node[ modifierNodes[ node.type ] ]; | ||
while ( subject.type === 'MemberExpression' ) subject = subject.object; | ||
const declaration = statement.module.trace( subject.name ); | ||
if ( !declaration || declaration.isExternal || declaration.statement.isIncluded ) { | ||
hasSideEffect = true; | ||
} | ||
} | ||
if ( hasSideEffect ) this.skip(); | ||
} | ||
}); | ||
if ( hasSideEffect ) statement.mark(); | ||
return hasSideEffect; | ||
if ( run( this.node, this.scope, this, strongDependencies ) ) { | ||
this.mark(); | ||
return true; | ||
} | ||
} | ||
@@ -224,0 +177,0 @@ |
import { isFile, readFileSync } from './fs.js'; | ||
import { dirname, extname, isAbsolute, resolve } from './path.js'; | ||
import { dirname, isAbsolute, resolve } from './path.js'; | ||
@@ -31,3 +31,3 @@ export function load ( id ) { | ||
export function onwarn ( msg ) { | ||
console.error( msg ); | ||
console.error( msg ); //eslint-disable-line no-console | ||
} |
@@ -12,3 +12,3 @@ import Promise from 'es6-promise/lib/es6-promise/promise.js'; | ||
}, Promise.resolve() ); | ||
} | ||
}; | ||
} |
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
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
1446886
53
14049
0