diff --git a/.babelrc b/.babelrc
index 6ce65b2..ed25dc0 100644
--- a/.babelrc
+++ b/.babelrc
@@ -5,6 +5,7 @@
"es6.classes",
"es6.constants",
"es6.destructuring",
+ "es6.modules",
"es6.parameters",
"es6.properties.shorthand",
"es6.spread",
diff --git a/.eslintrc b/.eslintrc
index 11f6d54..6e5fb1a 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,15 +1,19 @@
{
"rules": {
- "indent": [ 2, "tab", { "SwitchCase": 1}],
+ "indent": [ 2, "tab", { "SwitchCase": 1 } ],
"quotes": [ 2, "single" ],
"linebreak-style": [ 2, "unix" ],
"semi": [ 2, "always" ],
+ "space-after-keywords": [ 2, "always" ],
+ "space-before-blocks": [ 2, "always" ],
+ "space-before-function-paren": [ 2, "always" ],
"no-mixed-spaces-and-tabs": [ 2, "smart-tabs" ],
"no-cond-assign": [ 0 ]
},
"env": {
"es6": true,
"browser": true,
+ "mocha": true,
"node": true
},
"extends": "eslint:recommended",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8cfd6d3..937c2e3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,24 @@
# rollup changelog
+## 0.16.1
+
+* Handle assignment patterns, and destructured/rest parameters, when analysing scopes
+* Fix bug preventing project from self-building (make base `Identifier` class markable)
+
+## 0.16.0
+
+* Internal refactoring ([#99](https://github.com/rollup/rollup/pull/99))
+* Optimisation for statically-analysable namespace imports ([#99](https://github.com/rollup/rollup/pull/99))
+* Windows support (theoretically!) ([#117](https://github.com/rollup/rollup/pull/117) / [#119](https://github.com/rollup/rollup/pull/119))
+
+## 0.15.0
+
+* Load all modules specified by `import` statements, and do tree-shaking synchronously once loading is complete. This results in simpler and faster code, and enables future improvements ([#97](https://github.com/rollup/rollup/pull/97))
+* Only rewrite `foo` as `exports.foo` when it makes sense to ([#92](https://github.com/rollup/rollup/issues/92))
+* Fix bug with shadowed variables that are eventually exported ([#91](https://github.com/rollup/rollup/issues/91))
+* Exclude unused function declarations that happen to modify a used name ([#90](https://github.com/rollup/rollup/pull/90))
+* Simplify internal `Scope` model – scopes always attach to blocks, never function expressions/declarations
+
## 0.14.1
* `export { name } from './other'` does not create a local binding ([#16](https://github.com/rollup/rollup/issues/16))
diff --git a/README.md b/README.md
index ecc5c01..978fded 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,24 @@
# Rollup
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
> *I roll up, I roll up, I roll up, Shawty I roll up*
>
> *I roll up, I roll up, I roll up*
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..cd3fab4
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,33 @@
+# http://www.appveyor.com/docs/appveyor-yml
+
+version: "{build}"
+
+clone_depth: 10
+
+init:
+ - git config --global core.autocrlf false
+
+environment:
+ matrix:
+ # node.js
+ - nodejs_version: 0.10
+ - nodejs_version: 0.12
+ # io.js
+ - nodejs_version: 1
+
+install:
+ - ps: Install-Product node $env:nodejs_version
+ - npm install
+
+build: off
+
+test_script:
+ - node --version && npm --version
+ - npm test
+
+matrix:
+ fast_finish: false
+
+# cache:
+# - C:\Users\appveyor\AppData\Roaming\npm-cache -> package.json # npm cache
+# - node_modules -> package.json # local npm modules
diff --git a/package.json b/package.json
index b8c9890..991c290 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "rollup",
- "version": "0.14.1",
+ "version": "0.16.1",
"description": "Next-generation ES6 module bundler",
"main": "dist/rollup.js",
"jsnext:main": "src/rollup.js",
@@ -16,7 +16,7 @@
},
"repository": {
"type": "git",
- "url": "https://github.com/rich-harris/rollup"
+ "url": "https://github.com/rollup/rollup"
},
"keywords": [
"modules",
@@ -26,12 +26,16 @@
"optimizer"
],
"author": "Rich Harris",
+ "contributors": [
+ "Oskar Segersvärd "
+ ],
"license": "MIT",
"bugs": {
- "url": "https://github.com/rich-harris/rollup/issues"
+ "url": "https://github.com/rollup/rollup/issues"
},
- "homepage": "https://github.com/rich-harris/rollup",
+ "homepage": "https://github.com/rollup/rollup",
"devDependencies": {
+ "babel": "^5.8.21",
"babel-core": "^5.5.8",
"console-group": "^0.1.2",
"eslint": "^1.1.0",
@@ -40,15 +44,15 @@
"gobble-browserify": "^0.6.1",
"gobble-cli": "^0.4.2",
"gobble-esperanto-bundle": "^0.2.0",
- "gobble-rollup": "^0.7.0",
+ "gobble-rollup": "^0.8.0",
"gobble-rollup-babel": "^0.1.0",
"mocha": "^2.2.4",
- "source-map": "^0.1.40"
+ "source-map": "^0.4.4"
},
"dependencies": {
- "acorn": "^1.1.0",
+ "acorn": "^2.3.0",
"chalk": "^1.0.0",
- "magic-string": "^0.6.5",
+ "magic-string": "^0.7.0",
"minimist": "^1.1.1",
"sander": "^0.3.3",
"source-map-support": "^0.3.1"
diff --git a/src/Bundle.js b/src/Bundle.js
index 0fd7656..28347b1 100644
--- a/src/Bundle.js
+++ b/src/Bundle.js
@@ -1,17 +1,16 @@
-import { basename, extname } from './utils/path';
import { Promise } from 'sander';
import MagicString from 'magic-string';
import { blank, keys } from './utils/object';
import Module from './Module';
import ExternalModule from './ExternalModule';
import finalisers from './finalisers/index';
-import makeLegalIdentifier from './utils/makeLegalIdentifier';
import ensureArray from './utils/ensureArray';
import { defaultResolver, defaultExternalResolver } from './utils/resolveId';
import { defaultLoader } from './utils/load';
import getExportMode from './utils/getExportMode';
import getIndentString from './utils/getIndentString';
import { unixizePath } from './utils/normalizePlatform.js';
+import Scope from './Scope';
export default class Bundle {
constructor ( options ) {
@@ -30,246 +29,131 @@ export default class Bundle {
transform: ensureArray( options.transform )
};
+ // The global scope, and the bundle's internal scope.
+ this.globals = new Scope();
+ this.scope = new Scope( this.globals );
+
+ // Strictly speaking, these globals only apply to non-ES6, non-default-only bundles.
+ // However, the deconfliction logic is greatly simplified by being the same for all formats.
+ // * CommonJS needs `module` and `exports` ( and `require`? ) to be in scope.
+ // * SystemJS needs a reference to a function for its `exports`,
+ // and another one for any `module` it imports. These global names can be reused!
+ [ 'exports', 'module' ]
+ .forEach( name => {
+ this.globals.define( name );
+ this.scope.bind( name, this.globals.reference( name ) );
+ });
+
+ // Alias for entryModule.exports.
+ this.exports = null;
+
this.toExport = null;
- this.modulePromises = blank();
+ this.pending = blank();
+ this.moduleById = blank();
this.modules = [];
this.statements = null;
this.externalModules = [];
- this.internalNamespaceModules = [];
-
- this.assumedGlobals = blank();
- this.assumedGlobals.exports = true; // TODO strictly speaking, this only applies with non-ES6, non-default-only bundles
}
build () {
- return this.fetchModule( this.entry, undefined )
+ return Promise.resolve( this.resolveId( this.entry, undefined, this.resolveOptions ) )
+ .then( id => this.fetchModule( id ) )
.then( entryModule => {
- const defaultExport = entryModule.exports.default;
-
this.entryModule = entryModule;
+ this.exports = entryModule.exports;
- if ( defaultExport ) {
- entryModule.needsDefault = true;
+ entryModule.markAllStatements( true );
+ entryModule.markAllExports();
- // `export default function foo () {...}` -
- // use the declared name for the export
- if ( defaultExport.identifier ) {
- entryModule.suggestName( 'default', defaultExport.identifier );
- }
+ // Include all side-effects
+ this.modules.forEach( module => {
+ module.markAllSideEffects();
+ });
- // `export default a + b` - generate an export name
- // based on the id of the entry module
- else {
- let defaultExportName = makeLegalIdentifier( basename( this.entryModule.id ).slice( 0, -extname( this.entryModule.id ).length ) );
+ // Sort the modules.
+ this.orderedModules = this.sort();
- // deconflict
- let topLevelNames = [];
- entryModule.statements.forEach( statement => {
- keys( statement.defines ).forEach( name => topLevelNames.push( name ) );
- });
+ // As a last step, deconflict all identifier names, once.
+ this.scope.deconflict();
- while ( ~topLevelNames.indexOf( defaultExportName ) ) {
- defaultExportName = `_${defaultExportName}`;
- }
+ // Alias the default import to the external module named
+ // for external modules that don't need named imports.
+ this.externalModules.forEach( module => {
+ const externalDefault = module.exports.lookup( 'default' );
- entryModule.suggestName( 'default', defaultExportName );
+ if ( externalDefault && !( module.needsNamed || module.needsAll ) ) {
+ externalDefault.name = module.name;
}
- }
-
- return entryModule.markAllStatements( true );
- })
- .then( () => {
- return this.markAllModifierStatements();
- })
- .then( () => {
- this.orderedModules = this.sort();
+ });
});
}
- // TODO would be better to deconflict once, rather than per-render
- deconflict ( es6 ) {
- let usedNames = blank();
+ fetchModule ( id ) {
+ // short-circuit cycles
+ if ( this.pending[ id ] ) return null;
+ this.pending[ id ] = true;
- // ensure no conflicts with globals
- keys( this.assumedGlobals ).forEach( name => usedNames[ name ] = true );
+ return Promise.resolve( this.load( id, this.loadOptions ) )
+ .then( source => {
+ let ast;
- let allReplacements = blank();
-
- // Assign names to external modules
- this.externalModules.forEach( module => {
- // while we're here...
- allReplacements[ module.id ] = blank();
-
- // TODO is this necessary in the ES6 case?
- let name = makeLegalIdentifier( module.suggestedNames['*'] || module.suggestedNames.default || module.id );
- module.name = getSafeName( name );
- });
-
- // Discover conflicts (i.e. two statements in separate modules both define `foo`)
- let i = this.orderedModules.length;
- while ( i-- ) {
- const module = this.orderedModules[i];
-
- // while we're here...
- allReplacements[ module.id ] = blank();
-
- keys( module.definitions ).forEach( name => {
- const safeName = getSafeName( name );
- if ( safeName !== name ) {
- module.rename( name, safeName );
- allReplacements[ module.id ][ name ] = safeName;
+ if ( typeof source === 'object' ) {
+ ast = source.ast;
+ source = source.code;
}
- });
- }
- // Assign non-conflicting names to internal default/namespace export
- this.orderedModules.forEach( module => {
- if ( !module.needsDefault && !module.needsAll ) return;
-
- if ( module.needsAll ) {
- const namespaceName = getSafeName( module.suggestedNames[ '*' ] );
- module.replacements[ '*' ] = namespaceName;
- }
-
- if ( module.needsDefault || module.needsAll && module.exports.default ) {
- const defaultExport = module.exports.default;
-
- // only create a new name if either
- // a) it's an expression (`export default 42`) or
- // b) it's a name that is reassigned to (`export var a = 1; a = 2`)
- if ( defaultExport && defaultExport.identifier && !defaultExport.isModified ) return; // TODO encapsulate check for whether we need synthetic default name
-
- const defaultName = getSafeName( module.suggestedNames.default );
- module.replacements.default = defaultName;
- }
- });
+ const module = new Module({
+ id,
+ source,
+ ast,
+ bundle: this
+ });
- this.orderedModules.forEach( module => {
- keys( module.imports ).forEach( localName => {
- if ( !module.imports[ localName ].isUsed ) return;
+ this.modules.push( module );
+ this.moduleById[ id ] = module;
- const bundleName = this.trace( module, localName, es6 );
- if ( bundleName !== localName ) {
- allReplacements[ module.id ][ localName ] = bundleName;
- }
+ return this.fetchAllDependencies( module ).then( () => {
+ // Analyze the module once all its dependencies have been resolved.
+ // This means that any dependencies of a module has already been
+ // analysed when it's time for the module itself.
+ module.analyse();
+ return module;
+ });
});
- });
-
- function getSafeName ( name ) {
- while ( usedNames[ name ] ) {
- name = `_${name}`;
- }
-
- usedNames[ name ] = true;
- return name;
- }
-
- return allReplacements;
}
- fetchModule ( importee, importer ) {
- return Promise.resolve( this.resolveId( importee, importer, this.resolveOptions ) )
- .then( id => {
- if ( !id ) {
+ fetchAllDependencies ( module ) {
+ const promises = module.dependencies.map( source => {
+ return Promise.resolve( this.resolveId( source, module.id, this.resolveOptions ) )
+ .then( resolvedId => {
+ module.resolvedIds[ source ] = resolvedId || source;
+
// external module
- if ( !this.modulePromises[ importee ] ) {
- const module = new ExternalModule( importee );
- this.externalModules.push( module );
- this.modulePromises[ importee ] = Promise.resolve( module );
+ if ( !resolvedId ) {
+ if ( !this.moduleById[ source ] ) {
+ const module = new ExternalModule( { id: source, bundle: this } );
+ this.externalModules.push( module );
+ this.moduleById[ source ] = module;
+ }
}
- return this.modulePromises[ importee ];
- }
-
- if ( id === importer ) {
- throw new Error( `A module cannot import itself (${id})` );
- }
-
- if ( !this.modulePromises[ id ] ) {
- this.modulePromises[ id ] = Promise.resolve( this.load( id, this.loadOptions ) )
- .then( source => {
- let ast;
-
- if ( typeof source === 'object' ) {
- ast = source.ast;
- source = source.code;
- }
-
- const module = new Module({
- id,
- source,
- ast,
- bundle: this
- });
-
- this.modules.push( module );
-
- return module;
- });
- }
-
- return this.modulePromises[ id ];
- });
- }
-
- 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.reexports[ 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;
+ else if ( resolvedId === module.id ) {
+ throw new Error( `A module cannot import itself (${resolvedId})` );
}
- // 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 => {
- if ( module.isExternal ) return null;
-
- 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 );
+ else {
+ return this.fetchModule( resolvedId );
+ }
});
- });
});
- return Promise.all( promises ).then( () => {
- if ( !settled ) return this.markAllModifierStatements();
- });
+ return Promise.all( promises );
}
render ( options = {} ) {
const format = options.format || 'es6';
- const allReplacements = this.deconflict( format === 'es6' );
// Determine export mode - 'default', 'named', 'none'
const exportMode = getExportMode( this, options.exports );
@@ -290,23 +174,22 @@ export default class Bundle {
//
// This doesn't apply if the bundle is exported as ES6!
let allBundleExports = blank();
- let isVarDeclaration = blank();
+ let isReassignedVarDeclaration = blank();
let varExports = blank();
let getterExports = [];
this.orderedModules.forEach( module => {
- module.varDeclarations.forEach( name => {
- isVarDeclaration[ module.replacements[ name ] || name ] = true;
+ module.reassignments.forEach( name => {
+ isReassignedVarDeclaration[ module.locals.lookup( name ).name ] = true;
});
});
if ( format !== 'es6' && exportMode === 'named' ) {
- keys( this.entryModule.exports )
- .concat( keys( this.entryModule.reexports ) )
+ this.exports.getNames()
.forEach( name => {
- const canonicalName = this.traceExport( this.entryModule, name );
+ const canonicalName = this.exports.lookup( name ).name;
- if ( isVarDeclaration[ canonicalName ] ) {
+ if ( isReassignedVarDeclaration[ canonicalName ] ) {
varExports[ name ] = true;
// if the same binding is exported multiple ways, we need to
@@ -322,14 +205,13 @@ export default class Bundle {
// 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 )
- .concat( keys( this.entryModule.reexports ) )
+ this.toExport = this.exports.getNames()
.filter( key => !varExports[ key ] );
let magicString = new MagicString.Bundle({ separator: '\n\n' });
this.orderedModules.forEach( module => {
- const source = module.render( allBundleExports, allReplacements[ module.id ], format );
+ const source = module.render( allBundleExports, format === 'es6' );
if ( source.toString().length ) {
magicString.addSource( source );
}
@@ -337,15 +219,14 @@ export default class Bundle {
// prepend bundle with internal namespaces
const indentString = getIndentString( magicString, options );
- const namespaceBlock = this.internalNamespaceModules.map( module => {
- const exports = keys( module.exports )
- .concat( keys( module.reexports ) )
- .map( name => {
- const canonicalName = this.traceExport( module, name );
- return `${indentString}get ${name} () { return ${canonicalName}; }`;
- });
- return `var ${module.replacements['*']} = {\n` +
+ const namespaceBlock = this.modules.filter( module => module.needsDynamicAccess ).map( module => {
+ const exports = module.exports.getNames().map( name => {
+ const id = module.exports.lookup( name );
+ return `${indentString}get ${name} () { return ${id.name}; }`;
+ });
+
+ return `var ${module.name} = {\n` +
exports.join( ',\n' ) +
`\n};\n\n`;
}).join( '' );
@@ -390,12 +271,17 @@ export default class Bundle {
}
sort () {
- let seen = {};
+ // Set of visited module ids.
+ let seen = blank();
+
let ordered = [];
let hasCycles;
- let strongDeps = {};
- let stronglyDependsOn = {};
+ // Map from module id to list of modules.
+ let strongDeps = blank();
+
+ // Map from module id to boolean.
+ let stronglyDependsOn = blank();
function visit ( module ) {
seen[ module.id ] = true;
@@ -472,44 +358,4 @@ export default class Bundle {
return ordered;
}
-
- trace ( module, localName, es6 ) {
- const importDeclaration = module.imports[ localName ];
-
- // defined in this module
- if ( !importDeclaration ) return module.replacements[ localName ] || localName;
-
- // defined elsewhere
- return this.traceExport( importDeclaration.module, importDeclaration.name, es6 );
- }
-
- traceExport ( module, name, es6 ) {
- if ( module.isExternal ) {
- if ( name === 'default' ) return module.needsNamed && !es6 ? `${module.name}__default` : module.name;
- if ( name === '*' ) return module.name;
- return es6 ? name : `${module.name}.${name}`;
- }
-
- const reexportDeclaration = module.reexports[ name ];
- if ( reexportDeclaration ) {
- return this.traceExport( reexportDeclaration.module, reexportDeclaration.localName );
- }
-
- if ( name === '*' ) return module.replacements[ '*' ];
- if ( name === 'default' ) return module.defaultName();
-
- const exportDeclaration = module.exports[ name ];
- if ( exportDeclaration ) return this.trace( module, exportDeclaration.localName );
-
- for ( let i = 0; i < module.exportDelegates.length; i += 1 ) {
- const delegate = module.exportDelegates[i];
- const delegateExportDeclaration = delegate.module.exports[ name ];
-
- if ( delegateExportDeclaration ) {
- return this.trace( delegate.module, delegateExportDeclaration.localName, es6 );
- }
- }
-
- throw new Error( `Could not trace binding '${name}' from ${module.id}` );
- }
}
diff --git a/src/ExternalModule.js b/src/ExternalModule.js
index aabf9df..4b49a2e 100644
--- a/src/ExternalModule.js
+++ b/src/ExternalModule.js
@@ -1,16 +1,36 @@
import { blank } from './utils/object';
+import makeLegalIdentifier from './utils/makeLegalIdentifier';
+
+// An external identifier.
+class Id {
+ constructor ( module, name ) {
+ this.originalName = this.name = name;
+ this.module = module;
+
+ this.modifierStatements = [];
+ }
+
+ // Flags the identifier as imported by the bundle when marked.
+ mark () {
+ this.module.importedByBundle[ this.originalName ] = true;
+ this.modifierStatements.forEach( stmt => stmt.mark() );
+ }
+}
export default class ExternalModule {
- constructor ( id ) {
+ constructor ( { id, bundle } ) {
this.id = id;
- this.name = null;
- this.isExternal = true;
- this.importedByBundle = [];
+ // Implement `Identifier` interface.
+ this.originalName = this.name = makeLegalIdentifier( id );
+ this.module = this;
+ this.isModule = true;
- this.suggestedNames = blank();
+ // Define the external module's name in the bundle scope.
+ bundle.scope.define( id, this );
- this.needsDefault = false;
+ this.isExternal = true;
+ this.importedByBundle = blank();
// Invariant: needsNamed and needsAll are never both true at once.
// Because an import with both a namespace and named import is invalid:
@@ -19,19 +39,28 @@ export default class ExternalModule {
//
this.needsNamed = false;
this.needsAll = false;
- }
- findDefiningStatement () {
- return null;
- }
+ this.exports = bundle.scope.virtual( false );
+
+ const { reference } = this.exports;
+
+ // Override reference.
+ this.exports.reference = name => {
+ if ( name !== 'default' ) {
+ this.needsNamed = true;
+ }
+
+ if ( !this.exports.defines( name ) ) {
+ this.exports.define( name, new Id( this, name ) );
+ }
- rename () {
- // noop
+ return reference.call( this.exports, name );
+ };
}
- suggestName ( exportName, suggestion ) {
- if ( !this.suggestedNames[ exportName ] ) {
- this.suggestedNames[ exportName ] = suggestion;
- }
+ // External modules are always marked for inclusion in the bundle.
+ // Marking an external module signals its use as a namespace.
+ mark () {
+ this.needsAll = true;
}
}
diff --git a/src/Module.js b/src/Module.js
index d47601e..01b461a 100644
--- a/src/Module.js
+++ b/src/Module.js
@@ -1,30 +1,51 @@
-import { Promise } from 'sander';
-import { parse } from 'acorn/src/index';
+import { basename, extname } from './utils/path';
+import { parse } from 'acorn';
import MagicString from 'magic-string';
import Statement from './Statement';
import walk from './ast/walk';
import { blank, keys } from './utils/object';
-import { first, sequence } from './utils/promise';
import getLocation from './utils/getLocation';
import makeLegalIdentifier from './utils/makeLegalIdentifier';
-const emptyPromise = Promise.resolve();
+function isEmptyExportedVarDeclaration ( node, exports, toExport ) {
+ if ( node.type !== 'VariableDeclaration' || node.declarations[0].init ) return false;
+
+ const name = node.declarations[0].id.name;
-function deconflict ( name, names ) {
- while ( name in names ) {
- name = `_${name}`;
+ const id = exports.lookup( name );
+
+ return id && id.name in toExport;
+}
+
+function removeSourceMappingURLComments ( source, magicString ) {
+ const pattern = /\/\/#\s+sourceMappingURL=.+\n?/g;
+ let match;
+ while ( match = pattern.exec( source ) ) {
+ magicString.remove( match.index, match.index + match[0].length );
}
+}
- return name;
+function assign ( target, source ) {
+ for ( let key in source ) target[ key ] = source[ key ];
}
-function isEmptyExportedVarDeclaration ( node, allBundleExports, moduleReplacements ) {
- if ( node.type !== 'VariableDeclaration' || node.declarations[0].init ) return false;
+class Id {
+ constructor ( module, name, statement ) {
+ this.originalName = this.name = name;
+ this.module = module;
+ this.statement = statement;
- const name = node.declarations[0].id.name;
- const canonicalName = moduleReplacements[ name ] || name;
+ this.modifierStatements = [];
- return canonicalName in allBundleExports;
+ // modifiers
+ this.isUsed = false;
+ }
+
+ mark () {
+ this.isUsed = true;
+ this.statement.mark();
+ this.modifierStatements.forEach( stmt => stmt.mark() );
+ }
}
export default class Module {
@@ -33,6 +54,16 @@ export default class Module {
this.bundle = bundle;
this.id = id;
+ this.module = this;
+ this.isModule = true;
+
+ // Implement Identifier interface.
+ this.name = makeLegalIdentifier( basename( id ).slice( 0, -extname( id ).length ) );
+
+ // HACK: If `id` isn't a path, the above code yields the empty string.
+ if ( !this.name ) {
+ this.name = makeLegalIdentifier( id );
+ }
// By default, `id` is the filename. Custom resolvers and loaders
// can change that, but it makes sense to use it for the source filename
@@ -40,37 +71,60 @@ export default class Module {
filename: id
});
- // remove existing sourceMappingURL comments
- const pattern = /\/\/#\s+sourceMappingURL=.+\n?/g;
- let match;
- while ( match = pattern.exec( source ) ) {
- this.magicString.remove( match.index, match.index + match[0].length );
- }
+ removeSourceMappingURLComments( source, this.magicString );
- this.suggestedNames = blank();
this.comments = [];
this.statements = this.parse( ast );
- // imports and exports, indexed by ID
- this.imports = blank();
- this.exports = blank();
- this.reexports = blank();
+ // all dependencies
+ this.resolvedIds = blank();
- this.exportAlls = blank();
+ // Virtual scopes for the local and exported names.
+ this.locals = bundle.scope.virtual( true );
+ this.exports = bundle.scope.virtual( false );
- // array of all-export sources
- this.exportDelegates = [];
+ const { reference, inScope } = this.exports;
- this.replacements = blank();
+ this.exports.reference = name => {
+ // If we have it, grab it.
+ if ( inScope.call( this.exports, name ) ) {
+ return reference.call( this.exports, name );
+ }
- this.varDeclarations = [];
+ // ... otherwise search allExportsFrom
+ for ( let i = 0; i < this.allExportsFrom.length; i += 1 ) {
+ const module = this.allExportsFrom[i];
+ if ( module.exports.inScope( name ) ) {
+ return module.exports.reference( name );
+ }
+ }
+
+ // throw new Error( `The name "${name}" is never exported (from ${this.id})!` );
+ return reference.call( this.exports, name );
+ };
+
+ this.exports.inScope = name => {
+ if ( inScope.call( this.exports, name ) ) return true;
+
+ return this.allExportsFrom.some( module => module.exports.inScope( name ) );
+ };
+
+ // Create a unique virtual scope for references to the module.
+ // const unique = bundle.scope.virtual();
+ // unique.define( this.name, this );
+ // this.reference = unique.reference( this.name );
- this.definitions = blank();
- this.definitionPromises = blank();
- this.modifications = blank();
+ // As far as we know, all our exported bindings have been resolved.
+ this.allExportsResolved = true;
+ this.allExportsFrom = [];
- this.analyse();
+ this.reassignments = [];
+
+ // TODO: change to false, and detect when it's necessary.
+ this.needsDynamicAccess = false;
+
+ this.dependencies = this.collectDependencies();
}
addExport ( statement ) {
@@ -79,22 +133,32 @@ export default class Module {
// export { name } from './other'
if ( source ) {
+ const module = this.getModule( source );
+
if ( node.type === 'ExportAllDeclaration' ) {
// Store `export * from '...'` statements in an array of delegates.
// When an unknown import is encountered, we see if one of them can satisfy it.
- this.exportDelegates.push({
- statement,
- source
- });
+
+ if ( module.isExternal ) {
+ let err = new Error( `Cannot trace 'export *' references through external modules.` );
+ err.file = this.id;
+ err.loc = getLocation( this.source, node.start );
+ throw err;
+ }
+
+ // It seems like we must re-export all exports from another module...
+ this.allExportsResolved = false;
+
+ if ( !~this.allExportsFrom.indexOf( module ) ) {
+ this.allExportsFrom.push( module );
+ }
}
else {
node.specifiers.forEach( specifier => {
- this.reexports[ specifier.exported.name ] = {
- source,
- localName: specifier.local.name,
- module: null // filled in later
- };
+ // Bind the export of this module, to the export of the other.
+ this.exports.bind( specifier.exported.name,
+ module.exports.reference( specifier.local.name ) );
});
}
}
@@ -111,16 +175,18 @@ export default class Module {
node.declaration.type === 'Identifier' ?
node.declaration.name :
null;
+ const name = identifier || this.name;
+
+ // Always define a new `Identifier` for the default export.
+ const id = new Id( this, name, statement );
+
+ // Keep the identifier name, if one exists.
+ // We can optimize the newly created default `Identifier` away,
+ // if it is never modified.
+ // in case of `export default foo; foo = somethingElse`
+ assign( id, { isDeclaration, isAnonymous, identifier } );
- this.exports.default = {
- statement,
- name: 'default',
- localName: identifier || 'default',
- identifier,
- isDeclaration,
- isAnonymous,
- isModified: false // in case of `export default foo; foo = somethingElse`
- };
+ this.exports.define( 'default', id );
}
// export { foo, bar, baz }
@@ -133,11 +199,7 @@ export default class Module {
const localName = specifier.local.name;
const exportedName = specifier.exported.name;
- this.exports[ exportedName ] = {
- statement,
- localName,
- exportedName
- };
+ this.exports.bind( exportedName, this.locals.reference( localName ) );
});
}
@@ -154,38 +216,49 @@ export default class Module {
name = declaration.id.name;
}
- this.exports[ name ] = {
- statement,
- localName: name,
- expression: declaration
- };
+ this.locals.define( name, new Id( this, name, statement ) );
+ this.exports.bind( name, this.locals.reference( name ) );
}
}
}
addImport ( statement ) {
const node = statement.node;
- const source = node.source.value;
+ const module = this.getModule( node.source.value );
node.specifiers.forEach( specifier => {
const isDefault = specifier.type === 'ImportDefaultSpecifier';
const isNamespace = specifier.type === 'ImportNamespaceSpecifier';
const localName = specifier.local.name;
- const name = isDefault ? 'default' : isNamespace ? '*' : specifier.imported.name;
- if ( this.imports[ localName ] ) {
+ if ( this.locals.defines( localName ) ) {
const err = new Error( `Duplicated import '${localName}'` );
err.file = this.id;
err.loc = getLocation( this.source, specifier.start );
throw err;
}
- this.imports[ localName ] = {
- source,
- name,
- localName
- };
+ if ( isNamespace ) {
+ // If it's a namespace import, we bind the localName to the module itself.
+ module.needsAll = true;
+ module.name = localName;
+ this.locals.bind( localName, module );
+ } else {
+ const name = isDefault ? 'default' : specifier.imported.name;
+
+ this.locals.bind( localName, module.exports.reference( name ) );
+
+ // For compliance with earlier Rollup versions.
+ // If the module is external, and we access the default.
+ // Rewrite the module name, and the default name to the
+ // `localName` we use for it.
+ if ( module.isExternal && isDefault ) {
+ const id = module.exports.lookup( name );
+ module.name = id.name = localName;
+ id.name += '__default';
+ }
+ }
});
}
@@ -199,15 +272,36 @@ export default class Module {
// consolidate names that are defined/modified in this module
keys( statement.defines ).forEach( name => {
- this.definitions[ name ] = statement;
+ this.locals.define( name, new Id( this, name, statement ) );
});
+ });
- statement.scope.varDeclarations.forEach( name => {
- this.varDeclarations.push( name );
+ // If all exports aren't resolved, but all our delegate modules are...
+ if ( !this.allExportsResolved && this.allExportsFrom.every( module => module.allExportsResolved )) {
+ // .. then all our exports should be as well.
+ this.allExportsResolved = true;
+
+ // For all modules we export all from, iterate through its exported names.
+ // If we don't already define the binding 'name',
+ // bind the name to the other module's reference.
+ this.allExportsFrom.forEach( module => {
+ module.exports.getNames().forEach( name => {
+ if ( !this.exports.defines( name ) ) {
+ this.exports.bind( name, module.exports.reference( name ) );
+ }
+ });
});
+ }
- keys( statement.modifies ).forEach( name => {
- ( this.modifications[ name ] || ( this.modifications[ name ] = [] ) ).push( statement );
+ // discover variables that are reassigned inside function
+ // bodies, so we can keep bindings live, e.g.
+ //
+ // export var count = 0;
+ // export function incr () { count += 1 }
+ let reassigned = blank();
+ this.statements.forEach( statement => {
+ keys( statement.reassigns ).forEach( name => {
+ reassigned[ name ] = true;
});
});
@@ -216,12 +310,47 @@ export default class Module {
this.statements.forEach( statement => {
if ( statement.isReexportDeclaration ) return;
+ // while we're here, mark reassignments
+ statement.scope.varDeclarations.forEach( name => {
+ if ( reassigned[ name ] && !~this.reassignments.indexOf( name ) ) {
+ this.reassignments.push( name );
+ }
+ });
+
keys( statement.dependsOn ).forEach( name => {
- if ( !this.definitions[ name ] && !this.imports[ name ] ) {
- this.bundle.assumedGlobals[ name ] = true;
+ // For each name we depend on that isn't in scope,
+ // add a new global and bind the local name to it.
+ if ( !this.locals.inScope( name ) ) {
+ this.bundle.globals.define( name, {
+ originalName: name,
+ name,
+ mark () {}
+ });
+ this.locals.bind( name, this.bundle.globals.reference( name ) );
}
});
});
+
+ // OPTIMIZATION!
+ // If we have a default export and it's value is never modified,
+ // bind to it directly.
+ const def = this.exports.lookup( 'default' );
+ if ( def && !def.isModified && def.identifier ) {
+ this.exports.bind( 'default', this.locals.reference( def.identifier ) );
+ }
+ }
+
+ // Returns the set of imported module ids by going through all import/exports statements.
+ collectDependencies () {
+ const importedModules = blank();
+
+ this.statements.forEach( statement => {
+ if ( statement.isImportDeclaration || ( statement.isExportDeclaration && statement.node.source ) ) {
+ importedModules[ statement.node.source.value ] = true;
+ }
+ });
+
+ return keys( importedModules );
}
consolidateDependencies () {
@@ -235,25 +364,21 @@ export default class Module {
}
this.statements.forEach( statement => {
- if ( statement.isImportDeclaration && !statement.node.specifiers.length && !statement.module.isExternal ) {
+ 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?
+ const module = this.getModule( statement.node.source.value );
+
+ if ( !module.isExternal ) strongDependencies[ module.id ] = module;
}
else if ( statement.isReexportDeclaration ) {
if ( statement.node.specifiers ) {
statement.node.specifiers.forEach( specifier => {
- let reexport;
-
- let module = this;
let name = specifier.exported.name;
- while ( !module.isExternal && module.reexports[ name ] && module.reexports[ name ].isUsed ) {
- reexport = module.reexports[ name ];
- module = reexport.module;
- name = reexport.localName;
- }
- addDependency( strongDependencies, reexport );
+ let id = this.exports.lookup( name );
+
+ addDependency( strongDependencies, id );
});
}
}
@@ -262,8 +387,7 @@ export default class Module {
keys( statement.stronglyDependsOn ).forEach( name => {
if ( statement.defines[ name ] ) return;
- addDependency( strongDependencies, this.exportAlls[ name ] ) ||
- addDependency( strongDependencies, this.imports[ name ] );
+ addDependency( strongDependencies, this.locals.lookup( name ) );
});
}
});
@@ -274,114 +398,53 @@ export default class Module {
keys( statement.dependsOn ).forEach( name => {
if ( statement.defines[ name ] ) return;
- addDependency( weakDependencies, this.exportAlls[ name ] ) ||
- addDependency( weakDependencies, this.imports[ name ] );
+ addDependency( weakDependencies, this.locals.lookup( name ) );
});
});
- return { strongDependencies, weakDependencies };
- }
+ // Go through all our local and exported ids and make us depend on
+ // the defining modules as well as
+ this.exports.getIds().concat(this.locals.getIds()).forEach( id => {
+ if ( id.module && !id.module.isExternal ) {
+ weakDependencies[ id.module.id ] = id.module;
+ }
- defaultName () {
- const defaultExport = this.exports.default;
+ if ( !id.modifierStatements ) return;
- if ( !defaultExport ) return null;
+ id.modifierStatements.forEach( statement => {
+ const module = statement.module;
+ weakDependencies[ module.id ] = module;
+ });
+ });
- const name = defaultExport.identifier && !defaultExport.isModified ?
- defaultExport.identifier :
- this.replacements.default;
+ // `Bundle.sort` gets stuck in an infinite loop if a module has
+ // `strongDependencies` to itself. Make sure it doesn't happen.
+ delete strongDependencies[ this.id ];
+ delete weakDependencies[ this.id ];
- return this.replacements[ name ] || name;
+ return { strongDependencies, weakDependencies };
}
- findDefiningStatement ( name ) {
- if ( this.definitions[ name ] ) return this.definitions[ name ];
-
- // TODO what about `default`/`*`?
-
- const importDeclaration = this.imports[ name ];
- if ( !importDeclaration ) return null;
-
- return Promise.resolve( importDeclaration.module || this.bundle.fetchModule( importDeclaration.source, this.id ) )
- .then( module => {
- importDeclaration.module = module;
- return module.findDefiningStatement( name );
- });
+ getModule ( source ) {
+ return this.bundle.moduleById[ this.resolvedIds[ source ] ];
}
- mark ( name ) {
- // shortcut cycles
- if ( this.definitionPromises[ name ] ) {
- return emptyPromise;
- }
-
- let promise;
-
- // The definition for this name is in a different module
- if ( this.imports[ name ] ) {
- const importDeclaration = this.imports[ name ];
- importDeclaration.isUsed = true;
-
- promise = this.bundle.fetchModule( importDeclaration.source, this.id )
- .then( module => {
- importDeclaration.module = module;
-
- // suggest names. TODO should this apply to non default/* imports?
- if ( importDeclaration.name === 'default' ) {
- // TODO this seems ropey
- const localName = importDeclaration.localName;
- let suggestion = this.suggestedNames[ localName ] || localName;
-
- // special case - the module has its own import by this name
- while ( !module.isExternal && module.imports[ suggestion ] ) {
- suggestion = `_${suggestion}`;
- }
-
- module.suggestName( 'default', suggestion );
- } else if ( importDeclaration.name === '*' ) {
- const localName = importDeclaration.localName;
- const suggestion = this.suggestedNames[ localName ] || localName;
- module.suggestName( '*', suggestion );
- module.suggestName( 'default', `${suggestion}__default` );
- }
-
- if ( importDeclaration.name === 'default' ) {
- module.needsDefault = true;
- } else if ( importDeclaration.name === '*' ) {
- module.needsAll = true;
- } else {
- module.needsNamed = true;
- }
-
- if ( module.isExternal ) {
- module.importedByBundle.push( importDeclaration );
- return emptyPromise;
- }
-
- if ( importDeclaration.name === '*' ) {
- // we need to create an internal namespace
- if ( !~this.bundle.internalNamespaceModules.indexOf( module ) ) {
- this.bundle.internalNamespaceModules.push( module );
- }
+ // If a module is marked, enforce dynamic access of its properties.
+ mark () {
+ if ( this.needsDynamicAccess ) return;
+ this.needsDynamicAccess = true;
- return module.markAllExportStatements();
- }
-
- return module.markExport( importDeclaration.name, name, this );
- });
- }
-
- else {
- const statement = name === 'default' ? this.exports.default.statement : this.definitions[ name ];
- promise = statement && statement.mark();
- }
+ this.markAllExports();
+ }
- this.definitionPromises[ name ] = promise || emptyPromise;
- return this.definitionPromises[ name ];
+ markAllSideEffects () {
+ this.statements.forEach( statement => {
+ statement.markSideEffect();
+ });
}
markAllStatements ( isEntryModule ) {
- return sequence( this.statements, statement => {
+ this.statements.forEach( statement => {
if ( statement.isIncluded ) return; // TODO can this happen? probably not...
// skip import declarations...
@@ -389,87 +452,33 @@ export default class Module {
// ...unless they're empty, in which case assume we're importing them for the side-effects
// THIS IS NOT FOOLPROOF. Probably need /*rollup: include */ or similar
if ( !statement.node.specifiers.length ) {
- return this.bundle.fetchModule( statement.node.source.value, this.id )
- .then( module => {
- statement.module = module;
- if ( module.isExternal ) {
- return;
- }
- return module.markAllStatements();
- });
- }
+ const otherModule = this.getModule( statement.node.source.value );
- return;
+ if ( !otherModule.isExternal ) otherModule.markAllStatements();
+ }
}
// skip `export { foo, bar, baz }`...
- if ( statement.node.type === 'ExportNamedDeclaration' && statement.node.specifiers.length ) {
+ else if ( statement.node.type === 'ExportNamedDeclaration' && statement.node.specifiers.length ) {
// ...but ensure they are defined, if this is the entry module
- if ( isEntryModule ) {
- return statement.mark();
- }
-
- return;
+ if ( isEntryModule ) statement.mark();
}
// include everything else
- return statement.mark();
- });
- }
+ else {
+ // Be sure to mark the default export for the entry module.
+ if ( isEntryModule && statement.node.type === 'ExportDefaultDeclaration' ) {
+ this.exports.lookup( 'default' ).mark();
+ }
- markAllExportStatements () {
- return sequence( this.statements, statement => {
- return statement.isExportDeclaration ?
- statement.mark() :
- null;
+ statement.mark();
+ }
});
}
- markExport ( name, suggestedName, importer ) {
- const reexportDeclaration = this.reexports[ name ];
- if ( reexportDeclaration ) {
- reexportDeclaration.isUsed = true;
-
- return this.bundle.fetchModule( reexportDeclaration.source, this.id )
- .then( otherModule => {
- reexportDeclaration.module = otherModule;
- return otherModule.markExport( reexportDeclaration.localName, suggestedName, this );
- });
- }
-
- const exportDeclaration = this.exports[ name ];
- if ( exportDeclaration ) {
- exportDeclaration.isUsed = true;
- if ( name === 'default' ) {
- this.needsDefault = true;
- this.suggestName( 'default', suggestedName );
- return exportDeclaration.statement.mark();
- }
-
- return this.mark( exportDeclaration.localName );
- }
-
- const noExport = new Error( `Module ${this.id} does not export ${name} (imported by ${importer.id})` );
-
- // See if there exists an export delegate that defines `name`.
- return first( this.exportDelegates, noExport, declaration => {
- return this.bundle.fetchModule( declaration.source, this.id ).then( submodule => {
- declaration.module = submodule;
-
- return submodule.mark( name ).then( result => {
- if ( !result.length ) throw noExport;
-
- // It's found! This module exports `name` through declaration.
- // It is however not imported into this scope.
- this.exportAlls[ name ] = declaration;
-
- declaration.statement.dependsOn[ name ] =
- declaration.statement.stronglyDependsOn[ name ] = result;
-
- return result;
- });
- });
- });
+ // Marks all exported identifiers.
+ markAllExports () {
+ this.exports.getIds().forEach( id => id.mark() );
}
parse ( ast ) {
@@ -555,11 +564,7 @@ export default class Module {
return statements;
}
- rename ( name, replacement ) {
- this.replacements[ name ] = replacement;
- }
-
- render ( allBundleExports, moduleReplacements ) {
+ render ( toExport, direct ) {
let magicString = this.magicString.clone();
this.statements.forEach( statement => {
@@ -577,7 +582,7 @@ export default class Module {
}
// skip `export var foo;` if foo is exported
- if ( isEmptyExportedVarDeclaration( statement.node.declaration, allBundleExports, moduleReplacements ) ) {
+ if ( isEmptyExportedVarDeclaration( statement.node.declaration, this.exports, toExport ) ) {
magicString.remove( statement.start, statement.next );
return;
}
@@ -585,7 +590,7 @@ export default class Module {
// skip empty var declarations for exported bindings
// (otherwise we're left with `exports.foo;`, which is useless)
- if ( isEmptyExportedVarDeclaration( statement.node, allBundleExports, moduleReplacements ) ) {
+ if ( isEmptyExportedVarDeclaration( statement.node, this.exports, toExport ) ) {
magicString.remove( statement.start, statement.next );
return;
}
@@ -593,7 +598,7 @@ export default class Module {
// split up/remove var declarations as necessary
if ( statement.node.isSynthetic ) {
// insert `var/let/const` if necessary
- if ( !allBundleExports[ statement.node.declarations[0].id.name ] ) {
+ if ( !toExport[ statement.node.declarations[0].id.name ] ) {
magicString.insert( statement.start, `${statement.node.kind} ` );
}
@@ -603,14 +608,34 @@ export default class Module {
let replacements = blank();
let bundleExports = blank();
+ // Indirect identifier access.
+ if ( !direct ) {
+ keys( statement.dependsOn )
+ .forEach( name => {
+ const id = this.locals.lookup( name );
+
+ // We shouldn't create a replacement for `id` if
+ // 1. `id` is a Global, in which case it has no module property
+ // 2. `id.module` isn't external, which means we have direct access
+ // 3. `id` is its own module, in the case of namespace imports
+ if ( id.module && id.module.isExternal && id.module !== id ) {
+ replacements[ name ] = id.originalName === 'default' ?
+ // default names are always directly accessed
+ id.name :
+ // other names are indirectly accessed
+ `${id.module.name}.${id.originalName}`;
+ }
+ });
+ }
+
keys( statement.dependsOn )
.concat( keys( statement.defines ) )
.forEach( name => {
- const bundleName = moduleReplacements[ name ] || name;
+ const bundleName = this.locals.lookup( name ).name;
- if ( allBundleExports[ bundleName ] ) {
- bundleExports[ name ] = replacements[ name ] = allBundleExports[ bundleName ];
- } else if ( bundleName !== name ) { // TODO weird structure
+ if ( toExport[ bundleName ] ) {
+ bundleExports[ name ] = replacements[ name ] = toExport[ bundleName ];
+ } else if ( bundleName !== name && !replacements[ name ] ) { // TODO weird structure
replacements[ name ] = bundleName;
}
});
@@ -624,6 +649,11 @@ export default class Module {
magicString.remove( statement.node.start, statement.node.declaration.start );
}
+ else if ( statement.node.type === 'ExportAllDeclaration' ) {
+ // TODO: remove once `export * from 'external'` is supported.
+ magicString.remove( statement.start, statement.next );
+ }
+
// remove `export` from `export class Foo {...}` or `export default Foo`
// TODO default exports need different treatment
else if ( statement.node.declaration.id ) {
@@ -631,24 +661,25 @@ export default class Module {
}
else if ( statement.node.type === 'ExportDefaultDeclaration' ) {
- const canonicalName = this.defaultName();
+ const def = this.exports.lookup( 'default' );
- if ( statement.node.declaration.type === 'Identifier' && canonicalName === ( moduleReplacements[ statement.node.declaration.name ] || statement.node.declaration.name ) ) {
+ // FIXME: dunno what to do here yet.
+ if ( statement.node.declaration.type === 'Identifier' && def.name === ( replacements[ statement.node.declaration.name ] || statement.node.declaration.name ) ) {
magicString.remove( statement.start, statement.next );
return;
}
// prevent `var undefined = sideEffectyDefault(foo)`
- if ( canonicalName === undefined ) {
+ if ( !def.isUsed ) {
magicString.remove( statement.start, statement.node.declaration.start );
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}` );
+ magicString.overwrite( statement.node.start, statement.node.declaration.start + 8, `function ${def.name}` );
} else {
- magicString.overwrite( statement.node.start, statement.node.declaration.start, `var ${canonicalName} = ` );
+ magicString.overwrite( statement.node.start, statement.node.declaration.start, `var ${def.name} = ` );
}
}
@@ -660,15 +691,4 @@ export default class Module {
return magicString.trim();
}
-
- suggestName ( defaultOrBatch, suggestion ) {
- // deconflict anonymous default exports with this module's definitions
- const shouldDeconflict = this.exports.default && this.exports.default.isAnonymous;
-
- if ( shouldDeconflict ) suggestion = deconflict( suggestion, this.definitions );
-
- if ( !this.suggestedNames[ defaultOrBatch ] ) {
- this.suggestedNames[ defaultOrBatch ] = makeLegalIdentifier( suggestion );
- }
- }
}
diff --git a/src/Scope.js b/src/Scope.js
new file mode 100644
index 0000000..e5f5967
--- /dev/null
+++ b/src/Scope.js
@@ -0,0 +1,158 @@
+import { blank, keys } from './utils/object';
+
+// A minimal `Identifier` implementation. Anything that has an `originalName`,
+// and a mutable `name` property can be used as an `Identifier`.
+class Identifier {
+ constructor ( name ) {
+ this.originalName = this.name = name;
+ }
+
+ mark () {
+ // noop
+ }
+}
+
+// A reference to an `Identifier`.
+function Reference ( scope, index ) {
+ this.scope = scope;
+ this.index = index;
+}
+
+// Dereferences a `Reference`.
+function dereference ( ref ) {
+ return ref.scope.ids[ ref.index ];
+}
+
+function isntReference ( id ) {
+ return !( id instanceof Reference );
+}
+
+// Prefix the argument with '_'.
+function underscorePrefix ( x ) {
+ return '_' + x;
+}
+
+// ## Scope
+// A Scope is a mapping from string names to `Identifiers`.
+export default class Scope {
+ constructor ( parent ) {
+ this.ids = [];
+ this.names = blank();
+
+ this.parent = parent || null;
+ this.used = blank();
+ }
+
+ // Binds the `name` to the given reference `ref`.
+ bind ( name, ref ) {
+ this.ids[ this.index( name ) ] = ref;
+ }
+
+ // Deconflict all names within the scope,
+ // using the given renaming function.
+ // If no function is supplied, `underscorePrefix` is used.
+ deconflict ( rename = underscorePrefix ) {
+ const names = this.used;
+
+ this.ids.filter( ref => ref instanceof Reference ).forEach( ref => {
+ // Same scope.
+ if ( ref.scope.ids === this.ids ) return;
+
+ // Another scope!
+ while ( ref instanceof Reference ) {
+ ref = dereference( ref );
+ }
+
+ names[ ref.name ] = ref;
+ });
+
+ this.ids.filter( isntReference ).forEach( id => {
+ if ( typeof id === 'string' ) {
+ throw new Error( `Required name "${id}" is undefined!` );
+ }
+
+ let name = id.name;
+
+ while ( name in names && names[ name ] !== id ) {
+ name = rename( name );
+ }
+ names[ name ] = id;
+
+ id.name = name;
+ });
+ }
+
+ // Defines `name` in the scope to be `id`.
+ // If no `id` is supplied, a plain `Identifier` is created.
+ define ( name, id ) {
+ this.ids[ this.index( name ) ] = id || new Identifier( name );
+ }
+
+ // TODO: rename! Too similar to `define`.
+ defines ( name ) {
+ return name in this.names;
+ }
+
+ // Return the names referenced to in the scope.
+ getNames () {
+ return keys( this.names );
+ }
+
+ // *private, don't use*
+ //
+ // Return `name`'s index in the `ids` array if it exists,
+ // otherwise returns the index to a new placeholder slot.
+ index ( name ) {
+ if ( !( name in this.names ) ) {
+ return this.names[ name ] = this.ids.push( name ) - 1;
+ }
+
+ return this.names[ name ];
+ }
+
+ // Returns true if `name` is in Scope.
+ inScope ( name ) {
+ if ( name in this.names ) return true;
+
+ return this.parent ? this.parent.inScope( name ) : false;
+ }
+
+ // Returns a list of `[ name, identifier ]` tuples.
+ getIds () {
+ return keys( this.names ).map( name => this.lookup( name ) );
+ }
+
+ // Lookup the identifier referred to by `name`.
+ lookup ( name ) {
+ if ( !( name in this.names ) && this.parent ) {
+ return this.parent.lookup( name );
+ }
+
+ let id = this.ids[ this.names[ name ] ];
+
+ while ( id instanceof Reference ) {
+ id = dereference( id );
+ }
+
+ return id;
+ }
+
+ // Get a reference to the identifier `name` in this scope.
+ reference ( name ) {
+ return new Reference( this, this.index( name ) );
+ }
+
+ // Return the used names of the scope.
+ // Names aren't considered used unless they're deconflicted.
+ usedNames () {
+ return keys( this.used ).sort();
+ }
+
+ // Create and return a virtual `Scope` instance, bound to
+ // the actual scope of `this`, optionally inherit the parent scope.
+ virtual ( inheritParent ) {
+ const scope = new Scope( inheritParent ? this.parent : null );
+ scope.ids = this.ids;
+ return scope;
+ }
+}
diff --git a/src/Statement.js b/src/Statement.js
index b68d216..1a165b4 100644
--- a/src/Statement.js
+++ b/src/Statement.js
@@ -1,9 +1,18 @@
import { blank, keys } from './utils/object';
-import { sequence } from './utils/promise';
import getLocation from './utils/getLocation';
import walk from './ast/walk';
import Scope from './ast/Scope';
+const blockDeclarations = {
+ 'const': true,
+ 'let': true
+};
+
+const modifierNodes = {
+ AssignmentExpression: 'left',
+ UpdateExpression: 'argument'
+};
+
function isIife ( node, parent ) {
return parent && parent.type === 'CallExpression' && node === parent.callee;
}
@@ -16,6 +25,14 @@ function isFunctionDeclaration ( node, parent ) {
if ( node.type === 'FunctionExpression' && parent.type === 'VariableDeclarator' ) return true;
}
+function chainedMemberExpression ( node ) {
+ if ( node.object.type === 'MemberExpression' ) {
+ return chainedMemberExpression( node.object ) + '.' + node.property.name;
+ }
+
+ return node.object.name + '.' + node.property.name;
+}
+
export default class Statement {
constructor ( node, module, start, end ) {
this.node = node;
@@ -26,10 +43,15 @@ export default class Statement {
this.scope = new Scope();
this.defines = blank();
- this.modifies = blank();
this.dependsOn = blank();
this.stronglyDependsOn = blank();
+ this.reassigns = blank();
+
+ // TODO: make this more efficient
+ this.dependantIds = [];
+ this.namespaceReplacements = [];
+
this.isIncluded = false;
this.isImportDeclaration = node.type === 'ImportDeclaration';
@@ -40,6 +62,19 @@ export default class Statement {
analyse () {
if ( this.isImportDeclaration ) return; // nothing to analyse
+ // `export { name } from './other'` is a special case
+ if ( this.isReexportDeclaration ) {
+ this.node.specifiers && this.node.specifiers.forEach( specifier => {
+ const id = this.module.exports.lookup( specifier.exported.name );
+
+ if ( !~this.dependantIds.indexOf( id ) ) {
+ this.dependantIds.push( id );
+ }
+ });
+
+ return;
+ }
+
let scope = this.scope;
walk( this.node, {
@@ -47,29 +82,23 @@ export default class Statement {
let newScope;
switch ( node.type ) {
- case 'FunctionExpression':
case 'FunctionDeclaration':
- case 'ArrowFunctionExpression':
- if ( node.type === 'FunctionDeclaration' ) {
- scope.addDeclaration( node.id.name, node, false );
- }
-
- newScope = new Scope({
- parent: scope,
- params: node.params, // TODO rest params?
- block: false
- });
-
- // named function expressions - the name is considered
- // part of the function's scope
- if ( node.type === 'FunctionExpression' && node.id ) {
- newScope.addDeclaration( node.id.name, node, false );
- }
-
- break;
+ scope.addDeclaration( node, false, false );
case 'BlockStatement':
- if ( !/Function/.test( parent.type ) ) {
+ if ( parent && /Function/.test( parent.type ) ) {
+ newScope = new Scope({
+ parent: scope,
+ block: false,
+ params: parent.params
+ });
+
+ // named function expressions - the name is considered
+ // part of the function's scope
+ if ( parent.type === 'FunctionExpression' && parent.id ) {
+ newScope.addDeclaration( parent, false, false );
+ }
+ } else {
newScope = new Scope({
parent: scope,
block: true
@@ -89,12 +118,13 @@ export default class Statement {
case 'VariableDeclaration':
node.declarations.forEach( declarator => {
- scope.addDeclaration( declarator.id.name, node, true );
+ const isBlockDeclaration = node.type === 'VariableDeclaration' && blockDeclarations[ node.kind ];
+ scope.addDeclaration( declarator, isBlockDeclaration, true );
});
break;
case 'ClassDeclaration':
- scope.addDeclaration( node.id.name, node, false );
+ scope.addDeclaration( node, false, false );
break;
}
@@ -131,29 +161,105 @@ export default class Statement {
// /update expressions) need to be captured
let writeDepth = 0;
+ // Used to track
+ let topName;
+ let currentMemberExpression = null;
+ let namespace = null;
+
if ( !this.isImportDeclaration ) {
walk( this.node, {
enter: ( node, parent ) => {
- if ( node._scope ) {
- if ( !scope.isBlockScope ) {
- if ( !isIife( node, parent ) ) readDepth += 1;
- if ( isFunctionDeclaration( node, parent ) ) writeDepth += 1;
- }
+ if ( isFunctionDeclaration( node, parent ) ) writeDepth += 1;
+ if ( /Function/.test( node.type ) && !isIife( node, parent ) ) readDepth += 1;
- scope = node._scope;
- }
+ if ( node._scope ) scope = node._scope;
this.checkForReads( scope, node, parent, !readDepth );
this.checkForWrites( scope, node, writeDepth );
},
leave: ( node, parent ) => {
- if ( node._scope ) {
- if ( !scope.isBlockScope ) {
- if ( !isIife( node, parent ) ) readDepth -= 1;
- if ( isFunctionDeclaration( node, parent ) ) writeDepth -= 1;
+ if ( isFunctionDeclaration( node, parent ) ) writeDepth -= 1;
+ if ( /Function/.test( node.type ) && !isIife( node, parent ) ) readDepth -= 1;
+
+ if ( node._scope ) scope = scope.parent;
+
+ // Optimize namespace lookups, which manifest as MemberExpressions.
+ if ( node.type === 'MemberExpression' && ( !currentMemberExpression || node.object === currentMemberExpression ) ) {
+ currentMemberExpression = node;
+
+ if ( !namespace ) {
+ topName = node.object.name;
+ const id = this.module.locals.lookup( topName );
+
+ if ( !id || !id.isModule || id.isExternal ) return;
+
+ namespace = id;
+ }
+
+ // If a namespace is the left hand side of an assignment, throw an error.
+ if ( parent.type === 'AssignmentExpression' && parent.left === node ||
+ parent.type === 'UpdateExpression' && parent.argument === node ) {
+ const err = new Error( `Illegal reassignment to import '${chainedMemberExpression( node )}'` );
+ err.file = this.module.id;
+ err.loc = getLocation( this.module.magicString.toString(), node.start );
+ throw err;
+ }
+
+ // Extract the name of the accessed property, from and Identifier or Literal.
+ // Any eventual Literal value is converted to a string.
+ const name = !node.computed ? node.property.name :
+ ( node.property.type === 'Literal' ? String( node.property.value ) : null );
+
+ // If we can't resolve the name being accessed statically,
+ // we mark the whole namespace for inclusion in the bundle.
+ //
+ // // resolvable
+ // console.log( javascript.keywords.for )
+ // console.log( javascript.keywords[ 'for' ] )
+ // console.log( javascript.keywords[ 6 ] )
+ //
+ // // unresolvable
+ // console.log( javascript.keywords[ index ] )
+ // console.log( javascript.keywords[ 1 + 5 ] )
+ if ( name === null ) {
+ namespace.mark();
+
+ namespace = null;
+ currentMemberExpression = null;
+ return;
+ }
+
+ const id = namespace.exports.lookup( name );
+
+ // If the namespace doesn't define the given name,
+ // we can throw an error (even for nested namespaces).
+ if ( !id ) {
+ throw new Error( `Module doesn't define "${name}"!` );
+ }
+
+ // We can't resolve deeper. Replace the member chain.
+ if ( parent.type !== 'MemberExpression' || !( id.isModule && !id.isExternal ) ) {
+ if ( !~this.dependantIds.indexOf( id ) ) {
+ this.dependantIds.push( id );
+ }
+
+ // FIXME: do this better
+ // If we depend on this name...
+ if ( this.dependsOn[ topName ] ) {
+ // ... decrement the count...
+ if ( !--this.dependsOn[ topName ] ) {
+ // ... and remove it if the count is 0.
+ delete this.dependsOn[ topName ];
+ }
+ }
+
+ this.namespaceReplacements.push( [ node, id ] );
+ namespace = null;
+ currentMemberExpression = null;
+ return;
}
- scope = scope.parent;
+ namespace = id;
}
}
});
@@ -185,7 +291,11 @@ export default class Statement {
const definingScope = scope.findDefiningScope( node.name );
if ( !definingScope || definingScope.depth === 0 ) {
- this.dependsOn[ node.name ] = true;
+ if ( !( node.name in this.dependsOn ) ) {
+ this.dependsOn[ node.name ] = 0;
+ }
+
+ this.dependsOn[ node.name ]++;
if ( strong ) this.stronglyDependsOn[ node.name ] = true;
}
}
@@ -202,9 +312,9 @@ export default class Statement {
// disallow assignments/updates to imported bindings and namespaces
if ( isAssignment ) {
- const importSpecifier = this.module.imports[ node.name ];
+ const importSpecifier = this.module.locals.lookup( node.name );
- if ( importSpecifier && !scope.contains( node.name ) ) {
+ if ( importSpecifier && importSpecifier.module !== this.module && !scope.contains( node.name ) ) {
const minDepth = importSpecifier.name === '*' ?
2 : // cannot do e.g. `namespace.foo = bar`
1; // cannot do e.g. `foo = bar`, but `foo.bar = bar` is fine
@@ -220,13 +330,25 @@ export default class Statement {
// special case = `export default foo; foo += 1;` - we'll
// need to assign a new variable so that the exported
// value is not updated by the second statement
- if ( this.module.exports.default && depth === 0 && this.module.exports.default.identifier === node.name ) {
+ const def = this.module.exports.lookup( 'default' );
+ if ( def && depth === 0 && def.name === node.name ) {
// but only if this is a) inside a function body or
// b) after the export declaration
- if ( !!scope.parent || node.start > this.module.exports.default.statement.node.start ) {
- this.module.exports.default.isModified = true;
+ if ( !!scope.parent || node.start > def.statement.node.start ) {
+ def.isModified = true;
}
}
+
+ // we track updates/reassignments to variables, to know whether we
+ // need to rewrite it later from `foo` to `exports.foo` to keep
+ // bindings live
+ if (
+ depth === 0 &&
+ writeDepth > 0 &&
+ !scope.contains( node.name )
+ ) {
+ this.reassigns[ node.name ] = true;
+ }
}
// we only care about writes that happen a) at the top level,
@@ -236,7 +358,11 @@ export default class Statement {
// anything (but we still need to call checkForWrites to
// catch illegal reassignments to imported bindings)
if ( writeDepth === 0 && node.type === 'Identifier' ) {
- this.modifies[ node.name ] = true;
+ const id = this.module.locals.lookup( node.name );
+
+ if ( id && id.modifierStatements && !~id.modifierStatements.indexOf( this ) ) {
+ id.modifierStatements.push( this );
+ }
}
};
@@ -262,33 +388,42 @@ export default class Statement {
if ( this.isIncluded ) return; // prevent infinite loops
this.isIncluded = true;
- // `export { name } from './other'` is a special case
- if ( this.isReexportDeclaration ) {
- return this.module.bundle.fetchModule( this.node.source.value, this.module.id )
- .then( otherModule => {
- return sequence( this.node.specifiers, specifier => {
- const reexport = this.module.reexports[ specifier.exported.name ];
+ this.dependantIds.forEach( id => id.mark() );
- reexport.isUsed = true;
- reexport.module = otherModule;
+ // TODO: perhaps these could also be added?
+ keys( this.dependsOn ).forEach( name => {
+ if ( this.defines[ name ] ) return; // TODO maybe exclude from `this.dependsOn` in the first place?
+ this.module.locals.lookup( name ).mark();
+ });
+ }
- return otherModule.isExternal ?
- null :
- otherModule.markExport( specifier.local.name, specifier.exported.name, this.module );
- });
- });
- }
+ markSideEffect () {
+ const statement = this;
+
+ walk( this.node, {
+ enter ( node, parent ) {
+ if ( /Function/.test( node.type ) && !isIife( node, parent ) ) return this.skip();
- const dependencies = Object.keys( this.dependsOn );
+ // 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' ) {
+ statement.mark();
+ }
- return sequence( dependencies, name => {
- if ( this.defines[ name ] ) return; // TODO maybe exclude from `this.dependsOn` in the first place?
- return this.module.mark( name );
+ else if ( node.type in modifierNodes ) {
+ let subject = node[ modifierNodes[ node.type ] ];
+ while ( subject.type === 'MemberExpression' ) subject = subject.object;
+
+ if ( statement.module.bundle.globals.defines( subject.name ) ) statement.mark();
+ }
+ }
});
}
replaceIdentifiers ( magicString, names, bundleExports ) {
- const replacementStack = [ names ];
+ const statement = this;
+
+ const replacementStack = [];
const nameList = keys( names );
let deshadowList = [];
@@ -308,7 +443,7 @@ export default class Statement {
// `this` is undefined at the top level of ES6 modules
if ( node.type === 'ThisExpression' && depth === 0 ) {
- magicString.overwrite( node.start, node.end, 'undefined' );
+ magicString.overwrite( node.start, node.end, 'undefined', true );
}
// special case - variable declarations that need to be rewritten
@@ -317,10 +452,12 @@ export default class Statement {
if ( node.type === 'VariableDeclaration' ) {
// if this contains a single declarator, and it's one that
// needs to be rewritten, we replace the whole lot
- const name = node.declarations[0].id.name;
+ const id = node.declarations[0].id;
+ const name = id.name;
+
if ( node.declarations.length === 1 && bundleExports[ name ] ) {
- magicString.overwrite( node.start, node.declarations[0].id.end, bundleExports[ name ] );
- node.declarations[0].id._skip = true;
+ magicString.overwrite( node.start, id.end, bundleExports[ name ], true );
+ id._skip = true;
}
// otherwise, we insert the `exports.foo = foo` after the declaration
@@ -351,11 +488,6 @@ export default class Statement {
let newNames = blank();
let hasReplacements;
- // special case = function foo ( foo ) {...}
- if ( node.id && names[ node.id.name ] && scope.declarations[ node.id.name ] ) {
- magicString.overwrite( node.id.start, node.id.end, names[ node.id.name ] );
- }
-
keys( names ).forEach( name => {
if ( !scope.declarations[ name ] ) {
newNames[ name ] = names[ name ];
@@ -378,6 +510,18 @@ export default class Statement {
replacementStack.push( newNames );
}
+ if ( node.type === 'MemberExpression' ) {
+ const replacements = statement.namespaceReplacements;
+ for ( let i = 0; i < replacements.length; i += 1 ) {
+ const [ top, id ] = replacements[ i ];
+
+ if ( node === top ) {
+ magicString.overwrite( node.start, node.end, id.name );
+ return this.skip();
+ }
+ }
+ }
+
if ( node.type !== 'Identifier' ) return;
// if there's no replacement, or it's the same, there's nothing more to do
@@ -396,18 +540,19 @@ export default class Statement {
if ( parent.type === 'MemberExpression' && !parent.computed && node !== parent.object ) return;
if ( parent.type === 'Property' && node !== parent.value ) return;
if ( parent.type === 'MethodDefinition' && node === parent.key ) return;
+ if ( parent.type === 'FunctionExpression' ) return;
+ if ( /Function/.test( parent.type ) && ~parent.params.indexOf( node ) ) return;
// TODO others...?
// all other identifiers should be overwritten
- magicString.overwrite( node.start, node.end, name );
+ magicString.overwrite( node.start, node.end, name, true );
},
leave ( node ) {
if ( /^Function/.test( node.type ) ) depth -= 1;
if ( node._scope ) {
- replacementStack.pop();
- names = replacementStack[ replacementStack.length - 1 ];
+ names = replacementStack.pop();
}
}
});
diff --git a/src/ast/Scope.js b/src/ast/Scope.js
index 6cbc6ce..7bd79bf 100644
--- a/src/ast/Scope.js
+++ b/src/ast/Scope.js
@@ -1,10 +1,38 @@
import { blank } from '../utils/object';
-const blockDeclarations = {
- 'const': true,
- 'let': true
+const extractors = {
+ Identifier ( names, param ) {
+ names.push( param.name );
+ },
+
+ ObjectPattern ( names, param ) {
+ param.properties.forEach( prop => {
+ extractors[ prop.key.type ]( names, prop.key );
+ });
+ },
+
+ ArrayPattern ( names, param ) {
+ param.elements.forEach( element => {
+ if ( element ) extractors[ element.type ]( names, element );
+ });
+ },
+
+ RestElement ( names, param ) {
+ extractors[ param.argument.type ]( names, param.argument );
+ },
+
+ AssignmentPattern ( names, param ) {
+ return extractors[ param.left.type ]( names, param.left );
+ }
};
+function extractNames ( param ) {
+ let names = [];
+
+ extractors[ param.type ]( names, param );
+ return names;
+}
+
export default class Scope {
constructor ( options ) {
options = options || {};
@@ -18,26 +46,29 @@ export default class Scope {
if ( options.params ) {
options.params.forEach( param => {
- this.declarations[ param.name ] = param;
+ extractNames( param ).forEach( name => {
+ this.declarations[ name ] = true;
+ });
});
}
}
- addDeclaration ( name, declaration, isVar ) {
- const isBlockDeclaration = declaration.type === 'VariableDeclaration' && blockDeclarations[ declaration.kind ];
-
+ addDeclaration ( declaration, isBlockDeclaration, isVar ) {
if ( !isBlockDeclaration && this.isBlockScope ) {
- // it's a `var` or function declaration, and this
+ // it's a `var` or function node, and this
// is a block scope, so we need to go up
- this.parent.addDeclaration( name, declaration, isVar );
+ this.parent.addDeclaration( declaration, isBlockDeclaration, isVar );
} else {
- this.declarations[ name ] = declaration;
- if ( isVar ) this.varDeclarations.push( name )
+ extractNames( declaration.id ).forEach( name => {
+ this.declarations[ name ] = true;
+ if ( isVar ) this.varDeclarations.push( name );
+ });
}
}
contains ( name ) {
- return !!this.getDeclaration( name );
+ return this.declarations[ name ] ||
+ ( this.parent ? this.parent.contains( name ) : false );
}
findDefiningScope ( name ) {
@@ -51,9 +82,4 @@ export default class Scope {
return null;
}
-
- getDeclaration ( name ) {
- return this.declarations[ name ] ||
- this.parent && this.parent.getDeclaration( name );
- }
}
diff --git a/src/finalisers/cjs.js b/src/finalisers/cjs.js
index 034273d..8e065d2 100644
--- a/src/finalisers/cjs.js
+++ b/src/finalisers/cjs.js
@@ -1,21 +1,19 @@
+import getInteropBlock from './shared/getInteropBlock';
import getExportBlock from './shared/getExportBlock';
export default function cjs ( bundle, magicString, { exportMode }, options ) {
let intro = options.useStrict === false ? `` : `'use strict';\n\n`;
// TODO handle empty imports, once they're supported
- const importBlock = bundle.externalModules
- .map( module => {
- let requireStatement = `var ${module.name} = require('${module.id}');`;
-
- if ( module.needsDefault ) {
- requireStatement += '\n' + ( module.needsNamed ? `var ${module.name}__default = ` : `${module.name} = ` ) +
- `'default' in ${module.name} ? ${module.name}['default'] : ${module.name};`;
- }
-
- return requireStatement;
- })
- .join( '\n' );
+ let importBlock = bundle.externalModules
+ .map( module => `var ${module.name} = require('${module.id}');`)
+ .join('\n');
+
+ const interopBlock = getInteropBlock( bundle );
+
+ if ( interopBlock ) {
+ importBlock += '\n' + interopBlock;
+ }
if ( importBlock ) {
intro += importBlock + '\n\n';
diff --git a/src/finalisers/es6.js b/src/finalisers/es6.js
index 0ed804c..3cdd71c 100644
--- a/src/finalisers/es6.js
+++ b/src/finalisers/es6.js
@@ -1,13 +1,14 @@
-import { blank, keys } from '../utils/object';
+import { keys } from '../utils/object';
-function uniqueNames ( declarations ) {
- let uniques = blank();
+function specifiersFor ( externalModule ) {
+ return keys( externalModule.importedByBundle )
+ .filter( notDefault )
+ .sort()
+ .map( name => {
+ const id = externalModule.exports.lookup( name );
- declarations
- .filter( declaration => !/^(default|\*)$/.test( declaration.name ) )
- .forEach( declaration => uniques[ declaration.name ] = true );
-
- return keys( uniques );
+ return name !== id.name ? `${name} as ${id.name}` : name;
+ });
}
function notDefault ( name ) {
@@ -19,19 +20,18 @@ export default function es6 ( bundle, magicString ) {
.map( module => {
const specifiers = [];
- if ( module.needsDefault ) {
- specifiers.push( module.importedByBundle.filter( declaration =>
- declaration.name === 'default' )[0].localName );
+ const id = module.exports.lookup( 'default' );
+
+ if ( id ) {
+ specifiers.push( id.name );
}
if ( module.needsAll ) {
- specifiers.push( '* as ' + module.importedByBundle.filter( declaration =>
- declaration.name === '*' )[0].localName );
+ specifiers.push( '* as ' + module.name );
}
if ( module.needsNamed ) {
- specifiers.push( '{ ' + uniqueNames( module.importedByBundle )
- .join( ', ' ) + ' }' );
+ specifiers.push( '{ ' + specifiersFor( module ).join( ', ' ) + ' }' );
}
return specifiers.length ?
@@ -47,18 +47,18 @@ export default function es6 ( bundle, magicString ) {
const module = bundle.entryModule;
const specifiers = bundle.toExport.filter( notDefault ).map( name => {
- const canonicalName = bundle.traceExport( module, name );
+ const id = bundle.exports.lookup( name );
- return canonicalName === name ?
+ return id.name === name ?
name :
- `${canonicalName} as ${name}`;
+ `${id.name} as ${name}`;
});
let exportBlock = specifiers.length ? `export { ${specifiers.join(', ')} };` : '';
- const defaultExport = module.exports.default || module.reexports.default;
+ const defaultExport = module.exports.lookup( 'default' );
if ( defaultExport ) {
- exportBlock += `export default ${bundle.traceExport(module,'default')};`;
+ exportBlock += `\nexport default ${ defaultExport.name };`;
}
if ( exportBlock ) {
diff --git a/src/finalisers/shared/getExportBlock.js b/src/finalisers/shared/getExportBlock.js
index 24eeb61..6c53a3d 100644
--- a/src/finalisers/shared/getExportBlock.js
+++ b/src/finalisers/shared/getExportBlock.js
@@ -1,18 +1,24 @@
+function wrapAccess ( id ) {
+ return ( id.originalName !== 'default' && id.module && id.module.isExternal ) ?
+ id.module.name + propertyAccess( id.originalName ) : id.name;
+}
+
+function propertyAccess ( name ) {
+ return name === 'default' ? `['default']` : `.${name}`;
+}
+
export default function getExportBlock ( bundle, exportMode, mechanism = 'return' ) {
if ( exportMode === 'default' ) {
- const defaultExport = bundle.entryModule.exports.default;
-
- const defaultExportName = bundle.entryModule.replacements.default ||
- defaultExport.identifier;
+ const id = bundle.exports.lookup( 'default' );
- return `${mechanism} ${defaultExportName};`;
+ return `${mechanism} ${wrapAccess( id )};`;
}
return bundle.toExport
.map( name => {
- const prop = name === 'default' ? `['default']` : `.${name}`;
- name = bundle.traceExport( bundle.entryModule, name );
- return `exports${prop} = ${name};`;
+ const id = bundle.exports.lookup( name );
+
+ return `exports${propertyAccess( name )} = ${wrapAccess( id )};`;
})
.join( '\n' );
}
diff --git a/src/finalisers/shared/getInteropBlock.js b/src/finalisers/shared/getInteropBlock.js
index 8060e00..684af72 100644
--- a/src/finalisers/shared/getInteropBlock.js
+++ b/src/finalisers/shared/getInteropBlock.js
@@ -1,11 +1,12 @@
export default function getInteropBlock ( bundle ) {
return bundle.externalModules
.map( module => {
- return module.needsDefault ?
- ( module.needsNamed ?
- `var ${module.name}__default = 'default' in ${module.name} ? ${module.name}['default'] : ${module.name};` :
- `${module.name} = 'default' in ${module.name} ? ${module.name}['default'] : ${module.name};` ) :
- null;
+ const def = module.exports.lookup( 'default' );
+
+ if ( !def ) return;
+
+ return ( module.needsNamed ? 'var ' : '' ) +
+ `${def.name} = 'default' in ${module.name} ? ${module.name}['default'] : ${module.name};`;
})
.filter( Boolean )
.join( '\n' );
diff --git a/src/utils/getExportMode.js b/src/utils/getExportMode.js
index 86ed4d4..439c33c 100644
--- a/src/utils/getExportMode.js
+++ b/src/utils/getExportMode.js
@@ -5,7 +5,7 @@ function badExports ( option, keys ) {
}
export default function getExportMode ( bundle, exportMode ) {
- const exportKeys = keys( bundle.entryModule.exports ).concat( keys( bundle.entryModule.reexports ) );
+ const exportKeys = keys( bundle.entryModule.exports.names );
if ( exportMode === 'default' ) {
if ( exportKeys.length !== 1 || exportKeys[0] !== 'default' ) {
diff --git a/src/utils/makeLegalIdentifier.js b/src/utils/makeLegalIdentifier.js
index 7e0a846..1dd4454 100644
--- a/src/utils/makeLegalIdentifier.js
+++ b/src/utils/makeLegalIdentifier.js
@@ -8,7 +8,10 @@ reservedWords.concat( builtins ).forEach( word => blacklisted[ word ] = true );
export default function makeLegalIdentifier ( str ) {
- str = str.replace( /[^$_a-zA-Z0-9]/g, '_' );
+ str = str
+ .replace( /-(\w)/g, ( _, letter ) => letter.toUpperCase() )
+ .replace( /[^$_a-zA-Z0-9]/g, '_' );
+
if ( /\d/.test( str[0] ) || blacklisted[ str ] ) str = `_${str}`;
return str;
diff --git a/src/utils/path.js b/src/utils/path.js
index b5db7c1..4f55ba8 100644
--- a/src/utils/path.js
+++ b/src/utils/path.js
@@ -1,6 +1,6 @@
// TODO does this all work on windows?
-export const absolutePath = /^(?:\/|(?:[A-Za-z]:)?\\)/;
+export const absolutePath = /^(?:\/|(?:[A-Za-z]:)?[\\|\/])/;
export function isAbsolute ( path ) {
return absolutePath.test( path );
diff --git a/src/utils/promise.js b/src/utils/promise.js
deleted file mode 100644
index e37489d..0000000
--- a/src/utils/promise.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import { Promise } from 'sander';
-
-export function sequence ( arr, callback ) {
- const len = arr.length;
- let results = new Array( len );
-
- let promise = Promise.resolve();
-
- function next ( i ) {
- return promise
- .then( () => callback( arr[i], i ) )
- .then( result => results[i] = result );
- }
-
- let i;
-
- for ( i = 0; i < len; i += 1 ) {
- promise = next( i );
- }
-
- return promise.then( () => results );
-}
-
-
-export function first ( arr, fail, callback ) {
- const len = arr.length;
-
- let promise = Promise.reject( fail );
-
- function next ( i ) {
- return promise
- .catch(() => callback( arr[i], i ));
- }
-
- let i;
-
- for ( i = 0; i < len; i += 1 ) {
- promise = next( i );
- }
-
- return promise;
-}
diff --git a/src/utils/resolveId.js b/src/utils/resolveId.js
index 1af9378..3090001 100644
--- a/src/utils/resolveId.js
+++ b/src/utils/resolveId.js
@@ -10,15 +10,6 @@ function dirExists ( dir ) {
}
}
-function fileExists ( dir ) {
- try {
- readFileSync( dir );
- return true;
- } catch ( err ) {
- return false;
- }
-}
-
export function defaultResolver ( importee, importer, options ) {
// absolute paths are left untouched
if ( isAbsolute( importee ) ) return importee;
@@ -47,7 +38,7 @@ export function defaultExternalResolver ( id, importer ) {
// `foo` should use jsnext:main, but `foo/src/bar` shouldn't
const parts = id.split( /[\/\\]/ );
- while ( dir !== root ) {
+ while ( dir !== root && dir !== '.' ) {
const modulePath = resolve( dir, 'node_modules', parts[0] );
if ( dirExists( modulePath ) ) {
@@ -58,7 +49,6 @@ export function defaultExternalResolver ( id, importer ) {
// `foo`
const pkgPath = resolve( modulePath, 'package.json' );
- let pkgJson;
let pkg;
try {
diff --git a/test/cli/external-modules/main.js b/test/cli/external-modules/main.js
index 03f1b74..abaa454 100644
--- a/test/cli/external-modules/main.js
+++ b/test/cli/external-modules/main.js
@@ -1,5 +1,5 @@
-import { relative } from 'path';
+import { relative, normalize } from 'path';
import { format } from 'util';
assert.equal( format( 'it %s', 'works' ), 'it works' );
-assert.equal( relative( 'a/b/c', 'a/c/b' ), '../../c/b' );
+assert.equal( relative( 'a/b/c', 'a/c/b' ), normalize('../../c/b') );
diff --git a/test/form/export-all-from-internal/_config.js b/test/form/export-all-from-internal/_config.js
new file mode 100644
index 0000000..83871ca
--- /dev/null
+++ b/test/form/export-all-from-internal/_config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ description: 'should be able to export * from the bundle',
+ options: {
+ moduleName: 'exposedInternals'
+ }
+};
diff --git a/test/form/export-all-from-internal/_expected/amd.js b/test/form/export-all-from-internal/_expected/amd.js
new file mode 100644
index 0000000..89f5652
--- /dev/null
+++ b/test/form/export-all-from-internal/_expected/amd.js
@@ -0,0 +1,11 @@
+define(['exports'], function (exports) { 'use strict';
+
+ const a = 1;
+ const b = 2;
+ var internal = 42;
+
+ exports.a = a;
+ exports.b = b;
+ exports['default'] = internal;
+
+});
diff --git a/test/form/export-all-from-internal/_expected/cjs.js b/test/form/export-all-from-internal/_expected/cjs.js
new file mode 100644
index 0000000..cadb160
--- /dev/null
+++ b/test/form/export-all-from-internal/_expected/cjs.js
@@ -0,0 +1,9 @@
+'use strict';
+
+const a = 1;
+const b = 2;
+var internal = 42;
+
+exports.a = a;
+exports.b = b;
+exports['default'] = internal;
diff --git a/test/form/export-all-from-internal/_expected/es6.js b/test/form/export-all-from-internal/_expected/es6.js
new file mode 100644
index 0000000..d68e7f9
--- /dev/null
+++ b/test/form/export-all-from-internal/_expected/es6.js
@@ -0,0 +1,6 @@
+const a = 1;
+const b = 2;
+var internal = 42;
+
+export { a, b };
+export default internal;
diff --git a/test/form/export-all-from-internal/_expected/iife.js b/test/form/export-all-from-internal/_expected/iife.js
new file mode 100644
index 0000000..c093623
--- /dev/null
+++ b/test/form/export-all-from-internal/_expected/iife.js
@@ -0,0 +1,11 @@
+(function (exports) { 'use strict';
+
+ const a = 1;
+ const b = 2;
+ var internal = 42;
+
+ exports.a = a;
+ exports.b = b;
+ exports['default'] = internal;
+
+})((this.exposedInternals = {}));
diff --git a/test/form/export-all-from-internal/_expected/umd.js b/test/form/export-all-from-internal/_expected/umd.js
new file mode 100644
index 0000000..45a93fa
--- /dev/null
+++ b/test/form/export-all-from-internal/_expected/umd.js
@@ -0,0 +1,15 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ factory((global.exposedInternals = {}));
+}(this, function (exports) { 'use strict';
+
+ const a = 1;
+ const b = 2;
+ var internal = 42;
+
+ exports.a = a;
+ exports.b = b;
+ exports['default'] = internal;
+
+}));
diff --git a/test/form/export-all-from-internal/internal.js b/test/form/export-all-from-internal/internal.js
new file mode 100644
index 0000000..8f791b6
--- /dev/null
+++ b/test/form/export-all-from-internal/internal.js
@@ -0,0 +1,3 @@
+export const a = 1;
+export const b = 2;
+export default 42;
diff --git a/test/form/export-all-from-internal/main.js b/test/form/export-all-from-internal/main.js
new file mode 100644
index 0000000..676c1ba
--- /dev/null
+++ b/test/form/export-all-from-internal/main.js
@@ -0,0 +1 @@
+export * from './internal.js';
diff --git a/test/form/exported-empty-vars/_config.js b/test/form/exported-empty-vars/_config.js
deleted file mode 100644
index fa0287c..0000000
--- a/test/form/exported-empty-vars/_config.js
+++ /dev/null
@@ -1,6 +0,0 @@
-module.exports = {
- description: 'removes empty var declarations that are exported',
- options: {
- moduleName: 'myBundle'
- }
-};
diff --git a/test/form/exported-empty-vars/_expected/amd.js b/test/form/exported-empty-vars/_expected/amd.js
deleted file mode 100644
index c0f1472..0000000
--- a/test/form/exported-empty-vars/_expected/amd.js
+++ /dev/null
@@ -1,8 +0,0 @@
-define(['exports'], function (exports) { 'use strict';
-
- exports.foo = 42;
-
- exports.bar = 43;
- exports.baz = 44;
-
-});
diff --git a/test/form/exported-empty-vars/_expected/cjs.js b/test/form/exported-empty-vars/_expected/cjs.js
deleted file mode 100644
index 4011bd7..0000000
--- a/test/form/exported-empty-vars/_expected/cjs.js
+++ /dev/null
@@ -1,6 +0,0 @@
-'use strict';
-
-exports.foo = 42;
-
-exports.bar = 43;
-exports.baz = 44;
diff --git a/test/form/exported-empty-vars/_expected/es6.js b/test/form/exported-empty-vars/_expected/es6.js
deleted file mode 100644
index 4b76753..0000000
--- a/test/form/exported-empty-vars/_expected/es6.js
+++ /dev/null
@@ -1,9 +0,0 @@
-var foo;
-foo = 42;
-
-var bar;
-var baz;
-bar = 43;
-baz = 44;
-
-export { foo, bar, baz };
diff --git a/test/form/exported-empty-vars/_expected/iife.js b/test/form/exported-empty-vars/_expected/iife.js
deleted file mode 100644
index 63c56b2..0000000
--- a/test/form/exported-empty-vars/_expected/iife.js
+++ /dev/null
@@ -1,8 +0,0 @@
-(function (exports) { 'use strict';
-
- exports.foo = 42;
-
- exports.bar = 43;
- exports.baz = 44;
-
-})((this.myBundle = {}));
diff --git a/test/form/exported-empty-vars/bar.js b/test/form/exported-empty-vars/bar.js
deleted file mode 100644
index b8818da..0000000
--- a/test/form/exported-empty-vars/bar.js
+++ /dev/null
@@ -1,6 +0,0 @@
-var bar, baz;
-
-bar = 43;
-baz = 44;
-
-export { bar, baz };
diff --git a/test/form/exported-empty-vars/foo.js b/test/form/exported-empty-vars/foo.js
deleted file mode 100644
index 2c71d30..0000000
--- a/test/form/exported-empty-vars/foo.js
+++ /dev/null
@@ -1,2 +0,0 @@
-export var foo;
-foo = 42;
diff --git a/test/form/exported-empty-vars/main.js b/test/form/exported-empty-vars/main.js
deleted file mode 100644
index 21ee721..0000000
--- a/test/form/exported-empty-vars/main.js
+++ /dev/null
@@ -1,2 +0,0 @@
-export { foo } from './foo';
-export { bar, baz } from './bar';
diff --git a/test/form/exports-at-end-if-possible/_config.js b/test/form/exports-at-end-if-possible/_config.js
new file mode 100644
index 0000000..a898bc7
--- /dev/null
+++ b/test/form/exports-at-end-if-possible/_config.js
@@ -0,0 +1,7 @@
+module.exports = {
+ description: 'exports variables at end, if possible',
+ options: {
+ moduleName: 'myBundle'
+ },
+ // solo: true
+};
diff --git a/test/form/exports-at-end-if-possible/_expected/amd.js b/test/form/exports-at-end-if-possible/_expected/amd.js
new file mode 100644
index 0000000..2612901
--- /dev/null
+++ b/test/form/exports-at-end-if-possible/_expected/amd.js
@@ -0,0 +1,11 @@
+define(['exports'], function (exports) { 'use strict';
+
+ var FOO = 'foo';
+
+ console.log( FOO );
+ console.log( FOO );
+ console.log( FOO );
+
+ exports.FOO = FOO;
+
+});
diff --git a/test/form/exports-at-end-if-possible/_expected/cjs.js b/test/form/exports-at-end-if-possible/_expected/cjs.js
new file mode 100644
index 0000000..57d55d3
--- /dev/null
+++ b/test/form/exports-at-end-if-possible/_expected/cjs.js
@@ -0,0 +1,9 @@
+'use strict';
+
+var FOO = 'foo';
+
+console.log( FOO );
+console.log( FOO );
+console.log( FOO );
+
+exports.FOO = FOO;
diff --git a/test/form/exports-at-end-if-possible/_expected/es6.js b/test/form/exports-at-end-if-possible/_expected/es6.js
new file mode 100644
index 0000000..b42b4e4
--- /dev/null
+++ b/test/form/exports-at-end-if-possible/_expected/es6.js
@@ -0,0 +1,7 @@
+var FOO = 'foo';
+
+console.log( FOO );
+console.log( FOO );
+console.log( FOO );
+
+export { FOO };
diff --git a/test/form/exports-at-end-if-possible/_expected/iife.js b/test/form/exports-at-end-if-possible/_expected/iife.js
new file mode 100644
index 0000000..d1b29c8
--- /dev/null
+++ b/test/form/exports-at-end-if-possible/_expected/iife.js
@@ -0,0 +1,11 @@
+(function (exports) { 'use strict';
+
+ var FOO = 'foo';
+
+ console.log( FOO );
+ console.log( FOO );
+ console.log( FOO );
+
+ exports.FOO = FOO;
+
+})((this.myBundle = {}));
diff --git a/test/form/exported-empty-vars/_expected/umd.js b/test/form/exports-at-end-if-possible/_expected/umd.js
similarity index 72%
rename from test/form/exported-empty-vars/_expected/umd.js
rename to test/form/exports-at-end-if-possible/_expected/umd.js
index 9cde4bd..f30f365 100644
--- a/test/form/exported-empty-vars/_expected/umd.js
+++ b/test/form/exports-at-end-if-possible/_expected/umd.js
@@ -4,9 +4,12 @@
factory((global.myBundle = {}));
}(this, function (exports) { 'use strict';
- exports.foo = 42;
+ var FOO = 'foo';
- exports.bar = 43;
- exports.baz = 44;
+ console.log( FOO );
+ console.log( FOO );
+ console.log( FOO );
+
+ exports.FOO = FOO;
}));
diff --git a/test/form/exports-at-end-if-possible/main.js b/test/form/exports-at-end-if-possible/main.js
new file mode 100644
index 0000000..ee6a75a
--- /dev/null
+++ b/test/form/exports-at-end-if-possible/main.js
@@ -0,0 +1,5 @@
+export var FOO = 'foo';
+
+console.log( FOO );
+console.log( FOO );
+console.log( FOO );
diff --git a/test/form/external-imports/_expected/cjs.js b/test/form/external-imports/_expected/cjs.js
index ac9f79d..b6f5d31 100644
--- a/test/form/external-imports/_expected/cjs.js
+++ b/test/form/external-imports/_expected/cjs.js
@@ -1,10 +1,10 @@
'use strict';
var factory = require('factory');
-factory = 'default' in factory ? factory['default'] : factory;
var baz = require('baz');
var containers = require('shipping-port');
var alphabet = require('alphabet');
+factory = 'default' in factory ? factory['default'] : factory;
var alphabet__default = 'default' in alphabet ? alphabet['default'] : alphabet;
factory( null );
diff --git a/test/form/external-imports/_expected/es6.js b/test/form/external-imports/_expected/es6.js
index cd41111..4d05b46 100644
--- a/test/form/external-imports/_expected/es6.js
+++ b/test/form/external-imports/_expected/es6.js
@@ -1,10 +1,10 @@
import factory from 'factory';
import { bar, foo } from 'baz';
import * as containers from 'shipping-port';
-import alphabet, { a } from 'alphabet';
+import alphabet__default, { a } from 'alphabet';
factory( null );
foo( bar );
containers.forEach( console.log, console );
console.log( a );
-console.log( alphabet.length );
+console.log( alphabet__default.length );
diff --git a/test/form/internal-conflict-resolution/_expected/amd.js b/test/form/internal-conflict-resolution/_expected/amd.js
index f2b3c4a..62ff65b 100644
--- a/test/form/internal-conflict-resolution/_expected/amd.js
+++ b/test/form/internal-conflict-resolution/_expected/amd.js
@@ -1,15 +1,15 @@
define(function () { 'use strict';
- var _bar = 42;
+ var bar = 42;
function foo () {
- return _bar;
+ return bar;
}
- function bar () {
+ function _bar () {
alert( foo() );
}
- bar();
+ _bar();
});
diff --git a/test/form/internal-conflict-resolution/_expected/cjs.js b/test/form/internal-conflict-resolution/_expected/cjs.js
index 74ae2f8..cbc7af9 100644
--- a/test/form/internal-conflict-resolution/_expected/cjs.js
+++ b/test/form/internal-conflict-resolution/_expected/cjs.js
@@ -1,13 +1,13 @@
'use strict';
-var _bar = 42;
+var bar = 42;
function foo () {
- return _bar;
+ return bar;
}
-function bar () {
+function _bar () {
alert( foo() );
}
-bar();
+_bar();
diff --git a/test/form/internal-conflict-resolution/_expected/es6.js b/test/form/internal-conflict-resolution/_expected/es6.js
index e1fc1f7..7bb41e3 100644
--- a/test/form/internal-conflict-resolution/_expected/es6.js
+++ b/test/form/internal-conflict-resolution/_expected/es6.js
@@ -1,11 +1,11 @@
-var _bar = 42;
+var bar = 42;
function foo () {
- return _bar;
+ return bar;
}
-function bar () {
+function _bar () {
alert( foo() );
}
-bar();
+_bar();
diff --git a/test/form/internal-conflict-resolution/_expected/iife.js b/test/form/internal-conflict-resolution/_expected/iife.js
index efe296f..76687ff 100644
--- a/test/form/internal-conflict-resolution/_expected/iife.js
+++ b/test/form/internal-conflict-resolution/_expected/iife.js
@@ -1,15 +1,15 @@
(function () { 'use strict';
- var _bar = 42;
+ var bar = 42;
function foo () {
- return _bar;
+ return bar;
}
- function bar () {
+ function _bar () {
alert( foo() );
}
- bar();
+ _bar();
})();
diff --git a/test/form/internal-conflict-resolution/_expected/umd.js b/test/form/internal-conflict-resolution/_expected/umd.js
index 979f229..d9d801b 100644
--- a/test/form/internal-conflict-resolution/_expected/umd.js
+++ b/test/form/internal-conflict-resolution/_expected/umd.js
@@ -4,16 +4,16 @@
factory();
}(this, function () { 'use strict';
- var _bar = 42;
+ var bar = 42;
function foo () {
- return _bar;
+ return bar;
}
- function bar () {
+ function _bar () {
alert( foo() );
}
- bar();
+ _bar();
}));
diff --git a/test/form/multiple-exports/_expected/amd.js b/test/form/multiple-exports/_expected/amd.js
index 5460f4f..2ebff3b 100644
--- a/test/form/multiple-exports/_expected/amd.js
+++ b/test/form/multiple-exports/_expected/amd.js
@@ -1,6 +1,9 @@
define(['exports'], function (exports) { 'use strict';
- exports.foo = 1;
- exports.bar = 2;
+ var foo = 1;
+ var bar = 2;
+
+ exports.foo = foo;
+ exports.bar = bar;
});
diff --git a/test/form/multiple-exports/_expected/cjs.js b/test/form/multiple-exports/_expected/cjs.js
index 0bbfe60..1968b8b 100644
--- a/test/form/multiple-exports/_expected/cjs.js
+++ b/test/form/multiple-exports/_expected/cjs.js
@@ -1,4 +1,7 @@
'use strict';
-exports.foo = 1;
-exports.bar = 2;
+var foo = 1;
+var bar = 2;
+
+exports.foo = foo;
+exports.bar = bar;
diff --git a/test/form/multiple-exports/_expected/iife.js b/test/form/multiple-exports/_expected/iife.js
index 09461cb..8ed8290 100644
--- a/test/form/multiple-exports/_expected/iife.js
+++ b/test/form/multiple-exports/_expected/iife.js
@@ -1,6 +1,9 @@
(function (exports) { 'use strict';
- exports.foo = 1;
- exports.bar = 2;
+ var foo = 1;
+ var bar = 2;
+
+ exports.foo = foo;
+ exports.bar = bar;
})((this.myBundle = {}));
diff --git a/test/form/multiple-exports/_expected/umd.js b/test/form/multiple-exports/_expected/umd.js
index 2fd0eb7..687b75c 100644
--- a/test/form/multiple-exports/_expected/umd.js
+++ b/test/form/multiple-exports/_expected/umd.js
@@ -4,7 +4,10 @@
factory((global.myBundle = {}));
}(this, function (exports) { 'use strict';
- exports.foo = 1;
- exports.bar = 2;
+ var foo = 1;
+ var bar = 2;
+
+ exports.foo = foo;
+ exports.bar = bar;
}));
diff --git a/test/form/namespace-optimization/_config.js b/test/form/namespace-optimization/_config.js
new file mode 100644
index 0000000..ec88243
--- /dev/null
+++ b/test/form/namespace-optimization/_config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ description: 'it does static lookup optimization of internal namespaces'
+};
diff --git a/test/form/namespace-optimization/_expected/amd.js b/test/form/namespace-optimization/_expected/amd.js
new file mode 100644
index 0000000..a244c47
--- /dev/null
+++ b/test/form/namespace-optimization/_expected/amd.js
@@ -0,0 +1,7 @@
+define(function () { 'use strict';
+
+ function a () {}
+
+ a();
+
+});
diff --git a/test/form/namespace-optimization/_expected/cjs.js b/test/form/namespace-optimization/_expected/cjs.js
new file mode 100644
index 0000000..b52a7e5
--- /dev/null
+++ b/test/form/namespace-optimization/_expected/cjs.js
@@ -0,0 +1,5 @@
+'use strict';
+
+function a () {}
+
+a();
diff --git a/test/form/namespace-optimization/_expected/es6.js b/test/form/namespace-optimization/_expected/es6.js
new file mode 100644
index 0000000..8bee044
--- /dev/null
+++ b/test/form/namespace-optimization/_expected/es6.js
@@ -0,0 +1,3 @@
+function a () {}
+
+a();
diff --git a/test/form/namespace-optimization/_expected/iife.js b/test/form/namespace-optimization/_expected/iife.js
new file mode 100644
index 0000000..aac8ff9
--- /dev/null
+++ b/test/form/namespace-optimization/_expected/iife.js
@@ -0,0 +1,7 @@
+(function () { 'use strict';
+
+ function a () {}
+
+ a();
+
+})();
diff --git a/test/form/namespace-optimization/_expected/umd.js b/test/form/namespace-optimization/_expected/umd.js
new file mode 100644
index 0000000..38f7835
--- /dev/null
+++ b/test/form/namespace-optimization/_expected/umd.js
@@ -0,0 +1,11 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ factory();
+}(this, function () { 'use strict';
+
+ function a () {}
+
+ a();
+
+}));
diff --git a/test/form/namespace-optimization/bar.js b/test/form/namespace-optimization/bar.js
new file mode 100644
index 0000000..aa96676
--- /dev/null
+++ b/test/form/namespace-optimization/bar.js
@@ -0,0 +1,3 @@
+import * as quux from './quux';
+
+export { quux };
diff --git a/test/form/namespace-optimization/foo.js b/test/form/namespace-optimization/foo.js
new file mode 100644
index 0000000..42a93ae
--- /dev/null
+++ b/test/form/namespace-optimization/foo.js
@@ -0,0 +1,3 @@
+import * as bar from './bar';
+
+export { bar };
diff --git a/test/form/namespace-optimization/main.js b/test/form/namespace-optimization/main.js
new file mode 100644
index 0000000..e902244
--- /dev/null
+++ b/test/form/namespace-optimization/main.js
@@ -0,0 +1,3 @@
+import * as foo from './foo';
+
+foo.bar.quux.a();
diff --git a/test/form/namespace-optimization/quux.js b/test/form/namespace-optimization/quux.js
new file mode 100644
index 0000000..103a9f0
--- /dev/null
+++ b/test/form/namespace-optimization/quux.js
@@ -0,0 +1 @@
+export function a () {}
diff --git a/test/form/preserves-comments-after-imports/_expected/amd.js b/test/form/preserves-comments-after-imports/_expected/amd.js
index fd4591b..82a0a52 100644
--- a/test/form/preserves-comments-after-imports/_expected/amd.js
+++ b/test/form/preserves-comments-after-imports/_expected/amd.js
@@ -4,6 +4,8 @@ define(['exports'], function (exports) { 'use strict';
var number = 5;
/** A comment for obj */
- exports.obj = { number };
+ var obj = { number };
+
+ exports.obj = obj;
});
diff --git a/test/form/preserves-comments-after-imports/_expected/cjs.js b/test/form/preserves-comments-after-imports/_expected/cjs.js
index 86e64c1..8b15cd1 100644
--- a/test/form/preserves-comments-after-imports/_expected/cjs.js
+++ b/test/form/preserves-comments-after-imports/_expected/cjs.js
@@ -4,4 +4,6 @@
var number = 5;
/** A comment for obj */
-exports.obj = { number };
+var obj = { number };
+
+exports.obj = obj;
diff --git a/test/form/preserves-comments-after-imports/_expected/iife.js b/test/form/preserves-comments-after-imports/_expected/iife.js
index 02f5255..9da8252 100644
--- a/test/form/preserves-comments-after-imports/_expected/iife.js
+++ b/test/form/preserves-comments-after-imports/_expected/iife.js
@@ -4,6 +4,8 @@
var number = 5;
/** A comment for obj */
- exports.obj = { number };
+ var obj = { number };
+
+ exports.obj = obj;
})((this.myBundle = {}));
diff --git a/test/form/preserves-comments-after-imports/_expected/umd.js b/test/form/preserves-comments-after-imports/_expected/umd.js
index 6993070..96fec25 100644
--- a/test/form/preserves-comments-after-imports/_expected/umd.js
+++ b/test/form/preserves-comments-after-imports/_expected/umd.js
@@ -8,6 +8,8 @@
var number = 5;
/** A comment for obj */
- exports.obj = { number };
+ var obj = { number };
+
+ exports.obj = obj;
}));
diff --git a/test/form/sourcemaps-inline/_expected/amd.js b/test/form/sourcemaps-inline/_expected/amd.js
index 9861ddd..e6aaf48 100644
--- a/test/form/sourcemaps-inline/_expected/amd.js
+++ b/test/form/sourcemaps-inline/_expected/amd.js
@@ -14,4 +14,4 @@ define(function () { 'use strict';
bar();
});
-//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW1kLmpzIiwic291cmNlcyI6WyIuLi9mb28uanMiLCIuLi9iYXIuanMiLCIuLi9tYWluLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGZvbyAoKSB7XG5cdGNvbnNvbGUubG9nKCAnaGVsbG8gZnJvbSBmb28uanMnICk7XG59XG4iLCJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBiYXIgKCkge1xuXHRjb25zb2xlLmxvZyggJ2hlbGxvIGZyb20gYmFyLmpzJyApO1xufVxuIiwiaW1wb3J0IGZvbyBmcm9tICcuL2Zvbyc7XG5pbXBvcnQgYmFyIGZyb20gJy4vYmFyJztcblxuY29uc29sZS5sb2coICdoZWxsbyBmcm9tIG1haW4uanMnICk7XG5cbmZvbygpO1xuYmFyKCk7XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxDQUFlLFNBQVMsR0FBRyxJQUFJO0FBQS9CLENBQ0EsQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLG1CQUFtQixFQUFFO0FBRG5DLENBRUE7O0FDRkEsQ0FBZSxTQUFTLEdBQUcsSUFBSTtBQUEvQixDQUNBLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxtQkFBbUIsRUFBRTtBQURuQyxDQUVBOztBQ0ZBLENBR0EsT0FBTyxDQUFDLEdBQUcsRUFBRSxvQkFBb0IsRUFBRTs7QUFIbkMsQ0FLQSxHQUFHLEVBQUU7QUFMTCxDQU1BLEdBQUcsRUFBRSw7OyJ9
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW1kLmpzIiwic291cmNlcyI6WyIuLi9mb28uanMiLCIuLi9iYXIuanMiLCIuLi9tYWluLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGZvbyAoKSB7XG5cdGNvbnNvbGUubG9nKCAnaGVsbG8gZnJvbSBmb28uanMnICk7XG59XG4iLCJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBiYXIgKCkge1xuXHRjb25zb2xlLmxvZyggJ2hlbGxvIGZyb20gYmFyLmpzJyApO1xufVxuIiwiaW1wb3J0IGZvbyBmcm9tICcuL2Zvbyc7XG5pbXBvcnQgYmFyIGZyb20gJy4vYmFyJztcblxuY29uc29sZS5sb2coICdoZWxsbyBmcm9tIG1haW4uanMnICk7XG5cbmZvbygpO1xuYmFyKCk7XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Q0FBZSxTQUFTLEdBQUcsSUFBSTtFQUM5QixPQUFPLENBQUMsR0FBRyxFQUFFLG1CQUFtQixFQUFFOzs7Q0NEcEIsU0FBUyxHQUFHLElBQUk7RUFDOUIsT0FBTyxDQUFDLEdBQUcsRUFBRSxtQkFBbUIsRUFBRTs7O0NDRW5DLE9BQU8sQ0FBQyxHQUFHLEVBQUUsb0JBQW9CLEVBQUU7O0NBRW5DLEdBQUcsRUFBRTtDQUNMLEdBQUcsRUFBRSw7OyJ9
diff --git a/test/form/sourcemaps-inline/_expected/cjs.js b/test/form/sourcemaps-inline/_expected/cjs.js
index e71035b..1f995ee 100644
--- a/test/form/sourcemaps-inline/_expected/cjs.js
+++ b/test/form/sourcemaps-inline/_expected/cjs.js
@@ -12,4 +12,4 @@ console.log( 'hello from main.js' );
foo();
bar();
-//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2pzLmpzIiwic291cmNlcyI6WyIuLi9mb28uanMiLCIuLi9iYXIuanMiLCIuLi9tYWluLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGZvbyAoKSB7XG5cdGNvbnNvbGUubG9nKCAnaGVsbG8gZnJvbSBmb28uanMnICk7XG59XG4iLCJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBiYXIgKCkge1xuXHRjb25zb2xlLmxvZyggJ2hlbGxvIGZyb20gYmFyLmpzJyApO1xufVxuIiwiaW1wb3J0IGZvbyBmcm9tICcuL2Zvbyc7XG5pbXBvcnQgYmFyIGZyb20gJy4vYmFyJztcblxuY29uc29sZS5sb2coICdoZWxsbyBmcm9tIG1haW4uanMnICk7XG5cbmZvbygpO1xuYmFyKCk7XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBZSxTQUFTLEdBQUcsSUFBSTtBQUMvQixDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsbUJBQW1CLEVBQUU7QUFDbkM7O0FDRmUsU0FBUyxHQUFHLElBQUk7QUFDL0IsQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLG1CQUFtQixFQUFFO0FBQ25DOztBQ0NBLE9BQU8sQ0FBQyxHQUFHLEVBQUUsb0JBQW9CLEVBQUU7O0FBRW5DLEdBQUcsRUFBRTtBQUNMLEdBQUcsRUFBRSJ9
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2pzLmpzIiwic291cmNlcyI6WyIuLi9mb28uanMiLCIuLi9iYXIuanMiLCIuLi9tYWluLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGZvbyAoKSB7XG5cdGNvbnNvbGUubG9nKCAnaGVsbG8gZnJvbSBmb28uanMnICk7XG59XG4iLCJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBiYXIgKCkge1xuXHRjb25zb2xlLmxvZyggJ2hlbGxvIGZyb20gYmFyLmpzJyApO1xufVxuIiwiaW1wb3J0IGZvbyBmcm9tICcuL2Zvbyc7XG5pbXBvcnQgYmFyIGZyb20gJy4vYmFyJztcblxuY29uc29sZS5sb2coICdoZWxsbyBmcm9tIG1haW4uanMnICk7XG5cbmZvbygpO1xuYmFyKCk7XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBZSxTQUFTLEdBQUcsSUFBSTtDQUM5QixPQUFPLENBQUMsR0FBRyxFQUFFLG1CQUFtQixFQUFFOzs7QUNEcEIsU0FBUyxHQUFHLElBQUk7Q0FDOUIsT0FBTyxDQUFDLEdBQUcsRUFBRSxtQkFBbUIsRUFBRTs7O0FDRW5DLE9BQU8sQ0FBQyxHQUFHLEVBQUUsb0JBQW9CLEVBQUU7O0FBRW5DLEdBQUcsRUFBRTtBQUNMLEdBQUcsRUFBRSJ9
diff --git a/test/form/sourcemaps-inline/_expected/es6.js b/test/form/sourcemaps-inline/_expected/es6.js
index 9258b3e..3374dfb 100644
--- a/test/form/sourcemaps-inline/_expected/es6.js
+++ b/test/form/sourcemaps-inline/_expected/es6.js
@@ -10,4 +10,4 @@ console.log( 'hello from main.js' );
foo();
bar();
-//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXM2LmpzIiwic291cmNlcyI6WyIuLi9mb28uanMiLCIuLi9iYXIuanMiLCIuLi9tYWluLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGZvbyAoKSB7XG5cdGNvbnNvbGUubG9nKCAnaGVsbG8gZnJvbSBmb28uanMnICk7XG59XG4iLCJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBiYXIgKCkge1xuXHRjb25zb2xlLmxvZyggJ2hlbGxvIGZyb20gYmFyLmpzJyApO1xufVxuIiwiaW1wb3J0IGZvbyBmcm9tICcuL2Zvbyc7XG5pbXBvcnQgYmFyIGZyb20gJy4vYmFyJztcblxuY29uc29sZS5sb2coICdoZWxsbyBmcm9tIG1haW4uanMnICk7XG5cbmZvbygpO1xuYmFyKCk7XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQWUsU0FBUyxHQUFHLElBQUk7QUFDL0IsQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLG1CQUFtQixFQUFFO0FBQ25DOztBQ0ZlLFNBQVMsR0FBRyxJQUFJO0FBQy9CLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxtQkFBbUIsRUFBRTtBQUNuQzs7QUNDQSxPQUFPLENBQUMsR0FBRyxFQUFFLG9CQUFvQixFQUFFOztBQUVuQyxHQUFHLEVBQUU7QUFDTCxHQUFHLEVBQUUifQ==
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXM2LmpzIiwic291cmNlcyI6WyIuLi9mb28uanMiLCIuLi9iYXIuanMiLCIuLi9tYWluLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGZvbyAoKSB7XG5cdGNvbnNvbGUubG9nKCAnaGVsbG8gZnJvbSBmb28uanMnICk7XG59XG4iLCJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBiYXIgKCkge1xuXHRjb25zb2xlLmxvZyggJ2hlbGxvIGZyb20gYmFyLmpzJyApO1xufVxuIiwiaW1wb3J0IGZvbyBmcm9tICcuL2Zvbyc7XG5pbXBvcnQgYmFyIGZyb20gJy4vYmFyJztcblxuY29uc29sZS5sb2coICdoZWxsbyBmcm9tIG1haW4uanMnICk7XG5cbmZvbygpO1xuYmFyKCk7XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQWUsU0FBUyxHQUFHLElBQUk7Q0FDOUIsT0FBTyxDQUFDLEdBQUcsRUFBRSxtQkFBbUIsRUFBRTs7O0FDRHBCLFNBQVMsR0FBRyxJQUFJO0NBQzlCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsbUJBQW1CLEVBQUU7OztBQ0VuQyxPQUFPLENBQUMsR0FBRyxFQUFFLG9CQUFvQixFQUFFOztBQUVuQyxHQUFHLEVBQUU7QUFDTCxHQUFHLEVBQUUifQ==
diff --git a/test/form/sourcemaps-inline/_expected/iife.js b/test/form/sourcemaps-inline/_expected/iife.js
index 50dbeb3..f38c6ef 100644
--- a/test/form/sourcemaps-inline/_expected/iife.js
+++ b/test/form/sourcemaps-inline/_expected/iife.js
@@ -14,4 +14,4 @@
bar();
})();
-//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaWlmZS5qcyIsInNvdXJjZXMiOlsiLi4vZm9vLmpzIiwiLi4vYmFyLmpzIiwiLi4vbWFpbi5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBmb28gKCkge1xuXHRjb25zb2xlLmxvZyggJ2hlbGxvIGZyb20gZm9vLmpzJyApO1xufVxuIiwiZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gYmFyICgpIHtcblx0Y29uc29sZS5sb2coICdoZWxsbyBmcm9tIGJhci5qcycgKTtcbn1cbiIsImltcG9ydCBmb28gZnJvbSAnLi9mb28nO1xuaW1wb3J0IGJhciBmcm9tICcuL2Jhcic7XG5cbmNvbnNvbGUubG9nKCAnaGVsbG8gZnJvbSBtYWluLmpzJyApO1xuXG5mb28oKTtcbmJhcigpO1xuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsQ0FBZSxTQUFTLEdBQUcsSUFBSTtBQUEvQixDQUNBLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxtQkFBbUIsRUFBRTtBQURuQyxDQUVBOztBQ0ZBLENBQWUsU0FBUyxHQUFHLElBQUk7QUFBL0IsQ0FDQSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsbUJBQW1CLEVBQUU7QUFEbkMsQ0FFQTs7QUNGQSxDQUdBLE9BQU8sQ0FBQyxHQUFHLEVBQUUsb0JBQW9CLEVBQUU7O0FBSG5DLENBS0EsR0FBRyxFQUFFO0FBTEwsQ0FNQSxHQUFHLEVBQUUsOzsifQ==
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaWlmZS5qcyIsInNvdXJjZXMiOlsiLi4vZm9vLmpzIiwiLi4vYmFyLmpzIiwiLi4vbWFpbi5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBmb28gKCkge1xuXHRjb25zb2xlLmxvZyggJ2hlbGxvIGZyb20gZm9vLmpzJyApO1xufVxuIiwiZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gYmFyICgpIHtcblx0Y29uc29sZS5sb2coICdoZWxsbyBmcm9tIGJhci5qcycgKTtcbn1cbiIsImltcG9ydCBmb28gZnJvbSAnLi9mb28nO1xuaW1wb3J0IGJhciBmcm9tICcuL2Jhcic7XG5cbmNvbnNvbGUubG9nKCAnaGVsbG8gZnJvbSBtYWluLmpzJyApO1xuXG5mb28oKTtcbmJhcigpO1xuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0NBQWUsU0FBUyxHQUFHLElBQUk7RUFDOUIsT0FBTyxDQUFDLEdBQUcsRUFBRSxtQkFBbUIsRUFBRTs7O0NDRHBCLFNBQVMsR0FBRyxJQUFJO0VBQzlCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsbUJBQW1CLEVBQUU7OztDQ0VuQyxPQUFPLENBQUMsR0FBRyxFQUFFLG9CQUFvQixFQUFFOztDQUVuQyxHQUFHLEVBQUU7Q0FDTCxHQUFHLEVBQUUsOzsifQ==
diff --git a/test/form/sourcemaps-inline/_expected/umd.js b/test/form/sourcemaps-inline/_expected/umd.js
index e213a79..a63cd7a 100644
--- a/test/form/sourcemaps-inline/_expected/umd.js
+++ b/test/form/sourcemaps-inline/_expected/umd.js
@@ -18,4 +18,4 @@
bar();
}));
-//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW1kLmpzIiwic291cmNlcyI6WyIuLi9mb28uanMiLCIuLi9iYXIuanMiLCIuLi9tYWluLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGZvbyAoKSB7XG5cdGNvbnNvbGUubG9nKCAnaGVsbG8gZnJvbSBmb28uanMnICk7XG59XG4iLCJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBiYXIgKCkge1xuXHRjb25zb2xlLmxvZyggJ2hlbGxvIGZyb20gYmFyLmpzJyApO1xufVxuIiwiaW1wb3J0IGZvbyBmcm9tICcuL2Zvbyc7XG5pbXBvcnQgYmFyIGZyb20gJy4vYmFyJztcblxuY29uc29sZS5sb2coICdoZWxsbyBmcm9tIG1haW4uanMnICk7XG5cbmZvbygpO1xuYmFyKCk7XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsQ0FBZSxTQUFTLEdBQUcsSUFBSTtBQUEvQixDQUNBLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxtQkFBbUIsRUFBRTtBQURuQyxDQUVBOztBQ0ZBLENBQWUsU0FBUyxHQUFHLElBQUk7QUFBL0IsQ0FDQSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsbUJBQW1CLEVBQUU7QUFEbkMsQ0FFQTs7QUNGQSxDQUdBLE9BQU8sQ0FBQyxHQUFHLEVBQUUsb0JBQW9CLEVBQUU7O0FBSG5DLENBS0EsR0FBRyxFQUFFO0FBTEwsQ0FNQSxHQUFHLEVBQUUsOzsifQ==
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW1kLmpzIiwic291cmNlcyI6WyIuLi9mb28uanMiLCIuLi9iYXIuanMiLCIuLi9tYWluLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGZvbyAoKSB7XG5cdGNvbnNvbGUubG9nKCAnaGVsbG8gZnJvbSBmb28uanMnICk7XG59XG4iLCJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBiYXIgKCkge1xuXHRjb25zb2xlLmxvZyggJ2hlbGxvIGZyb20gYmFyLmpzJyApO1xufVxuIiwiaW1wb3J0IGZvbyBmcm9tICcuL2Zvbyc7XG5pbXBvcnQgYmFyIGZyb20gJy4vYmFyJztcblxuY29uc29sZS5sb2coICdoZWxsbyBmcm9tIG1haW4uanMnICk7XG5cbmZvbygpO1xuYmFyKCk7XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0NBQWUsU0FBUyxHQUFHLElBQUk7RUFDOUIsT0FBTyxDQUFDLEdBQUcsRUFBRSxtQkFBbUIsRUFBRTs7O0NDRHBCLFNBQVMsR0FBRyxJQUFJO0VBQzlCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsbUJBQW1CLEVBQUU7OztDQ0VuQyxPQUFPLENBQUMsR0FBRyxFQUFFLG9CQUFvQixFQUFFOztDQUVuQyxHQUFHLEVBQUU7Q0FDTCxHQUFHLEVBQUUsOzsifQ==
diff --git a/test/form/sourcemaps/_expected/amd.js.map b/test/form/sourcemaps/_expected/amd.js.map
index f596c11..2b2f6e2 100644
--- a/test/form/sourcemaps/_expected/amd.js.map
+++ b/test/form/sourcemaps/_expected/amd.js.map
@@ -1 +1 @@
-{"version":3,"file":"amd.js","sources":["../foo.js","../bar.js","../main.js"],"sourcesContent":["export default function foo () {\n\tconsole.log( 'hello from foo.js' );\n}\n","export default function bar () {\n\tconsole.log( 'hello from bar.js' );\n}\n","import foo from './foo';\nimport bar from './bar';\n\nconsole.log( 'hello from main.js' );\n\nfoo();\nbar();\n"],"names":[],"mappings":";;AAAA,CAAe,SAAS,GAAG,IAAI;AAA/B,CACA,CAAC,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;AADnC,CAEA;;ACFA,CAAe,SAAS,GAAG,IAAI;AAA/B,CACA,CAAC,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;AADnC,CAEA;;ACFA,CAGA,OAAO,CAAC,GAAG,EAAE,oBAAoB,EAAE;;AAHnC,CAKA,GAAG,EAAE;AALL,CAMA,GAAG,EAAE,;;"}
+{"version":3,"file":"amd.js","sources":["../foo.js","../bar.js","../main.js"],"sourcesContent":["export default function foo () {\n\tconsole.log( 'hello from foo.js' );\n}\n","export default function bar () {\n\tconsole.log( 'hello from bar.js' );\n}\n","import foo from './foo';\nimport bar from './bar';\n\nconsole.log( 'hello from main.js' );\n\nfoo();\nbar();\n"],"names":[],"mappings":";;CAAe,SAAS,GAAG,IAAI;EAC9B,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;;;CCDpB,SAAS,GAAG,IAAI;EAC9B,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;;;CCEnC,OAAO,CAAC,GAAG,EAAE,oBAAoB,EAAE;;CAEnC,GAAG,EAAE;CACL,GAAG,EAAE,;;"}
diff --git a/test/form/sourcemaps/_expected/cjs.js.map b/test/form/sourcemaps/_expected/cjs.js.map
index 99d230e..452d9f3 100644
--- a/test/form/sourcemaps/_expected/cjs.js.map
+++ b/test/form/sourcemaps/_expected/cjs.js.map
@@ -1 +1 @@
-{"version":3,"file":"cjs.js","sources":["../foo.js","../bar.js","../main.js"],"sourcesContent":["export default function foo () {\n\tconsole.log( 'hello from foo.js' );\n}\n","export default function bar () {\n\tconsole.log( 'hello from bar.js' );\n}\n","import foo from './foo';\nimport bar from './bar';\n\nconsole.log( 'hello from main.js' );\n\nfoo();\nbar();\n"],"names":[],"mappings":";;AAAe,SAAS,GAAG,IAAI;AAC/B,CAAC,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;AACnC;;ACFe,SAAS,GAAG,IAAI;AAC/B,CAAC,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;AACnC;;ACCA,OAAO,CAAC,GAAG,EAAE,oBAAoB,EAAE;;AAEnC,GAAG,EAAE;AACL,GAAG,EAAE"}
+{"version":3,"file":"cjs.js","sources":["../foo.js","../bar.js","../main.js"],"sourcesContent":["export default function foo () {\n\tconsole.log( 'hello from foo.js' );\n}\n","export default function bar () {\n\tconsole.log( 'hello from bar.js' );\n}\n","import foo from './foo';\nimport bar from './bar';\n\nconsole.log( 'hello from main.js' );\n\nfoo();\nbar();\n"],"names":[],"mappings":";;AAAe,SAAS,GAAG,IAAI;CAC9B,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;;;ACDpB,SAAS,GAAG,IAAI;CAC9B,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;;;ACEnC,OAAO,CAAC,GAAG,EAAE,oBAAoB,EAAE;;AAEnC,GAAG,EAAE;AACL,GAAG,EAAE"}
diff --git a/test/form/sourcemaps/_expected/es6.js.map b/test/form/sourcemaps/_expected/es6.js.map
index ea62cf4..6489227 100644
--- a/test/form/sourcemaps/_expected/es6.js.map
+++ b/test/form/sourcemaps/_expected/es6.js.map
@@ -1 +1 @@
-{"version":3,"file":"es6.js","sources":["../foo.js","../bar.js","../main.js"],"sourcesContent":["export default function foo () {\n\tconsole.log( 'hello from foo.js' );\n}\n","export default function bar () {\n\tconsole.log( 'hello from bar.js' );\n}\n","import foo from './foo';\nimport bar from './bar';\n\nconsole.log( 'hello from main.js' );\n\nfoo();\nbar();\n"],"names":[],"mappings":"AAAe,SAAS,GAAG,IAAI;AAC/B,CAAC,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;AACnC;;ACFe,SAAS,GAAG,IAAI;AAC/B,CAAC,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;AACnC;;ACCA,OAAO,CAAC,GAAG,EAAE,oBAAoB,EAAE;;AAEnC,GAAG,EAAE;AACL,GAAG,EAAE"}
+{"version":3,"file":"es6.js","sources":["../foo.js","../bar.js","../main.js"],"sourcesContent":["export default function foo () {\n\tconsole.log( 'hello from foo.js' );\n}\n","export default function bar () {\n\tconsole.log( 'hello from bar.js' );\n}\n","import foo from './foo';\nimport bar from './bar';\n\nconsole.log( 'hello from main.js' );\n\nfoo();\nbar();\n"],"names":[],"mappings":"AAAe,SAAS,GAAG,IAAI;CAC9B,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;;;ACDpB,SAAS,GAAG,IAAI;CAC9B,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;;;ACEnC,OAAO,CAAC,GAAG,EAAE,oBAAoB,EAAE;;AAEnC,GAAG,EAAE;AACL,GAAG,EAAE"}
diff --git a/test/form/sourcemaps/_expected/iife.js.map b/test/form/sourcemaps/_expected/iife.js.map
index 8584fd5..6b91b81 100644
--- a/test/form/sourcemaps/_expected/iife.js.map
+++ b/test/form/sourcemaps/_expected/iife.js.map
@@ -1 +1 @@
-{"version":3,"file":"iife.js","sources":["../foo.js","../bar.js","../main.js"],"sourcesContent":["export default function foo () {\n\tconsole.log( 'hello from foo.js' );\n}\n","export default function bar () {\n\tconsole.log( 'hello from bar.js' );\n}\n","import foo from './foo';\nimport bar from './bar';\n\nconsole.log( 'hello from main.js' );\n\nfoo();\nbar();\n"],"names":[],"mappings":";;AAAA,CAAe,SAAS,GAAG,IAAI;AAA/B,CACA,CAAC,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;AADnC,CAEA;;ACFA,CAAe,SAAS,GAAG,IAAI;AAA/B,CACA,CAAC,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;AADnC,CAEA;;ACFA,CAGA,OAAO,CAAC,GAAG,EAAE,oBAAoB,EAAE;;AAHnC,CAKA,GAAG,EAAE;AALL,CAMA,GAAG,EAAE,;;"}
+{"version":3,"file":"iife.js","sources":["../foo.js","../bar.js","../main.js"],"sourcesContent":["export default function foo () {\n\tconsole.log( 'hello from foo.js' );\n}\n","export default function bar () {\n\tconsole.log( 'hello from bar.js' );\n}\n","import foo from './foo';\nimport bar from './bar';\n\nconsole.log( 'hello from main.js' );\n\nfoo();\nbar();\n"],"names":[],"mappings":";;CAAe,SAAS,GAAG,IAAI;EAC9B,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;;;CCDpB,SAAS,GAAG,IAAI;EAC9B,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;;;CCEnC,OAAO,CAAC,GAAG,EAAE,oBAAoB,EAAE;;CAEnC,GAAG,EAAE;CACL,GAAG,EAAE,;;"}
diff --git a/test/form/sourcemaps/_expected/umd.js.map b/test/form/sourcemaps/_expected/umd.js.map
index 396895e..08e949a 100644
--- a/test/form/sourcemaps/_expected/umd.js.map
+++ b/test/form/sourcemaps/_expected/umd.js.map
@@ -1 +1 @@
-{"version":3,"file":"umd.js","sources":["../foo.js","../bar.js","../main.js"],"sourcesContent":["export default function foo () {\n\tconsole.log( 'hello from foo.js' );\n}\n","export default function bar () {\n\tconsole.log( 'hello from bar.js' );\n}\n","import foo from './foo';\nimport bar from './bar';\n\nconsole.log( 'hello from main.js' );\n\nfoo();\nbar();\n"],"names":[],"mappings":";;;;;;AAAA,CAAe,SAAS,GAAG,IAAI;AAA/B,CACA,CAAC,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;AADnC,CAEA;;ACFA,CAAe,SAAS,GAAG,IAAI;AAA/B,CACA,CAAC,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;AADnC,CAEA;;ACFA,CAGA,OAAO,CAAC,GAAG,EAAE,oBAAoB,EAAE;;AAHnC,CAKA,GAAG,EAAE;AALL,CAMA,GAAG,EAAE,;;"}
+{"version":3,"file":"umd.js","sources":["../foo.js","../bar.js","../main.js"],"sourcesContent":["export default function foo () {\n\tconsole.log( 'hello from foo.js' );\n}\n","export default function bar () {\n\tconsole.log( 'hello from bar.js' );\n}\n","import foo from './foo';\nimport bar from './bar';\n\nconsole.log( 'hello from main.js' );\n\nfoo();\nbar();\n"],"names":[],"mappings":";;;;;;CAAe,SAAS,GAAG,IAAI;EAC9B,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;;;CCDpB,SAAS,GAAG,IAAI;EAC9B,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE;;;CCEnC,OAAO,CAAC,GAAG,EAAE,oBAAoB,EAAE;;CAEnC,GAAG,EAAE;CACL,GAAG,EAAE,;;"}
diff --git a/test/form/unused-side-effect/_config.js b/test/form/unused-side-effect/_config.js
new file mode 100644
index 0000000..0431077
--- /dev/null
+++ b/test/form/unused-side-effect/_config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ description: 'side-effects to non-globals are not blindly included'
+};
diff --git a/test/form/unused-side-effect/_expected/amd.js b/test/form/unused-side-effect/_expected/amd.js
new file mode 100644
index 0000000..c82bf4f
--- /dev/null
+++ b/test/form/unused-side-effect/_expected/amd.js
@@ -0,0 +1,7 @@
+define(function () { 'use strict';
+
+ var foo = 42;
+
+ assert.equal( foo, 42 );
+
+});
diff --git a/test/form/unused-side-effect/_expected/cjs.js b/test/form/unused-side-effect/_expected/cjs.js
new file mode 100644
index 0000000..0d2573f
--- /dev/null
+++ b/test/form/unused-side-effect/_expected/cjs.js
@@ -0,0 +1,5 @@
+'use strict';
+
+var foo = 42;
+
+assert.equal( foo, 42 );
diff --git a/test/form/unused-side-effect/_expected/es6.js b/test/form/unused-side-effect/_expected/es6.js
new file mode 100644
index 0000000..e50ecda
--- /dev/null
+++ b/test/form/unused-side-effect/_expected/es6.js
@@ -0,0 +1,3 @@
+var foo = 42;
+
+assert.equal( foo, 42 );
diff --git a/test/form/unused-side-effect/_expected/iife.js b/test/form/unused-side-effect/_expected/iife.js
new file mode 100644
index 0000000..a3f7fc8
--- /dev/null
+++ b/test/form/unused-side-effect/_expected/iife.js
@@ -0,0 +1,7 @@
+(function () { 'use strict';
+
+ var foo = 42;
+
+ assert.equal( foo, 42 );
+
+})();
diff --git a/test/form/unused-side-effect/_expected/umd.js b/test/form/unused-side-effect/_expected/umd.js
new file mode 100644
index 0000000..f96fd30
--- /dev/null
+++ b/test/form/unused-side-effect/_expected/umd.js
@@ -0,0 +1,11 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ factory();
+}(this, function () { 'use strict';
+
+ var foo = 42;
+
+ assert.equal( foo, 42 );
+
+}));
diff --git a/test/form/unused-side-effect/foo.js b/test/form/unused-side-effect/foo.js
new file mode 100644
index 0000000..7953f6e
--- /dev/null
+++ b/test/form/unused-side-effect/foo.js
@@ -0,0 +1,13 @@
+var uid = 0;
+uid = 1;
+uid += 1;
+uid++;
+
+// ensure identifiers aren't treated as globals just because
+// var declaration hasn't been encountered yet...
+uid2 = 1;
+uid2 += 1;
+uid2++;
+var uid2;
+
+export var foo = 42;
diff --git a/test/form/unused-side-effect/main.js b/test/form/unused-side-effect/main.js
new file mode 100644
index 0000000..ef12cae
--- /dev/null
+++ b/test/form/unused-side-effect/main.js
@@ -0,0 +1,2 @@
+import { foo } from './foo';
+assert.equal( foo, 42 );
diff --git a/test/function/allows-external-modules-from-nested-module/main.js b/test/function/allows-external-modules-from-nested-module/main.js
index 8bacdc9..bdd4f85 100644
--- a/test/function/allows-external-modules-from-nested-module/main.js
+++ b/test/function/allows-external-modules-from-nested-module/main.js
@@ -1,8 +1,8 @@
-import { relative } from 'path';
+import { relative, normalize } from 'path';
import foo from './foo';
var path = 'foo/bar/baz';
var path2 = 'foo/baz/bar';
-assert.equal( relative( path, path2 ), '../../baz/bar' );
-assert.equal( foo, '../../c/b' );
\ No newline at end of file
+assert.equal( relative( path, path2 ), normalize('../../baz/bar') );
+assert.equal( foo, normalize('../../c/b') );
\ No newline at end of file
diff --git a/test/function/assignment-patterns/_config.js b/test/function/assignment-patterns/_config.js
new file mode 100644
index 0000000..d4afdc3
--- /dev/null
+++ b/test/function/assignment-patterns/_config.js
@@ -0,0 +1,4 @@
+module.exports = {
+ description: 'allows reassigments to default parameters that shadow imports',
+ babel: true
+};
diff --git a/test/function/assignment-patterns/main.js b/test/function/assignment-patterns/main.js
new file mode 100644
index 0000000..844c908
--- /dev/null
+++ b/test/function/assignment-patterns/main.js
@@ -0,0 +1,21 @@
+import { bar, baz, x, items, p, q, r, s } from './other';
+
+function foo ( bar = 1, { baz } = { baz: 2 }, [[[,x = 3] = []] = []] = [], ...items ) {
+ bar += 1;
+ baz += 1;
+ x += 1;
+
+ let { p, q } = { p: 4, q: 5 };
+ let [ r, s ] = [ 6, 7 ];
+
+ p++;
+ q += 1;
+ r = 7;
+ s = 6;
+
+ return bar + baz + x + items.length + p + q + r + s;
+}
+
+assert.equal( foo(), 33 );
+assert.equal( foo( 2 ), 34 );
+assert.equal( foo( 2, { baz: 3 }, [[[99,10]]], 'a', 'b', 'c' ), 45 );
diff --git a/test/function/assignment-patterns/other.js b/test/function/assignment-patterns/other.js
new file mode 100644
index 0000000..840584c
--- /dev/null
+++ b/test/function/assignment-patterns/other.js
@@ -0,0 +1,8 @@
+export const bar = 'bar';
+export const baz = 'baz';
+export const x = 'x';
+export const items = 'items';
+export const p = 'p';
+export const q = 'q';
+export const r = 'r';
+export const s = 's';
diff --git a/test/function/assignment-to-exports/_config.js b/test/function/assignment-to-exports/_config.js
index c958adb..fac0cb3 100644
--- a/test/function/assignment-to-exports/_config.js
+++ b/test/function/assignment-to-exports/_config.js
@@ -6,5 +6,6 @@ module.exports = {
assert.equal( exports.count, 0 );
exports.incr();
assert.equal( exports.count, 1 );
- }
+ },
+ // solo: true
};
diff --git a/test/function/custom-path-resolver-async/_config.js b/test/function/custom-path-resolver-async/_config.js
index 287f536..c336d67 100644
--- a/test/function/custom-path-resolver-async/_config.js
+++ b/test/function/custom-path-resolver-async/_config.js
@@ -8,7 +8,7 @@ module.exports = {
var Promise = require( 'sander' ).Promise;
var resolved;
- if ( importee === path.resolve( __dirname, 'main.js' ) ) return importee;
+ if ( path.normalize(importee) === path.resolve( __dirname, 'main.js' ) ) return importee;
if ( importee === 'foo' ) {
resolved = path.resolve( __dirname, 'bar.js' );
diff --git a/test/function/custom-path-resolver-sync/_config.js b/test/function/custom-path-resolver-sync/_config.js
index 9a755e5..ee993e1 100644
--- a/test/function/custom-path-resolver-sync/_config.js
+++ b/test/function/custom-path-resolver-sync/_config.js
@@ -5,7 +5,7 @@ module.exports = {
description: 'uses a custom path resolver (synchronous)',
options: {
resolveId: function ( importee, importer ) {
- if ( importee === path.resolve( __dirname, 'main.js' ) ) return importee;
+ if ( path.normalize(importee) === path.resolve( __dirname, 'main.js' ) ) return importee;
if ( importee === 'foo' ) return path.resolve( __dirname, 'bar.js' );
return false;
diff --git a/test/function/duplicate-import-fails/_config.js b/test/function/duplicate-import-fails/_config.js
index 4280c53..4083690 100644
--- a/test/function/duplicate-import-fails/_config.js
+++ b/test/function/duplicate-import-fails/_config.js
@@ -4,7 +4,7 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows duplicate imports',
error: function ( err ) {
- assert.equal( err.file, path.resolve( __dirname, 'main.js' ) );
+ assert.equal( path.normalize(err.file), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { line: 2, column: 9 });
assert.ok( /Duplicated import/.test( err.message ) );
}
diff --git a/test/function/duplicate-import-specifier-fails/_config.js b/test/function/duplicate-import-specifier-fails/_config.js
index b935a69..e3957b5 100644
--- a/test/function/duplicate-import-specifier-fails/_config.js
+++ b/test/function/duplicate-import-specifier-fails/_config.js
@@ -4,7 +4,7 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows duplicate import specifiers',
error: function ( err ) {
- assert.equal( err.file, path.resolve( __dirname, 'main.js' ) );
+ assert.equal( path.normalize(err.file), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { line: 1, column: 12 });
assert.ok( /Duplicated import/.test( err.message ) );
}
diff --git a/test/function/dynamic-namespace-lookup/_config.js b/test/function/dynamic-namespace-lookup/_config.js
new file mode 100644
index 0000000..3c2fe54
--- /dev/null
+++ b/test/function/dynamic-namespace-lookup/_config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ description: 'does namespace optimization when possible, but not for dynamic lookups'
+};
diff --git a/test/function/dynamic-namespace-lookup/foo.js b/test/function/dynamic-namespace-lookup/foo.js
new file mode 100644
index 0000000..a727aee
--- /dev/null
+++ b/test/function/dynamic-namespace-lookup/foo.js
@@ -0,0 +1,2 @@
+export var bar = 'bar';
+export var baz = 'baz';
diff --git a/test/function/dynamic-namespace-lookup/main.js b/test/function/dynamic-namespace-lookup/main.js
new file mode 100644
index 0000000..ee1ce10
--- /dev/null
+++ b/test/function/dynamic-namespace-lookup/main.js
@@ -0,0 +1,8 @@
+import * as foo from './foo';
+
+var bar = 'baz';
+
+assert.equal( foo.bar, 'bar' );
+assert.equal( foo.baz, 'baz' );
+
+assert.equal( foo[ bar ], 'baz' );
diff --git a/test/function/export-all/_config.js b/test/function/export-all/_config.js
index 24639c6..8060316 100644
--- a/test/function/export-all/_config.js
+++ b/test/function/export-all/_config.js
@@ -1,5 +1,3 @@
-var assert = require( 'assert' );
-
module.exports = {
description: 'allows export *'
};
diff --git a/test/function/export-from-default-renamed/_config.js b/test/function/export-from-default-renamed/_config.js
new file mode 100644
index 0000000..2c9c52a
--- /dev/null
+++ b/test/function/export-from-default-renamed/_config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ description: 'export from works with multiple renamed default exports'
+};
diff --git a/test/function/export-from-default-renamed/a.js b/test/function/export-from-default-renamed/a.js
new file mode 100644
index 0000000..0ed5c30
--- /dev/null
+++ b/test/function/export-from-default-renamed/a.js
@@ -0,0 +1 @@
+export default 'a';
diff --git a/test/function/export-from-default-renamed/b.js b/test/function/export-from-default-renamed/b.js
new file mode 100644
index 0000000..a68ac28
--- /dev/null
+++ b/test/function/export-from-default-renamed/b.js
@@ -0,0 +1 @@
+export default 'b';
diff --git a/test/function/export-from-default-renamed/foo.js b/test/function/export-from-default-renamed/foo.js
new file mode 100644
index 0000000..c69fcf2
--- /dev/null
+++ b/test/function/export-from-default-renamed/foo.js
@@ -0,0 +1,2 @@
+export { default as a } from './a';
+export { default as b } from './b';
diff --git a/test/function/export-from-default-renamed/main.js b/test/function/export-from-default-renamed/main.js
new file mode 100644
index 0000000..3a9fb68
--- /dev/null
+++ b/test/function/export-from-default-renamed/main.js
@@ -0,0 +1,3 @@
+import { a, b } from './foo';
+assert.equal( a, 'a' );
+assert.equal( b, 'b' );
diff --git a/test/function/export-not-at-top-level-fails/_config.js b/test/function/export-not-at-top-level-fails/_config.js
index c68ca42..2dadeab 100644
--- a/test/function/export-not-at-top-level-fails/_config.js
+++ b/test/function/export-not-at-top-level-fails/_config.js
@@ -4,7 +4,7 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows non-top-level exports',
error: function ( err ) {
- assert.equal( err.file, path.resolve( __dirname, 'main.js' ) );
+ assert.equal( path.normalize(err.file), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { line: 2, column: 2 });
assert.ok( /may only appear at the top level/.test( err.message ) );
}
diff --git a/test/function/functions-renamed-correctly/_config.js b/test/function/functions-renamed-correctly/_config.js
new file mode 100644
index 0000000..b0719a0
--- /dev/null
+++ b/test/function/functions-renamed-correctly/_config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ description: 'renames function expression IDs correctly'
+};
diff --git a/test/function/functions-renamed-correctly/after.js b/test/function/functions-renamed-correctly/after.js
new file mode 100644
index 0000000..549d2db
--- /dev/null
+++ b/test/function/functions-renamed-correctly/after.js
@@ -0,0 +1,5 @@
+function x () {
+ return 'after';
+}
+
+export { x as after };
diff --git a/test/function/functions-renamed-correctly/before.js b/test/function/functions-renamed-correctly/before.js
new file mode 100644
index 0000000..56fba5f
--- /dev/null
+++ b/test/function/functions-renamed-correctly/before.js
@@ -0,0 +1,5 @@
+function x () {
+ return 'before';
+}
+
+export { x as before };
diff --git a/test/function/functions-renamed-correctly/factorial.js b/test/function/functions-renamed-correctly/factorial.js
new file mode 100644
index 0000000..9f5e752
--- /dev/null
+++ b/test/function/functions-renamed-correctly/factorial.js
@@ -0,0 +1,7 @@
+var x = (function () {
+ return function x ( num ) {
+ return num <= 2 ? num : num * x( num - 1 );
+ };
+})();
+
+export { x };
diff --git a/test/function/functions-renamed-correctly/main.js b/test/function/functions-renamed-correctly/main.js
new file mode 100644
index 0000000..d2e616a
--- /dev/null
+++ b/test/function/functions-renamed-correctly/main.js
@@ -0,0 +1,7 @@
+import { before } from './before';
+import { x } from './factorial';
+import { after } from './after';
+
+before(); // before and after ensure x is renamed
+assert.equal( x( 5 ), 120 );
+after();
diff --git a/test/function/globally-called-modifying-function/_config.js b/test/function/globally-called-modifying-function/_config.js
new file mode 100644
index 0000000..86b2f62
--- /dev/null
+++ b/test/function/globally-called-modifying-function/_config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ description: 'globally called function should be included if it modifies an exported value (#112)'
+};
diff --git a/test/function/globally-called-modifying-function/main.js b/test/function/globally-called-modifying-function/main.js
new file mode 100644
index 0000000..3b2061e
--- /dev/null
+++ b/test/function/globally-called-modifying-function/main.js
@@ -0,0 +1,3 @@
+import value from './module.js';
+
+assert.equal( value, 3 );
diff --git a/test/function/globally-called-modifying-function/module.js b/test/function/globally-called-modifying-function/module.js
new file mode 100644
index 0000000..fc50ee5
--- /dev/null
+++ b/test/function/globally-called-modifying-function/module.js
@@ -0,0 +1,17 @@
+var value = 1;
+
+function change () {
+ value = 2;
+}
+
+function changeAgain () {
+ value += 1;
+}
+
+change();
+
+if ( true ) {
+ changeAgain();
+}
+
+export default value;
diff --git a/test/function/import-default-from-external/main.js b/test/function/import-default-from-external/main.js
index 5d5cd6d..aacce04 100644
--- a/test/function/import-default-from-external/main.js
+++ b/test/function/import-default-from-external/main.js
@@ -4,4 +4,4 @@ import path from 'path';
var path1 = 'foo/bar/baz';
var path2 = 'foo/baz/bar';
-assert.equal( path.relative( path1, path2 ), '../../baz/bar' );
\ No newline at end of file
+assert.equal( path.relative( path1, path2 ), path.normalize('../../baz/bar') );
\ No newline at end of file
diff --git a/test/function/import-named-from-external/main.js b/test/function/import-named-from-external/main.js
index 49027f6..1030a82 100644
--- a/test/function/import-named-from-external/main.js
+++ b/test/function/import-named-from-external/main.js
@@ -1,6 +1,6 @@
-import { relative } from 'path';
+import { relative, normalize } from 'path';
var path = 'foo/bar/baz';
var path2 = 'foo/baz/bar';
-assert.equal( relative( path, path2 ), '../../baz/bar' );
\ No newline at end of file
+assert.equal( relative( path, path2 ), normalize('../../baz/bar') );
\ No newline at end of file
diff --git a/test/function/import-namespace-from-external-module-renamed/main.js b/test/function/import-namespace-from-external-module-renamed/main.js
index 2208cd4..947231e 100644
--- a/test/function/import-namespace-from-external-module-renamed/main.js
+++ b/test/function/import-namespace-from-external-module-renamed/main.js
@@ -3,4 +3,4 @@ import * as node_path from 'path';
var path1 = 'foo/bar/baz';
var path2 = 'foo/baz/bar';
-assert.equal( node_path.relative( path1, path2 ), '../../baz/bar' );
+assert.equal( node_path.relative( path1, path2 ), node_path.normalize('../../baz/bar') );
diff --git a/test/function/import-namespace-from-external-module/main.js b/test/function/import-namespace-from-external-module/main.js
index 000a219..10f9d36 100644
--- a/test/function/import-namespace-from-external-module/main.js
+++ b/test/function/import-namespace-from-external-module/main.js
@@ -3,4 +3,4 @@ import * as path from 'path';
var path1 = 'foo/bar/baz';
var path2 = 'foo/baz/bar';
-assert.equal( path.relative( path1, path2 ), '../../baz/bar' );
\ No newline at end of file
+assert.equal( path.relative( path1, path2 ), path.normalize('../../baz/bar') );
\ No newline at end of file
diff --git a/test/function/import-not-at-top-level-fails/_config.js b/test/function/import-not-at-top-level-fails/_config.js
index c75e986..4f873aa 100644
--- a/test/function/import-not-at-top-level-fails/_config.js
+++ b/test/function/import-not-at-top-level-fails/_config.js
@@ -4,7 +4,7 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows non-top-level imports',
error: function ( err ) {
- assert.equal( err.file, path.resolve( __dirname, 'main.js' ) );
+ assert.equal( path.normalize(err.file), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { line: 2, column: 2 });
assert.ok( /may only appear at the top level/.test( err.message ) );
}
diff --git a/test/function/imports-are-deconflicted-b/main.js b/test/function/imports-are-deconflicted-b/main.js
index fa81bd0..cf917fc 100644
--- a/test/function/imports-are-deconflicted-b/main.js
+++ b/test/function/imports-are-deconflicted-b/main.js
@@ -1,5 +1,6 @@
import foo from './foo';
import bar from './bar';
+import { normalize } from 'path';
assert.equal( foo, 'foo' );
-assert.equal( bar(), '../../baz/bar' );
+assert.equal( bar(), normalize('../../baz/bar') );
diff --git a/test/function/imports-are-deconflicted/main.js b/test/function/imports-are-deconflicted/main.js
index fa81bd0..cf917fc 100644
--- a/test/function/imports-are-deconflicted/main.js
+++ b/test/function/imports-are-deconflicted/main.js
@@ -1,5 +1,6 @@
import foo from './foo';
import bar from './bar';
+import { normalize } from 'path';
assert.equal( foo, 'foo' );
-assert.equal( bar(), '../../baz/bar' );
+assert.equal( bar(), normalize('../../baz/bar') );
diff --git a/test/function/modify-assumed-global/_config.js b/test/function/modify-assumed-global/_config.js
new file mode 100644
index 0000000..ee89a52
--- /dev/null
+++ b/test/function/modify-assumed-global/_config.js
@@ -0,0 +1,14 @@
+var assert = require( 'assert' );
+
+var Math = {};
+
+module.exports = {
+ description: 'side-effects to assumed globals are included',
+ context: {
+ Math: Math
+ },
+ exports: function ( exports ) {
+ assert.equal( Math.square( 3 ), 9 );
+ assert.equal( Math.cube( 3 ), 27 );
+ }
+};
diff --git a/test/function/modify-assumed-global/main.js b/test/function/modify-assumed-global/main.js
new file mode 100644
index 0000000..cc4f217
--- /dev/null
+++ b/test/function/modify-assumed-global/main.js
@@ -0,0 +1,3 @@
+import { square } from './math';
+
+assert.equal( square( 2 ), 4 );
diff --git a/test/function/modify-assumed-global/math.js b/test/function/modify-assumed-global/math.js
new file mode 100644
index 0000000..469df59
--- /dev/null
+++ b/test/function/modify-assumed-global/math.js
@@ -0,0 +1,15 @@
+function square ( x ) {
+ return x * x;
+}
+
+function cube ( x ) {
+ return x * x * x;
+}
+
+Math.square = square;
+
+if ( true ) {
+ Math.cube = cube;
+}
+
+export { square };
diff --git a/test/function/namespace-reassign-import-fails/_config.js b/test/function/namespace-reassign-import-fails/_config.js
index 7e2d7a8..2d606f4 100644
--- a/test/function/namespace-reassign-import-fails/_config.js
+++ b/test/function/namespace-reassign-import-fails/_config.js
@@ -4,7 +4,7 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows reassignments to namespace exports',
error: function ( err ) {
- assert.equal( err.file, path.resolve( __dirname, 'main.js' ) );
+ assert.equal( path.normalize(err.file), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { line: 3, column: 0 });
assert.ok( /Illegal reassignment/.test( err.message ) );
}
diff --git a/test/function/namespace-update-import-fails/_config.js b/test/function/namespace-update-import-fails/_config.js
index 116f4d0..4cff5c0 100644
--- a/test/function/namespace-update-import-fails/_config.js
+++ b/test/function/namespace-update-import-fails/_config.js
@@ -4,7 +4,7 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows updates to namespace exports',
error: function ( err ) {
- assert.equal( err.file, path.resolve( __dirname, 'main.js' ) );
+ assert.equal( path.normalize(err.file), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { line: 3, column: 0 });
assert.ok( /Illegal reassignment/.test( err.message ) );
}
diff --git a/test/function/reassign-import-fails/_config.js b/test/function/reassign-import-fails/_config.js
index c73c36d..e01d090 100644
--- a/test/function/reassign-import-fails/_config.js
+++ b/test/function/reassign-import-fails/_config.js
@@ -4,7 +4,7 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows assignments to imported bindings',
error: function ( err ) {
- assert.equal( err.file, path.resolve( __dirname, 'main.js' ) );
+ assert.equal( path.normalize(err.file), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { line: 8, column: 0 });
assert.ok( /Illegal reassignment/.test( err.message ) );
}
diff --git a/test/function/reassign-import-not-at-top-level-fails/_config.js b/test/function/reassign-import-not-at-top-level-fails/_config.js
index d17af29..6e00786 100644
--- a/test/function/reassign-import-not-at-top-level-fails/_config.js
+++ b/test/function/reassign-import-not-at-top-level-fails/_config.js
@@ -4,7 +4,7 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows assignments to imported bindings not at the top level',
error: function ( err ) {
- assert.equal( err.file, path.resolve( __dirname, 'main.js' ) );
+ assert.equal( path.normalize(err.file), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { line: 7, column: 2 });
assert.ok( /Illegal reassignment/.test( err.message ) );
}
diff --git a/test/function/shadowed-external-export/main.js b/test/function/shadowed-external-export/main.js
index d8a080a..37ce4be 100644
--- a/test/function/shadowed-external-export/main.js
+++ b/test/function/shadowed-external-export/main.js
@@ -1,4 +1,4 @@
-import { relative } from 'path';
+import { relative, normalize } from 'path';
var paths = {};
function getRelativePath ( path, path2 ) {
@@ -6,5 +6,5 @@ function getRelativePath ( path, path2 ) {
return relative( path, path2 );
}
-assert.equal( getRelativePath( 'foo/bar/baz', 'foo/baz/bar' ), '../../baz/bar' );
+assert.equal( getRelativePath( 'foo/bar/baz', 'foo/baz/bar' ), normalize('../../baz/bar') );
assert.deepEqual( paths, { 'foo/bar/baz': true });
diff --git a/test/function/shorthand-properties/baz.js b/test/function/shorthand-properties/baz.js
new file mode 100644
index 0000000..d826bfa
--- /dev/null
+++ b/test/function/shorthand-properties/baz.js
@@ -0,0 +1,3 @@
+export default function bar () {
+ return 'main-bar';
+}
diff --git a/test/function/shorthand-properties/foo.js b/test/function/shorthand-properties/foo.js
index 25941fc..831360e 100644
--- a/test/function/shorthand-properties/foo.js
+++ b/test/function/shorthand-properties/foo.js
@@ -1,7 +1,10 @@
+import baz from './baz.js';
+
function bar () {
return 'foo-bar';
}
export var foo = {
- bar
+ bar,
+ baz
};
diff --git a/test/function/shorthand-properties/main.js b/test/function/shorthand-properties/main.js
index 5118a11..754fba8 100644
--- a/test/function/shorthand-properties/main.js
+++ b/test/function/shorthand-properties/main.js
@@ -1,8 +1,5 @@
+import bar from './baz.js';
import { foo } from './foo';
-function bar () {
- return 'main-bar';
-}
-
assert.equal( bar(), 'main-bar' );
assert.equal( foo.bar(), 'foo-bar' );
diff --git a/test/function/tracks-alias-mutations/_config.js b/test/function/tracks-alias-mutations/_config.js
new file mode 100644
index 0000000..c3a0619
--- /dev/null
+++ b/test/function/tracks-alias-mutations/_config.js
@@ -0,0 +1,4 @@
+module.exports = {
+ description: 'tracks mutations of aliased objects',
+ skip: true
+};
diff --git a/test/function/tracks-alias-mutations/bar.js b/test/function/tracks-alias-mutations/bar.js
new file mode 100644
index 0000000..4ec5140
--- /dev/null
+++ b/test/function/tracks-alias-mutations/bar.js
@@ -0,0 +1,6 @@
+import { foo } from './foo';
+
+var f = foo;
+f.wasMutated = true;
+
+export var bar = 'whatever';
diff --git a/test/function/tracks-alias-mutations/foo.js b/test/function/tracks-alias-mutations/foo.js
new file mode 100644
index 0000000..386f265
--- /dev/null
+++ b/test/function/tracks-alias-mutations/foo.js
@@ -0,0 +1 @@
+export var foo = {};
diff --git a/test/function/tracks-alias-mutations/main.js b/test/function/tracks-alias-mutations/main.js
new file mode 100644
index 0000000..7cf7ee2
--- /dev/null
+++ b/test/function/tracks-alias-mutations/main.js
@@ -0,0 +1,4 @@
+import { foo } from './foo';
+import { bar } from './bar';
+
+assert.ok( foo.wasMutated );
diff --git a/test/function/update-expression-of-import-fails/_config.js b/test/function/update-expression-of-import-fails/_config.js
index f2f1390..5f5c27c 100644
--- a/test/function/update-expression-of-import-fails/_config.js
+++ b/test/function/update-expression-of-import-fails/_config.js
@@ -4,7 +4,7 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows updates to imported bindings',
error: function ( err ) {
- assert.equal( err.file, path.resolve( __dirname, 'main.js' ) );
+ assert.equal( path.normalize(err.file), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { line: 3, column: 0 });
assert.ok( /Illegal reassignment/.test( err.message ) );
}
diff --git a/test/sourcemaps/names/_config.js b/test/sourcemaps/names/_config.js
new file mode 100644
index 0000000..36523ef
--- /dev/null
+++ b/test/sourcemaps/names/_config.js
@@ -0,0 +1,28 @@
+var assert = require( 'assert' );
+var getLocation = require( '../../utils/getLocation' );
+var SourceMapConsumer = require( 'source-map' ).SourceMapConsumer;
+
+module.exports = {
+ description: 'names are recovered (https://github.com/rollup/rollup/issues/101)',
+ options: {
+ moduleName: 'myModule'
+ },
+ test: function ( code, map ) {
+ var match = /Object\.create\( ([^\.]+)\.prototype/.exec( code );
+
+ var deconflictedName = match[1];
+ if ( deconflictedName !== 'Foo' ) throw new Error( 'Need to update this test!' );
+
+ var smc = new SourceMapConsumer( map );
+
+ var index = code.indexOf( deconflictedName );
+ var generatedLoc = getLocation( code, index );
+ var originalLoc = smc.originalPositionFor( generatedLoc );
+ assert.equal( originalLoc.name, null );
+
+ index = code.indexOf( deconflictedName, index + 1 );
+ generatedLoc = getLocation( code, index );
+ originalLoc = smc.originalPositionFor( generatedLoc );
+ assert.equal( originalLoc.name, 'Foo' );
+ }
+};
diff --git a/test/sourcemaps/names/bar.js b/test/sourcemaps/names/bar.js
new file mode 100644
index 0000000..b074af8
--- /dev/null
+++ b/test/sourcemaps/names/bar.js
@@ -0,0 +1 @@
+export default function Foo () {}
diff --git a/test/sourcemaps/names/foo.js b/test/sourcemaps/names/foo.js
new file mode 100644
index 0000000..82616f3
--- /dev/null
+++ b/test/sourcemaps/names/foo.js
@@ -0,0 +1,5 @@
+import Bar from './bar';
+
+export default function Foo () {}
+
+Foo.prototype = Object.create( Bar.prototype );
diff --git a/test/sourcemaps/names/main.js b/test/sourcemaps/names/main.js
new file mode 100644
index 0000000..a03af97
--- /dev/null
+++ b/test/sourcemaps/names/main.js
@@ -0,0 +1 @@
+export { default as Foo } from './foo';
diff --git a/test/test.js b/test/test.js
index 0ea9e27..4e2bd25 100644
--- a/test/test.js
+++ b/test/test.js
@@ -2,11 +2,11 @@ require( 'source-map-support' ).install();
require( 'console-group' ).install();
var path = require( 'path' );
+var os = require( 'os' );
var sander = require( 'sander' );
var assert = require( 'assert' );
var exec = require( 'child_process' ).exec;
var babel = require( 'babel-core' );
-var sequence = require( './utils/promiseSequence' );
var rollup = require( '../dist/rollup' );
var FUNCTION = path.resolve( __dirname, 'function' );
@@ -32,6 +32,10 @@ function extend ( target ) {
return target;
}
+function normaliseOutput ( code ) {
+ return code.toString().trim().replace( /\r\n/g, '\n' );
+}
+
describe( 'rollup', function () {
describe( 'sanity checks', function () {
it( 'exists', function () {
@@ -157,49 +161,45 @@ describe( 'rollup', function () {
sander.readdirSync( FORM ).sort().forEach( function ( dir ) {
if ( dir[0] === '.' ) return; // .DS_Store...
- describe( dir, function () {
- var config = require( FORM + '/' + dir + '/_config' );
+ var config = require( FORM + '/' + dir + '/_config' );
- var options = extend( {}, config.options, {
- entry: FORM + '/' + dir + '/main.js'
- });
-
- var bundlePromise = rollup.rollup( options );
+ var options = extend( {}, config.options, {
+ entry: FORM + '/' + dir + '/main.js'
+ });
+ ( config.skip ? describe.skip : config.solo ? describe.only : describe)( dir, function () {
PROFILES.forEach( function ( profile ) {
- ( config.skip ? it.skip : config.solo ? it.only : it )( 'generates ' + profile.format, function () {
- if ( config.solo ) console.group( dir );
-
- return bundlePromise.then( function ( bundle ) {
+ it( 'generates ' + profile.format, function () {
+ return rollup.rollup( options ).then( function ( bundle ) {
var options = extend( {}, config.options, {
dest: FORM + '/' + dir + '/_actual/' + profile.format + '.js',
format: profile.format
});
return bundle.write( options ).then( function () {
- var actualCode = sander.readFileSync( FORM, dir, '_actual', profile.format + '.js' ).toString().trim();
+ var actualCode = normaliseOutput( sander.readFileSync( FORM, dir, '_actual', profile.format + '.js' ) );
var expectedCode;
var actualMap;
var expectedMap;
try {
- expectedCode = sander.readFileSync( FORM, dir, '_expected', profile.format + '.js' ).toString().trim();
+ expectedCode = normaliseOutput( sander.readFileSync( FORM, dir, '_expected', profile.format + '.js' ) );
} catch ( err ) {
expectedCode = 'missing file';
}
try {
actualMap = JSON.parse( sander.readFileSync( FORM, dir, '_actual', profile.format + '.js.map' ).toString() );
+ actualMap.sourcesContent = actualMap.sourcesContent.map( normaliseOutput );
} catch ( err ) {}
try {
expectedMap = JSON.parse( sander.readFileSync( FORM, dir, '_expected', profile.format + '.js.map' ).toString() );
+ expectedMap.sourcesContent = expectedMap.sourcesContent.map( normaliseOutput );
} catch ( err ) {}
assert.equal( actualCode, expectedCode );
assert.deepEqual( actualMap, expectedMap );
-
- if ( config.solo ) console.groupEnd();
});
});
});
@@ -219,17 +219,16 @@ describe( 'rollup', function () {
entry: SOURCEMAPS + '/' + dir + '/main.js'
});
- var bundlePromise = rollup.rollup( options );
-
PROFILES.forEach( function ( profile ) {
( config.skip ? it.skip : config.solo ? it.only : it )( 'generates ' + profile.format, function () {
- return bundlePromise.then( function ( bundle ) {
- var result = bundle.generate({
+ return rollup.rollup( options ).then( function ( bundle ) {
+ var options = extend( {}, config.options, {
format: profile.format,
sourceMap: true,
sourceMapFile: 'bundle.js'
});
+ var result = bundle.generate( options );
config.test( result.code, result.map );
});
});
@@ -248,9 +247,13 @@ describe( 'rollup', function () {
( config.skip ? it.skip : config.solo ? it.only : it )( dir, function ( done ) {
process.chdir( path.resolve( CLI, dir ) );
+ if (os.platform() === 'win32') {
+ config.command = "node " + path.resolve( __dirname, '../bin' ) + path.sep + config.command;
+ }
+
exec( config.command, {
env: {
- PATH: path.resolve( __dirname, '../bin' ) + ':' + process.env.PATH
+ PATH: path.resolve( __dirname, '../bin' ) + path.delimiter + process.env.PATH
}
}, function ( err, code, stderr ) {
if ( err ) return done( err );
@@ -309,7 +312,7 @@ describe( 'rollup', function () {
else {
var expected = sander.readFileSync( '_expected.js' ).toString();
try {
- assert.equal( code.trim(), expected.trim() );
+ assert.equal( normaliseOutput( code ), normaliseOutput( expected ) );
done();
} catch ( err ) {
done( err );
diff --git a/test/testScope.js b/test/testScope.js
new file mode 100644
index 0000000..2658774
--- /dev/null
+++ b/test/testScope.js
@@ -0,0 +1,120 @@
+require('babel/register');
+var assert = require( 'assert' );
+
+var Scope = require( '../src/Scope' );
+
+describe( 'Scope', function () {
+ it( 'can define and bind names', function () {
+ const scope = new Scope();
+
+ // If I define 'a'...
+ scope.define( 'a' );
+
+ // ... and bind 'b' to a reference to 'a'...
+ scope.bind( 'b', scope.reference( 'a' ) );
+
+ // ... lookups for 'a' and 'b' should both
+ // resolve to the same identifier.
+ assert.equal( scope.lookup( 'b' ), scope.lookup( 'a' ) );
+ });
+
+ describe( 'parent:', function () {
+ var parent = new Scope(),
+ child = new Scope( parent );
+
+ it( 'allows children access to its names', function () {
+ parent.define( 'a' );
+
+ assert.equal( child.lookup( 'a' ), parent.lookup( 'a' ) );
+ });
+
+ it( 'names in the child scope shadows the parent', function () {
+ child.define( 'a' );
+
+ assert.notEqual( child.lookup( 'a' ), parent.lookup( 'a' ) );
+
+ child.define( 'b' );
+
+ assert.equal( parent.lookup( 'b' ), undefined );
+ });
+ });
+
+ describe( 'virtual scope:', function () {
+ var real, a, b;
+
+ beforeEach(function () {
+ real = new Scope();
+ a = real.virtual();
+ b = real.virtual();
+ });
+
+ it( 'is created within another scope', function () {
+ // The actual ids are the same.
+ assert.equal( real.ids, a.ids );
+ assert.equal( real.ids, b.ids );
+ });
+
+ it( 'lookups different identifiers', function () {
+ // If I define 'a' in both scopes...
+ a.define( 'a' );
+ b.define( 'a' );
+
+ // ... the name 'a' should lookup different identifiers.
+ assert.notEqual( a.lookup( 'a' ), b.lookup( 'b' ) );
+ });
+
+ it( 'can deconflict names', function () {
+ a.define( 'a' );
+ b.define( 'a' );
+
+ // Deconflicting the actual scope should make all identifiers unique.
+ real.deconflict();
+
+ assert.deepEqual( real.usedNames(), [ '_a', 'a' ] );
+ });
+
+ it( 'deconflicts with a custom function, if provided', function () {
+ for (var i = 0; i < 26; i++) {
+ // Create 26 scopes, all of which define 'a'.
+ real.virtual().define( 'a' );
+ }
+
+ // Custom deconfliction function which ignores the current name.
+ var num = 10;
+ real.deconflict( function () {
+ return (num++).toString(36);
+ });
+
+ assert.deepEqual( real.usedNames(), 'abcdefghijklmnopqrstuvwxyz'.split('') );
+
+ // Deconflicting twice has no additional effect.
+ real.deconflict();
+ assert.deepEqual( real.usedNames(), 'abcdefghijklmnopqrstuvwxyz'.split('') );
+ });
+ });
+
+ it( 'dedupes-external-imports', function () {
+ var real = new Scope();
+
+ var external = real.virtual(),
+ locals = real.virtual(),
+ exports = real.virtual();
+
+ external.define( 'Component' );
+
+ locals.bind( 'Comp', external.reference( 'Component' ) );
+
+ exports.bind( 'default', locals.reference( 'Foo' ) );
+
+ try {
+ real.deconflict();
+ assert.ok( false, 'Scope.deconflict should throw with "Foo" undefined' );
+ } catch ( ignore ) {
+ // as expected
+ }
+
+ locals.define( 'Foo' );
+
+ real.deconflict();
+ });
+});