rollup
Advanced tools
Comparing version 0.9.1 to 0.10.0
# rollup changelog | ||
## 0.10.0 | ||
* Better sorting algorithm – sorting happens at the module level, rather than the statement level. This avoids certain edge cases | ||
* IIFEs are ignored for the purposes of distinguishing between 'strong' and 'weak' dependencies | ||
* Empty `var` declarations for exported bindings are omitted | ||
## 0.9.1 | ||
@@ -4,0 +10,0 @@ |
@@ -263,2 +263,11 @@ 'use strict'; | ||
function isEmptyExportedVarDeclaration(node, module, allBundleExports) { | ||
if (node.type !== 'VariableDeclaration' || node.declarations[0].init) return false; | ||
var name = node.declarations[0].id.name; | ||
var canonicalName = module.getCanonicalName(name); | ||
return canonicalName in allBundleExports; | ||
} | ||
var blacklisted = blank(); | ||
@@ -476,2 +485,6 @@ | ||
function isIife(node, parent) { | ||
return parent && parent.type === 'CallExpression' && node === parent.callee; | ||
} | ||
var blockDeclarations = { | ||
@@ -589,3 +602,3 @@ 'const': true, | ||
walk(this.node, { | ||
enter: function (node) { | ||
enter: function (node, parent) { | ||
var newScope = undefined; | ||
@@ -618,6 +631,8 @@ | ||
case 'BlockStatement': | ||
newScope = new Scope({ | ||
parent: scope, | ||
block: true | ||
}); | ||
if (!/Function/.test(parent.type)) { | ||
newScope = new Scope({ | ||
parent: scope, | ||
block: true | ||
}); | ||
} | ||
@@ -658,12 +673,31 @@ break; | ||
// This allows us to track whether we're looking at code that will | ||
// be executed immediately (either outside a function, or immediately | ||
// inside an IIFE), for the purposes of determining whether dependencies | ||
// are strong or weak. It's not bulletproof, since it wouldn't catch... | ||
// | ||
// var calledImmediately = function () { | ||
// doSomethingWith( strongDependency ); | ||
// } | ||
// calledImmediately(); | ||
// | ||
// ...but it's better than nothing | ||
var depth = 0; | ||
if (!this.isImportDeclaration) { | ||
walk(this.node, { | ||
enter: function (node, parent) { | ||
if (node._scope) scope = node._scope; | ||
if (node._scope) { | ||
if (!scope.isBlockScope && !isIife(node, parent)) depth += 1; | ||
scope = node._scope; | ||
} | ||
_this.checkForReads(scope, node, parent); | ||
_this.checkForReads(scope, node, parent, !depth); | ||
_this.checkForWrites(scope, node); | ||
}, | ||
leave: function (node) { | ||
if (node._scope) scope = scope.parent; | ||
leave: function (node, parent) { | ||
if (node._scope) { | ||
if (!scope.isBlockScope && !isIife(node, parent)) depth -= 1; | ||
scope = scope.parent; | ||
} | ||
} | ||
@@ -678,3 +712,3 @@ }); | ||
Statement.prototype.checkForReads = function checkForReads(scope, node, parent) { | ||
Statement.prototype.checkForReads = function checkForReads(scope, node, parent, strong) { | ||
if (node.type === 'Identifier') { | ||
@@ -698,4 +732,3 @@ // disregard the `bar` in `foo.bar` - these appear as Identifier nodes | ||
this.dependsOn[node.name] = true; | ||
if (!scope.parent) this.stronglyDependsOn[node.name] = true; | ||
if (strong) this.stronglyDependsOn[node.name] = true; | ||
} | ||
@@ -1167,2 +1200,41 @@ } | ||
Module.prototype.consolidateDependencies = function consolidateDependencies() { | ||
var _this3 = this; | ||
var strongDependencies = blank(); | ||
this.statements.forEach(function (statement) { | ||
if (statement.isImportDeclaration && !statement.node.specifiers.length) { | ||
// include module for its side-effects | ||
strongDependencies[statement.module.id] = statement.module; // TODO is this right? `statement.module` should be `this`, surely? | ||
} | ||
keys(statement.stronglyDependsOn).forEach(function (name) { | ||
if (statement.defines[name]) return; | ||
var importDeclaration = _this3.imports[name]; | ||
if (importDeclaration && importDeclaration.module && !importDeclaration.module.isExternal) { | ||
strongDependencies[importDeclaration.module.id] = importDeclaration.module; | ||
} | ||
}); | ||
}); | ||
var weakDependencies = blank(); | ||
this.statements.forEach(function (statement) { | ||
keys(statement.dependsOn).forEach(function (name) { | ||
if (statement.defines[name]) return; | ||
var importDeclaration = _this3.imports[name]; | ||
if (importDeclaration && importDeclaration.module && !importDeclaration.module.isExternal) { | ||
weakDependencies[importDeclaration.module.id] = importDeclaration.module; | ||
} | ||
}); | ||
}); | ||
return { strongDependencies: strongDependencies, weakDependencies: weakDependencies }; | ||
}; | ||
Module.prototype.findDeclaration = function findDeclaration(localName) { | ||
@@ -1236,3 +1308,3 @@ var importDeclaration = this.imports[localName]; | ||
Module.prototype.define = function define(name) { | ||
var _this3 = this; | ||
var _this4 = this; | ||
@@ -1249,5 +1321,5 @@ // shortcut cycles. TODO this won't work everywhere... | ||
(function () { | ||
var importDeclaration = _this3.imports[name]; | ||
var importDeclaration = _this4.imports[name]; | ||
promise = _this3.bundle.fetchModule(importDeclaration.source, _this3.id).then(function (module) { | ||
promise = _this4.bundle.fetchModule(importDeclaration.source, _this4.id).then(function (module) { | ||
importDeclaration.module = module; | ||
@@ -1259,3 +1331,3 @@ | ||
var localName = importDeclaration.localName; | ||
var suggestion = _this3.suggestedNames[localName] || localName; | ||
var suggestion = _this4.suggestedNames[localName] || localName; | ||
@@ -1270,3 +1342,3 @@ // special case - the module has its own import by this name | ||
var localName = importDeclaration.localName; | ||
var suggestion = _this3.suggestedNames[localName] || localName; | ||
var suggestion = _this4.suggestedNames[localName] || localName; | ||
module.suggestName('*', suggestion); | ||
@@ -1289,4 +1361,4 @@ module.suggestName('default', '' + suggestion + '__default'); | ||
// we need to create an internal namespace | ||
if (! ~_this3.bundle.internalNamespaceModules.indexOf(module)) { | ||
_this3.bundle.internalNamespaceModules.push(module); | ||
if (! ~_this4.bundle.internalNamespaceModules.indexOf(module)) { | ||
_this4.bundle.internalNamespaceModules.push(module); | ||
} | ||
@@ -1300,3 +1372,3 @@ | ||
if (!exportDeclaration) { | ||
throw new Error('Module ' + module.id + ' does not export ' + importDeclaration.name + ' (imported by ' + _this3.id + ')'); | ||
throw new Error('Module ' + module.id + ' does not export ' + importDeclaration.name + ' (imported by ' + _this4.id + ')'); | ||
} | ||
@@ -1318,3 +1390,3 @@ | ||
statement = name === 'default' ? _this3.exports.default.statement : _this3.definitions[name]; | ||
statement = name === 'default' ? _this4.exports.default.statement : _this4.definitions[name]; | ||
promise = statement && !statement.isIncluded ? statement.expand() : emptyArrayPromise; | ||
@@ -1327,5 +1399,5 @@ | ||
// `foo` will include statements *after* the declaration | ||
if (name === 'default' && _this3.exports.default.identifier && _this3.exports.default.isModified) { | ||
if (name === 'default' && _this4.exports.default.identifier && _this4.exports.default.isModified) { | ||
(function () { | ||
var defaultExportStatement = _this3.exports.default.statement; | ||
var defaultExportStatement = _this4.exports.default.statement; | ||
promise = promise.then(function (statements) { | ||
@@ -1340,3 +1412,3 @@ // remove the default export statement... | ||
while (i--) { | ||
if (statements[i].module === _this3 && statements[i].index < defaultExportStatement.index) { | ||
if (statements[i].module === _this4 && statements[i].index < defaultExportStatement.index) { | ||
statements.splice(i + 1, 0, defaultExportStatement); | ||
@@ -1361,3 +1433,3 @@ inserted = true; | ||
Module.prototype.expandAllStatements = function expandAllStatements(isEntryModule) { | ||
var _this4 = this; | ||
var _this5 = this; | ||
@@ -1385,3 +1457,3 @@ var allStatements = []; | ||
if (!statement.node.specifiers.length) { | ||
return _this4.bundle.fetchModule(statement.node.source.value, _this4.id).then(function (module) { | ||
return _this5.bundle.fetchModule(statement.node.source.value, _this5.id).then(function (module) { | ||
statement.module = module; | ||
@@ -1581,2 +1653,4 @@ return module.expandAllStatements(); | ||
this.modulePromises = blank(); | ||
this.modules = []; | ||
this.statements = []; | ||
@@ -1611,2 +1685,4 @@ this.externalModules = []; | ||
_this.modules.push(module); | ||
return module; | ||
@@ -1663,3 +1739,4 @@ }); | ||
_this2.deconflict(); | ||
_this2.sort(); | ||
_this2.orderedStatements = _this2.sort(); | ||
}); | ||
@@ -1756,93 +1833,94 @@ }; | ||
Bundle.prototype.sort = function sort() { | ||
// TODO avoid this work whenever possible... | ||
var seen = {}; | ||
var ordered = []; | ||
var hasCycles = undefined; | ||
var definitions = blank(); | ||
var strongDeps = {}; | ||
var stronglyDependsOn = {}; | ||
// gather definitions | ||
this.statements.forEach(function (statement) { | ||
keys(statement.defines).forEach(function (name) { | ||
var canonicalName = statement.module.getCanonicalName(name); | ||
definitions[canonicalName] = statement; | ||
}); | ||
}); | ||
function visit(module) { | ||
seen[module.id] = true; | ||
var strongDeps = blank(); | ||
var stronglyDependsOn = blank(); | ||
var _module$consolidateDependencies = module.consolidateDependencies(); | ||
this.statements.forEach(function (statement) { | ||
var id = statement.id; | ||
strongDeps[id] = []; | ||
stronglyDependsOn[id] = {}; | ||
var strongDependencies = _module$consolidateDependencies.strongDependencies; | ||
var weakDependencies = _module$consolidateDependencies.weakDependencies; | ||
keys(statement.stronglyDependsOn).forEach(function (name) { | ||
if (statement.defines[name]) return; // TODO seriously... need to fix this | ||
var canonicalName = statement.module.getCanonicalName(name); | ||
var definition = definitions[canonicalName]; | ||
strongDeps[module.id] = []; | ||
stronglyDependsOn[module.id] = {}; | ||
if (definition) strongDeps[statement.id].push(definition); | ||
keys(strongDependencies).forEach(function (id) { | ||
var imported = strongDependencies[id]; | ||
strongDeps[module.id].push(imported); | ||
if (seen[id]) { | ||
// we need to prevent an infinite loop, and note that | ||
// we need to check for strong/weak dependency relationships | ||
hasCycles = true; | ||
return; | ||
} | ||
visit(imported); | ||
}); | ||
}); | ||
// add second (and third...) order strong dependencies | ||
this.statements.forEach(function (statement) { | ||
var id = statement.id; | ||
keys(weakDependencies).forEach(function (id) { | ||
var imported = weakDependencies[id]; | ||
if (seen[id]) { | ||
// we need to prevent an infinite loop, and note that | ||
// we need to check for strong/weak dependency relationships | ||
hasCycles = true; | ||
return; | ||
} | ||
visit(imported); | ||
}); | ||
// add second (and third...) order dependencies | ||
function addStrongDependencies(dependency) { | ||
if (stronglyDependsOn[id][dependency.id]) return; | ||
if (stronglyDependsOn[module.id][dependency.id]) return; | ||
stronglyDependsOn[id][dependency.id] = true; | ||
stronglyDependsOn[module.id][dependency.id] = true; | ||
strongDeps[dependency.id].forEach(addStrongDependencies); | ||
} | ||
strongDeps[id].forEach(addStrongDependencies); | ||
}); | ||
strongDeps[module.id].forEach(addStrongDependencies); | ||
// reinsert each statement, ensuring its strong dependencies appear first | ||
var sorted = []; | ||
var included = blank(); | ||
var highestIndex = blank(); | ||
ordered.push(module); | ||
} | ||
function include(statement) { | ||
if (included[statement.id]) return; | ||
included[statement.id] = true; | ||
visit(this.entryModule); | ||
var alreadyIncluded = false; | ||
if (hasCycles) { | ||
var unordered = ordered; | ||
ordered = []; | ||
var unordered = statement.index < highestIndex[statement.module.id]; | ||
highestIndex[statement.module.id] = Math.max(statement.index, highestIndex[statement.module.id] || 0); | ||
// unordered is actually semi-ordered, as [ fewer dependencies ... more dependencies ] | ||
unordered.forEach(function (module) { | ||
// ensure strong dependencies of `module` that don't strongly depend on `module` go first | ||
strongDeps[module.id].forEach(place); | ||
if (unordered) { | ||
var len = sorted.length; | ||
var i = 0; | ||
for (i = 0; i < len; i += 1) { | ||
// ensure that this statement appears above later statements | ||
// from the same module - in rare situations (#34) they can | ||
// become jumbled | ||
var existing = sorted[i]; | ||
if (existing.module === statement.module && existing.index > statement.index) { | ||
sorted.splice(i, 0, statement); | ||
return; | ||
function place(dep) { | ||
if (!stronglyDependsOn[dep.id][module.id] && ! ~ordered.indexOf(dep)) { | ||
strongDeps[dep.id].forEach(place); | ||
ordered.push(dep); | ||
} | ||
} | ||
} | ||
sorted.push(statement); | ||
if (! ~ordered.indexOf(module)) { | ||
ordered.push(module); | ||
} | ||
}); | ||
} | ||
this.statements.forEach(function (statement) { | ||
strongDeps[statement.id].forEach(includeStrongDependency); | ||
var statements = []; | ||
function includeStrongDependency(dependency) { | ||
if (!stronglyDependsOn[dependency.id][statement.id] && !included[dependency.id]) { | ||
strongDeps[dependency.id].forEach(includeStrongDependency); | ||
include(dependency); | ||
} | ||
} | ||
include(statement); | ||
ordered.forEach(function (module) { | ||
module.statements.forEach(function (statement) { | ||
if (statement.isIncluded) statements.push(statement); | ||
}); | ||
}); | ||
this.statements = sorted; | ||
return statements; | ||
}; | ||
@@ -1902,8 +1980,16 @@ | ||
this.statements.forEach(function (statement) { | ||
this.orderedStatements.forEach(function (statement) { | ||
// skip `export { foo, bar, baz }` | ||
if (statement.node.type === 'ExportNamedDeclaration' && statement.node.specifiers.length) { | ||
return; | ||
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)) 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)) return; | ||
var replacements = blank(); | ||
@@ -1910,0 +1996,0 @@ var bundleExports = blank(); |
{ | ||
"name": "rollup", | ||
"version": "0.9.1", | ||
"version": "0.10.0", | ||
"description": "Next-generation ES6 module bundler", | ||
@@ -5,0 +5,0 @@ "main": "dist/rollup.js", |
@@ -16,2 +16,11 @@ import { basename, dirname, extname, relative } from '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 { | ||
@@ -38,2 +47,4 @@ constructor ( options ) { | ||
this.modulePromises = blank(); | ||
this.modules = []; | ||
this.statements = []; | ||
@@ -68,2 +79,4 @@ this.externalModules = []; | ||
this.modules.push( module ); | ||
return module; | ||
@@ -116,3 +129,4 @@ }); | ||
this.deconflict(); | ||
this.sort(); | ||
this.orderedStatements = this.sort(); | ||
}); | ||
@@ -207,96 +221,91 @@ } | ||
sort () { | ||
// TODO avoid this work whenever possible... | ||
let seen = {}; | ||
let ordered = []; | ||
let hasCycles; | ||
let definitions = blank(); | ||
let strongDeps = {}; | ||
let stronglyDependsOn = {}; | ||
// gather definitions | ||
this.statements.forEach( statement => { | ||
keys( statement.defines ).forEach( name => { | ||
const canonicalName = statement.module.getCanonicalName( name ); | ||
definitions[ canonicalName ] = statement; | ||
}); | ||
}); | ||
function visit ( module ) { | ||
seen[ module.id ] = true; | ||
let strongDeps = blank(); | ||
let stronglyDependsOn = blank(); | ||
const { strongDependencies, weakDependencies } = module.consolidateDependencies(); | ||
this.statements.forEach( statement => { | ||
const id = statement.id; | ||
strongDeps[ id ] = []; | ||
stronglyDependsOn[ id ] = {}; | ||
strongDeps[ module.id ] = []; | ||
stronglyDependsOn[ module.id ] = {}; | ||
keys( statement.stronglyDependsOn ).forEach( name => { | ||
if ( statement.defines[ name ] ) return; // TODO seriously... need to fix this | ||
const canonicalName = statement.module.getCanonicalName( name ); | ||
const definition = definitions[ canonicalName ]; | ||
keys( strongDependencies ).forEach( id => { | ||
const imported = strongDependencies[ id ]; | ||
if ( definition ) strongDeps[ statement.id ].push( definition ); | ||
strongDeps[ module.id ].push( imported ); | ||
if ( seen[ id ] ) { | ||
// we need to prevent an infinite loop, and note that | ||
// we need to check for strong/weak dependency relationships | ||
hasCycles = true; | ||
return; | ||
} | ||
visit( imported ); | ||
}); | ||
}); | ||
// add second (and third...) order strong dependencies | ||
this.statements.forEach( statement => { | ||
const id = statement.id; | ||
keys( weakDependencies ).forEach( id => { | ||
const imported = weakDependencies[ id ]; | ||
if ( seen[ id ] ) { | ||
// we need to prevent an infinite loop, and note that | ||
// we need to check for strong/weak dependency relationships | ||
hasCycles = true; | ||
return; | ||
} | ||
visit( imported ); | ||
}); | ||
// add second (and third...) order dependencies | ||
function addStrongDependencies ( dependency ) { | ||
if ( stronglyDependsOn[ id ][ dependency.id ] ) return; | ||
if ( stronglyDependsOn[ module.id ][ dependency.id ] ) return; | ||
stronglyDependsOn[ id ][ dependency.id ] = true; | ||
stronglyDependsOn[ module.id ][ dependency.id ] = true; | ||
strongDeps[ dependency.id ].forEach( addStrongDependencies ); | ||
} | ||
strongDeps[ id ].forEach( addStrongDependencies ); | ||
}); | ||
strongDeps[ module.id ].forEach( addStrongDependencies ); | ||
// reinsert each statement, ensuring its strong dependencies appear first | ||
let sorted = []; | ||
let included = blank(); | ||
let highestIndex = blank(); | ||
ordered.push( module ); | ||
} | ||
function include ( statement ) { | ||
if ( included[ statement.id ] ) return; | ||
included[ statement.id ] = true; | ||
visit( this.entryModule ); | ||
let alreadyIncluded = false; | ||
if ( hasCycles ) { | ||
let unordered = ordered; | ||
ordered = []; | ||
const unordered = statement.index < highestIndex[ statement.module.id ]; | ||
highestIndex[ statement.module.id ] = Math.max( | ||
statement.index, | ||
highestIndex[ statement.module.id ] || 0 | ||
); | ||
// unordered is actually semi-ordered, as [ fewer dependencies ... more dependencies ] | ||
unordered.forEach( module => { | ||
// ensure strong dependencies of `module` that don't strongly depend on `module` go first | ||
strongDeps[ module.id ].forEach( place ); | ||
if ( unordered ) { | ||
const len = sorted.length; | ||
let i = 0; | ||
for ( i = 0; i < len; i += 1 ) { | ||
// ensure that this statement appears above later statements | ||
// from the same module - in rare situations (#34) they can | ||
// become jumbled | ||
const existing = sorted[i]; | ||
if ( existing.module === statement.module && existing.index > statement.index ) { | ||
sorted.splice( i, 0, statement ); | ||
return | ||
function place ( dep ) { | ||
if ( !stronglyDependsOn[ dep.id ][ module.id ] && !~ordered.indexOf( dep ) ) { | ||
strongDeps[ dep.id ].forEach( place ); | ||
ordered.push( dep ); | ||
} | ||
} | ||
} | ||
sorted.push( statement ); | ||
if ( !~ordered.indexOf( module ) ) { | ||
ordered.push( module ); | ||
} | ||
}); | ||
} | ||
this.statements.forEach( statement => { | ||
strongDeps[ statement.id ].forEach( includeStrongDependency ); | ||
let statements = []; | ||
function includeStrongDependency ( dependency ) { | ||
if ( !stronglyDependsOn[ dependency.id ][ statement.id ] && !included[ dependency.id ] ) { | ||
strongDeps[ dependency.id ].forEach( includeStrongDependency ); | ||
include( dependency ); | ||
} | ||
} | ||
include( statement ); | ||
ordered.forEach( module => { | ||
module.statements.forEach( statement => { | ||
if ( statement.isIncluded ) statements.push( statement ); | ||
}); | ||
}); | ||
this.statements = sorted; | ||
return statements; | ||
} | ||
@@ -351,8 +360,16 @@ | ||
this.statements.forEach( statement => { | ||
this.orderedStatements.forEach( statement => { | ||
// skip `export { foo, bar, baz }` | ||
if ( statement.node.type === 'ExportNamedDeclaration' && statement.node.specifiers.length ) { | ||
return; | ||
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 ) ) 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 ) ) return; | ||
let replacements = blank(); | ||
@@ -359,0 +376,0 @@ let bundleExports = blank(); |
@@ -237,2 +237,39 @@ import { dirname } from 'path'; | ||
consolidateDependencies () { | ||
let strongDependencies = blank(); | ||
this.statements.forEach( statement => { | ||
if ( statement.isImportDeclaration && !statement.node.specifiers.length ) { | ||
// include module for its side-effects | ||
strongDependencies[ statement.module.id ] = statement.module; // TODO is this right? `statement.module` should be `this`, surely? | ||
} | ||
keys( statement.stronglyDependsOn ).forEach( name => { | ||
if ( statement.defines[ name ] ) return; | ||
const importDeclaration = this.imports[ name ]; | ||
if ( importDeclaration && importDeclaration.module && !importDeclaration.module.isExternal ) { | ||
strongDependencies[ importDeclaration.module.id ] = importDeclaration.module; | ||
} | ||
}); | ||
}); | ||
let weakDependencies = blank(); | ||
this.statements.forEach( statement => { | ||
keys( statement.dependsOn ).forEach( name => { | ||
if ( statement.defines[ name ] ) return; | ||
const importDeclaration = this.imports[ name ]; | ||
if ( importDeclaration && importDeclaration.module && !importDeclaration.module.isExternal ) { | ||
weakDependencies[ importDeclaration.module.id ] = importDeclaration.module; | ||
} | ||
}); | ||
}); | ||
return { strongDependencies, weakDependencies }; | ||
} | ||
findDeclaration ( localName ) { | ||
@@ -239,0 +276,0 @@ const importDeclaration = this.imports[ localName ]; |
@@ -7,2 +7,6 @@ import { blank, keys } from './utils/object'; | ||
function isIife ( node, parent ) { | ||
return parent && parent.type === 'CallExpression' && node === parent.callee; | ||
} | ||
export default class Statement { | ||
@@ -42,3 +46,3 @@ constructor ( node, magicString, module, index ) { | ||
walk( this.node, { | ||
enter ( node ) { | ||
enter ( node, parent ) { | ||
let newScope; | ||
@@ -71,6 +75,8 @@ | ||
case 'BlockStatement': | ||
newScope = new Scope({ | ||
parent: scope, | ||
block: true | ||
}); | ||
if ( !/Function/.test( parent.type ) ) { | ||
newScope = new Scope({ | ||
parent: scope, | ||
block: true | ||
}); | ||
} | ||
@@ -111,12 +117,31 @@ break; | ||
// This allows us to track whether we're looking at code that will | ||
// be executed immediately (either outside a function, or immediately | ||
// inside an IIFE), for the purposes of determining whether dependencies | ||
// are strong or weak. It's not bulletproof, since it wouldn't catch... | ||
// | ||
// var calledImmediately = function () { | ||
// doSomethingWith( strongDependency ); | ||
// } | ||
// calledImmediately(); | ||
// | ||
// ...but it's better than nothing | ||
let depth = 0; | ||
if ( !this.isImportDeclaration ) { | ||
walk( this.node, { | ||
enter: ( node, parent ) => { | ||
if ( node._scope ) scope = node._scope; | ||
if ( node._scope ) { | ||
if ( !scope.isBlockScope && !isIife( node, parent ) ) depth += 1; | ||
scope = node._scope; | ||
} | ||
this.checkForReads( scope, node, parent ); | ||
this.checkForReads( scope, node, parent, !depth ); | ||
this.checkForWrites( scope, node ); | ||
}, | ||
leave: ( node ) => { | ||
if ( node._scope ) scope = scope.parent; | ||
leave: ( node, parent ) => { | ||
if ( node._scope ) { | ||
if ( !scope.isBlockScope && !isIife( node, parent ) ) depth -= 1; | ||
scope = scope.parent; | ||
} | ||
} | ||
@@ -131,3 +156,3 @@ }); | ||
checkForReads ( scope, node, parent ) { | ||
checkForReads ( scope, node, parent, strong ) { | ||
if ( node.type === 'Identifier' ) { | ||
@@ -151,4 +176,3 @@ // disregard the `bar` in `foo.bar` - these appear as Identifier nodes | ||
this.dependsOn[ node.name ] = true; | ||
if ( !scope.parent ) this.stronglyDependsOn[ node.name ] = true; | ||
if ( strong ) this.stronglyDependsOn[ node.name ] = true; | ||
} | ||
@@ -155,0 +179,0 @@ } |
141363
3461