Comparing version 0.6.29 to 0.6.30
# changelog | ||
## 0.6.30 | ||
* Mutually dependent modules within a bundle are re-ordered based on whether one is referenced at the top level of the other ([#152](https://github.com/esperantojs/esperanto/issues/152)) | ||
## 0.6.29 | ||
@@ -4,0 +8,0 @@ |
{ | ||
"name": "esperanto", | ||
"description": "An easier way to convert ES6 modules to AMD and CommonJS", | ||
"version": "0.6.29", | ||
"version": "0.6.30", | ||
"author": "Rich Harris", | ||
@@ -6,0 +6,0 @@ "repository": "https://github.com/esperantojs/esperanto", |
@@ -31,3 +31,3 @@ import path from 'path'; | ||
let entryModule = moduleLookup[ entry ]; | ||
modules = sortModules( entryModule, moduleLookup ); // TODO is this necessary? surely it's already sorted because of the fetch order? or do we need to prevent parallel reads? | ||
modules = sortModules( entryModule, moduleLookup ); | ||
@@ -34,0 +34,0 @@ let bundle = { |
import hasOwnProp from 'utils/hasOwnProp'; | ||
import walk from 'utils/ast/walk'; | ||
export default function sortModules ( entry, moduleLookup ) { | ||
var seen = {}, | ||
ordered = []; | ||
let seen = {}; | ||
let ordered = []; | ||
let swapPairs = []; | ||
function visit ( mod ) { | ||
// ignore external modules, and modules we've | ||
// already included | ||
if ( !mod || hasOwnProp.call( seen, mod.id ) ) { | ||
return; | ||
} | ||
seen[ mod.id ] = true; | ||
mod.imports.forEach( x => { | ||
visit( moduleLookup[ x.id ] ); | ||
const imported = moduleLookup[ x.id ]; | ||
if ( !imported ) return; | ||
// ignore modules we've already included | ||
if ( hasOwnProp.call( seen, imported.id ) ) { | ||
if ( shouldSwap( imported, mod ) ) { | ||
swapPairs.push([ imported, mod ]); | ||
} | ||
return; | ||
} | ||
visit( imported ); | ||
}); | ||
@@ -25,3 +34,60 @@ | ||
swapPairs.forEach( ([ a, b ]) => { | ||
const aIndex = ordered.indexOf( a ); | ||
const bIndex = ordered.indexOf( b ); | ||
ordered[ aIndex ] = b; | ||
ordered[ bIndex ] = a; | ||
}); | ||
return ordered; | ||
} | ||
function shouldSwap ( a, b ) { | ||
// if these modules don't import each other, abort | ||
if ( !( imports( a, b ) && imports( b, a ) ) ) return; | ||
return usesAtTopLevel( b, a ) && !usesAtTopLevel( a, b ); | ||
} | ||
function imports ( a, b ) { | ||
let i = a.imports.length; | ||
while ( i-- ) { | ||
if ( a.imports[i].id === b.id ) { | ||
return true; | ||
} | ||
} | ||
} | ||
function usesAtTopLevel ( a, b ) { | ||
let bindings = []; | ||
// find out which bindings a imports from b | ||
let i = a.imports.length; | ||
while ( i-- ) { | ||
if ( a.imports[i].id === b.id ) { | ||
bindings.push.apply( bindings, a.imports[i].specifiers.map( x => x.as ) ); | ||
} | ||
} | ||
// see if any of those bindings are referenced at the top level | ||
let referencedAtTopLevel = false; | ||
walk( a.ast, { | ||
enter ( node ) { | ||
if ( referencedAtTopLevel ) { | ||
return this.skip(); | ||
} | ||
if ( /^Import/.test( node.type ) || ( node._scope && node._scope.parent ) ) { | ||
return this.skip(); | ||
} | ||
if ( node.type === 'Identifier' && ~bindings.indexOf( node.name ) ) { | ||
referencedAtTopLevel = true; | ||
} | ||
} | ||
}); | ||
return referencedAtTopLevel; | ||
} |
@@ -81,4 +81,2 @@ /** | ||
specifiers: node.specifiers.map( s => { | ||
var id; | ||
if ( s.type === 'ImportNamespaceSpecifier' ) { | ||
@@ -97,3 +95,3 @@ return { | ||
as: s.local.name | ||
} | ||
}; | ||
} | ||
@@ -100,0 +98,0 @@ |
@@ -38,3 +38,3 @@ import hasOwnProp from 'utils/hasOwnProp'; | ||
if ( !node.prefix ) { | ||
suffix += `, ${name} ${node.operator === '++' ? '-' : '+'} 1` | ||
suffix += `, ${name} ${node.operator === '++' ? '-' : '+'} 1`; | ||
} | ||
@@ -41,0 +41,0 @@ prefix += `( `; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
238232
6795