Browse Source

merge master -> non-jsnextmain-resolver

gh-109
Rich-Harris 9 years ago
parent
commit
58fa25a96f
  1. 1
      .babelrc
  2. 6
      .eslintrc
  3. 19
      CHANGELOG.md
  4. 19
      README.md
  5. 33
      appveyor.yml
  6. 20
      package.json
  7. 368
      src/Bundle.js
  8. 61
      src/ExternalModule.js
  9. 576
      src/Module.js
  10. 158
      src/Scope.js
  11. 293
      src/Statement.js
  12. 60
      src/ast/Scope.js
  13. 22
      src/finalisers/cjs.js
  14. 40
      src/finalisers/es6.js
  15. 22
      src/finalisers/shared/getExportBlock.js
  16. 11
      src/finalisers/shared/getInteropBlock.js
  17. 2
      src/utils/getExportMode.js
  18. 5
      src/utils/makeLegalIdentifier.js
  19. 2
      src/utils/path.js
  20. 42
      src/utils/promise.js
  21. 12
      src/utils/resolveId.js
  22. 4
      test/cli/external-modules/main.js
  23. 6
      test/form/export-all-from-internal/_config.js
  24. 11
      test/form/export-all-from-internal/_expected/amd.js
  25. 9
      test/form/export-all-from-internal/_expected/cjs.js
  26. 6
      test/form/export-all-from-internal/_expected/es6.js
  27. 11
      test/form/export-all-from-internal/_expected/iife.js
  28. 15
      test/form/export-all-from-internal/_expected/umd.js
  29. 3
      test/form/export-all-from-internal/internal.js
  30. 1
      test/form/export-all-from-internal/main.js
  31. 6
      test/form/exported-empty-vars/_config.js
  32. 8
      test/form/exported-empty-vars/_expected/amd.js
  33. 6
      test/form/exported-empty-vars/_expected/cjs.js
  34. 9
      test/form/exported-empty-vars/_expected/es6.js
  35. 8
      test/form/exported-empty-vars/_expected/iife.js
  36. 6
      test/form/exported-empty-vars/bar.js
  37. 2
      test/form/exported-empty-vars/foo.js
  38. 2
      test/form/exported-empty-vars/main.js
  39. 7
      test/form/exports-at-end-if-possible/_config.js
  40. 11
      test/form/exports-at-end-if-possible/_expected/amd.js
  41. 9
      test/form/exports-at-end-if-possible/_expected/cjs.js
  42. 7
      test/form/exports-at-end-if-possible/_expected/es6.js
  43. 11
      test/form/exports-at-end-if-possible/_expected/iife.js
  44. 9
      test/form/exports-at-end-if-possible/_expected/umd.js
  45. 5
      test/form/exports-at-end-if-possible/main.js
  46. 2
      test/form/external-imports/_expected/cjs.js
  47. 4
      test/form/external-imports/_expected/es6.js
  48. 8
      test/form/internal-conflict-resolution/_expected/amd.js
  49. 8
      test/form/internal-conflict-resolution/_expected/cjs.js
  50. 8
      test/form/internal-conflict-resolution/_expected/es6.js
  51. 8
      test/form/internal-conflict-resolution/_expected/iife.js
  52. 8
      test/form/internal-conflict-resolution/_expected/umd.js
  53. 7
      test/form/multiple-exports/_expected/amd.js
  54. 7
      test/form/multiple-exports/_expected/cjs.js
  55. 7
      test/form/multiple-exports/_expected/iife.js
  56. 7
      test/form/multiple-exports/_expected/umd.js
  57. 3
      test/form/namespace-optimization/_config.js
  58. 7
      test/form/namespace-optimization/_expected/amd.js
  59. 5
      test/form/namespace-optimization/_expected/cjs.js
  60. 3
      test/form/namespace-optimization/_expected/es6.js
  61. 7
      test/form/namespace-optimization/_expected/iife.js
  62. 11
      test/form/namespace-optimization/_expected/umd.js
  63. 3
      test/form/namespace-optimization/bar.js
  64. 3
      test/form/namespace-optimization/foo.js
  65. 3
      test/form/namespace-optimization/main.js
  66. 1
      test/form/namespace-optimization/quux.js
  67. 4
      test/form/preserves-comments-after-imports/_expected/amd.js
  68. 4
      test/form/preserves-comments-after-imports/_expected/cjs.js
  69. 4
      test/form/preserves-comments-after-imports/_expected/iife.js
  70. 4
      test/form/preserves-comments-after-imports/_expected/umd.js
  71. 2
      test/form/sourcemaps-inline/_expected/amd.js
  72. 2
      test/form/sourcemaps-inline/_expected/cjs.js
  73. 2
      test/form/sourcemaps-inline/_expected/es6.js
  74. 2
      test/form/sourcemaps-inline/_expected/iife.js
  75. 2
      test/form/sourcemaps-inline/_expected/umd.js
  76. 2
      test/form/sourcemaps/_expected/amd.js.map
  77. 2
      test/form/sourcemaps/_expected/cjs.js.map
  78. 2
      test/form/sourcemaps/_expected/es6.js.map
  79. 2
      test/form/sourcemaps/_expected/iife.js.map
  80. 2
      test/form/sourcemaps/_expected/umd.js.map
  81. 3
      test/form/unused-side-effect/_config.js
  82. 7
      test/form/unused-side-effect/_expected/amd.js
  83. 5
      test/form/unused-side-effect/_expected/cjs.js
  84. 3
      test/form/unused-side-effect/_expected/es6.js
  85. 7
      test/form/unused-side-effect/_expected/iife.js
  86. 11
      test/form/unused-side-effect/_expected/umd.js
  87. 13
      test/form/unused-side-effect/foo.js
  88. 2
      test/form/unused-side-effect/main.js
  89. 6
      test/function/allows-external-modules-from-nested-module/main.js
  90. 4
      test/function/assignment-patterns/_config.js
  91. 21
      test/function/assignment-patterns/main.js
  92. 8
      test/function/assignment-patterns/other.js
  93. 3
      test/function/assignment-to-exports/_config.js
  94. 2
      test/function/custom-path-resolver-async/_config.js
  95. 2
      test/function/custom-path-resolver-sync/_config.js
  96. 2
      test/function/duplicate-import-fails/_config.js
  97. 2
      test/function/duplicate-import-specifier-fails/_config.js
  98. 3
      test/function/dynamic-namespace-lookup/_config.js
  99. 2
      test/function/dynamic-namespace-lookup/foo.js
  100. 8
      test/function/dynamic-namespace-lookup/main.js

1
.babelrc

@ -5,6 +5,7 @@
"es6.classes",
"es6.constants",
"es6.destructuring",
"es6.modules",
"es6.parameters",
"es6.properties.shorthand",
"es6.spread",

6
.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",

19
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))

19
README.md

@ -1,5 +1,24 @@
# Rollup
<p align="center">
<a href="https://travis-ci.org/rollup/rollup">
<img src="http://img.shields.io/travis/rollup/rollup.svg"
alt="build status">
</a>
<a href="https://npmjs.org/package/rollup">
<img src="https://img.shields.io/npm/v/rollup.svg"
alt="npm version">
</a>
<a href="https://github.com/rollup/rollup/blob/master/LICENSE.md">
<img src="https://img.shields.io/npm/l/rollup.svg"
alt="license">
</a>
<a href="https://david-dm.org/rollup/rollup">
<img src="https://david-dm.org/rollup/rollup.svg"
alt="dependency status">
</a>
</p>
> *I roll up, I roll up, I roll up, Shawty I roll up*
>
> *I roll up, I roll up, I roll up*

33
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

20
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 <victorystick@gmail.com>"
],
"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"

368
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}` );
}
}

61
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;
}
}

576
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 );
}
}
}

158
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;
}
}

293
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();
}
}
});

60
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 );
}
}

22
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';

40
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 ) {

22
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' );
}

11
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' );

2
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' ) {

5
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;

2
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 );

42
src/utils/promise.js

@ -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;
}

12
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 {

4
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') );

6
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'
}
};

11
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;
});

9
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;

6
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;

11
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 = {}));

15
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;
}));

3
test/form/export-all-from-internal/internal.js

@ -0,0 +1,3 @@
export const a = 1;
export const b = 2;
export default 42;

1
test/form/export-all-from-internal/main.js

@ -0,0 +1 @@
export * from './internal.js';

6
test/form/exported-empty-vars/_config.js

@ -1,6 +0,0 @@
module.exports = {
description: 'removes empty var declarations that are exported',
options: {
moduleName: 'myBundle'
}
};

8
test/form/exported-empty-vars/_expected/amd.js

@ -1,8 +0,0 @@
define(['exports'], function (exports) { 'use strict';
exports.foo = 42;
exports.bar = 43;
exports.baz = 44;
});

6
test/form/exported-empty-vars/_expected/cjs.js

@ -1,6 +0,0 @@
'use strict';
exports.foo = 42;
exports.bar = 43;
exports.baz = 44;

9
test/form/exported-empty-vars/_expected/es6.js

@ -1,9 +0,0 @@
var foo;
foo = 42;
var bar;
var baz;
bar = 43;
baz = 44;
export { foo, bar, baz };

8
test/form/exported-empty-vars/_expected/iife.js

@ -1,8 +0,0 @@
(function (exports) { 'use strict';
exports.foo = 42;
exports.bar = 43;
exports.baz = 44;
})((this.myBundle = {}));

6
test/form/exported-empty-vars/bar.js

@ -1,6 +0,0 @@
var bar, baz;
bar = 43;
baz = 44;
export { bar, baz };

2
test/form/exported-empty-vars/foo.js

@ -1,2 +0,0 @@
export var foo;
foo = 42;

2
test/form/exported-empty-vars/main.js

@ -1,2 +0,0 @@
export { foo } from './foo';
export { bar, baz } from './bar';

7
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
};

11
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;
});

9
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;

7
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 };

11
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 = {}));

9
test/form/exported-empty-vars/_expected/umd.js → 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;
}));

5
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 );

2
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 );

4
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 );

8
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();
});

8
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();

8
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();

8
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();
})();

8
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();
}));

7
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;
});

7
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;

7
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 = {}));

7
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;
}));

3
test/form/namespace-optimization/_config.js

@ -0,0 +1,3 @@
module.exports = {
description: 'it does static lookup optimization of internal namespaces'
};

7
test/form/namespace-optimization/_expected/amd.js

@ -0,0 +1,7 @@
define(function () { 'use strict';
function a () {}
a();
});

5
test/form/namespace-optimization/_expected/cjs.js

@ -0,0 +1,5 @@
'use strict';
function a () {}
a();

3
test/form/namespace-optimization/_expected/es6.js

@ -0,0 +1,3 @@
function a () {}
a();

7
test/form/namespace-optimization/_expected/iife.js

@ -0,0 +1,7 @@
(function () { 'use strict';
function a () {}
a();
})();

11
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();
}));

3
test/form/namespace-optimization/bar.js

@ -0,0 +1,3 @@
import * as quux from './quux';
export { quux };

3
test/form/namespace-optimization/foo.js

@ -0,0 +1,3 @@
import * as bar from './bar';
export { bar };

3
test/form/namespace-optimization/main.js

@ -0,0 +1,3 @@
import * as foo from './foo';
foo.bar.quux.a();

1
test/form/namespace-optimization/quux.js

@ -0,0 +1 @@
export function a () {}

4
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;
});

4
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;

4
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 = {}));

4
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;
}));

2
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

2
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

2
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==

2
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==

2
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==

2
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,;;"}

2
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"}

2
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"}

2
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,;;"}

2
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,;;"}

3
test/form/unused-side-effect/_config.js

@ -0,0 +1,3 @@
module.exports = {
description: 'side-effects to non-globals are not blindly included'
};

7
test/form/unused-side-effect/_expected/amd.js

@ -0,0 +1,7 @@
define(function () { 'use strict';
var foo = 42;
assert.equal( foo, 42 );
});

5
test/form/unused-side-effect/_expected/cjs.js

@ -0,0 +1,5 @@
'use strict';
var foo = 42;
assert.equal( foo, 42 );

3
test/form/unused-side-effect/_expected/es6.js

@ -0,0 +1,3 @@
var foo = 42;
assert.equal( foo, 42 );

7
test/form/unused-side-effect/_expected/iife.js

@ -0,0 +1,7 @@
(function () { 'use strict';
var foo = 42;
assert.equal( foo, 42 );
})();

11
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 );
}));

13
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;

2
test/form/unused-side-effect/main.js

@ -0,0 +1,2 @@
import { foo } from './foo';
assert.equal( foo, 42 );

6
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' );
assert.equal( relative( path, path2 ), normalize('../../baz/bar') );
assert.equal( foo, normalize('../../c/b') );

4
test/function/assignment-patterns/_config.js

@ -0,0 +1,4 @@
module.exports = {
description: 'allows reassigments to default parameters that shadow imports',
babel: true
};

21
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 );

8
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';

3
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
};

2
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' );

2
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;

2
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 ) );
}

2
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 ) );
}

3
test/function/dynamic-namespace-lookup/_config.js

@ -0,0 +1,3 @@
module.exports = {
description: 'does namespace optimization when possible, but not for dynamic lookups'
};

2
test/function/dynamic-namespace-lookup/foo.js

@ -0,0 +1,2 @@
export var bar = 'bar';
export var baz = 'baz';

8
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' );

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save