Browse Source

Merge branch 'master' into gh-590

rewrite
Rich-Harris 9 years ago
parent
commit
49bcda425f
  1. 2
      .eslintignore
  2. 54
      .eslintrc
  3. 4
      .gitignore
  4. 1
      .travis.yml
  5. 170
      CHANGELOG.md
  6. 2
      README.md
  7. 1
      appveyor.yml
  8. 49
      bin/handleError.js
  9. 37
      bin/rollup
  10. 161
      bin/runRollup.js
  11. 13
      bin/showHelp.js
  12. 65
      bin/src/handleError.js
  13. 3
      bin/src/help.md
  14. 38
      bin/src/index.js
  15. 246
      bin/src/runRollup.js
  16. 4
      bin/src/sourceMappingUrl.js
  17. 80
      browser/path.js
  18. 1
      browser/promise.js
  19. 62
      package.json
  20. 4
      rollup.config.browser.js
  21. 34
      rollup.config.cli.js
  22. 16
      rollup.config.js
  23. 231
      src/Bundle.js
  24. 20
      src/Declaration.js
  25. 11
      src/ExternalModule.js
  26. 119
      src/Module.js
  27. 51
      src/Statement.js
  28. 8
      src/ast/attachScopes.js
  29. 2
      src/ast/flatten.js
  30. 17
      src/finalisers/amd.js
  31. 36
      src/finalisers/cjs.js
  32. 30
      src/finalisers/es.js
  33. 27
      src/finalisers/iife.js
  34. 4
      src/finalisers/index.js
  35. 1
      src/finalisers/shared/esModuleExport.js
  36. 16
      src/finalisers/shared/getInteropBlock.js
  37. 27
      src/finalisers/umd.js
  38. 50
      src/rollup.js
  39. 7
      src/utils/array.js
  40. 117
      src/utils/collapseSourcemaps.js
  41. 4
      src/utils/defaults.js
  42. 2
      src/utils/first.js
  43. 1
      src/utils/fs.js
  44. 5
      src/utils/getExportMode.js
  45. 2
      src/utils/getIndentString.js
  46. 2
      src/utils/makeLegalIdentifier.js
  47. 6
      src/utils/map-helpers.js
  48. 3
      src/utils/normalizePlatform.js
  49. 10
      src/utils/object.js
  50. 68
      src/utils/path.js
  51. 4
      src/utils/promise.js
  52. 4
      src/utils/pureFunctions.js
  53. 6
      src/utils/run.js
  54. 45
      src/utils/transform.js
  55. 24
      src/utils/transformBundle.js
  56. 20
      test/.babelrc
  57. 9
      test/.eslintrc
  58. 2
      test/cli/banner-intro-outro-footer/_config.js
  59. 13
      test/cli/config-cwd-case-insensitive/_config.js
  60. 1
      test/cli/config-cwd-case-insensitive/main.js
  61. 9
      test/cli/config-cwd-case-insensitive/rollup.config.js
  62. 4
      test/cli/config-external-function/_config.js
  63. 8
      test/cli/config-external-function/_expected.js
  64. 6
      test/cli/config-external-function/main.js
  65. 21
      test/cli/config-external-function/rollup.config.js
  66. 5
      test/cli/external-modules-auto-global/_config.js
  67. 3
      test/cli/external-modules-auto-global/main.js
  68. 2
      test/cli/indent-none/_config.js
  69. 4
      test/cli/indent-none/_expected.js
  70. 2
      test/cli/module-name/_config.js
  71. 4
      test/cli/module-name/_expected.js
  72. 4
      test/cli/multiple-targets-shared-config/_config.js
  73. 6
      test/cli/multiple-targets-shared-config/_expected/cjs.js
  74. 1
      test/cli/multiple-targets-shared-config/_expected/cjs.js.map
  75. 4
      test/cli/multiple-targets-shared-config/_expected/es.js
  76. 1
      test/cli/multiple-targets-shared-config/_expected/es.js.map
  77. 1
      test/cli/multiple-targets-shared-config/main.js
  78. 14
      test/cli/multiple-targets-shared-config/rollup.config.js
  79. 4
      test/cli/multiple-targets/_config.js
  80. 5
      test/cli/multiple-targets/_expected/cjs.js
  81. 3
      test/cli/multiple-targets/_expected/es.js
  82. 1
      test/cli/multiple-targets/main.js
  83. 13
      test/cli/multiple-targets/rollup.config.js
  84. 4
      test/cli/no-conflict/_config.js
  85. 16
      test/cli/no-conflict/_expected.js
  86. 1
      test/cli/no-conflict/main.js
  87. 6
      test/cli/no-conflict/rollup.config.js
  88. 4
      test/cli/no-strict/_config.js
  89. 4
      test/cli/no-strict/_expected.js
  90. 1
      test/cli/no-strict/main.js
  91. 2
      test/cli/no-treeshake/_config.js
  92. 5
      test/cli/node-config-auto-prefix/_config.js
  93. 11
      test/cli/node-config-auto-prefix/_expected.js
  94. 1
      test/cli/node-config-auto-prefix/main.js
  95. 9
      test/cli/node-config-auto-prefix/node_modules/rollup-config-foo/lib/config.js
  96. 3
      test/cli/node-config-auto-prefix/node_modules/rollup-config-foo/package.json
  97. 5
      test/cli/node-config/_config.js
  98. 11
      test/cli/node-config/_expected.js
  99. 1
      test/cli/node-config/main.js
  100. 9
      test/cli/node-config/node_modules/foo/lib/config.js

2
.eslintignore

@ -0,0 +1,2 @@
test/*
!test/test.js

54
.eslintrc

@ -1,23 +1,35 @@
{
"root": true,
"rules": {
"indent": [ 2, "tab", { "SwitchCase": 1 } ],
"quotes": [ 2, "single" ],
"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",
"ecmaFeatures": {
"modules": true
}
"root": true,
"rules": {
"indent": [ 2, "tab", { "SwitchCase": 1 } ],
"semi": [ 2, "always" ],
"keyword-spacing": [ 2, { "before": true, "after": true } ],
"space-before-blocks": [ 2, "always" ],
"space-before-function-paren": [ 2, "always" ],
"no-mixed-spaces-and-tabs": [ 2, "smart-tabs" ],
"no-cond-assign": 0,
"no-unused-vars": 2,
"object-shorthand": [ 2, "always" ],
"no-const-assign": 2,
"no-class-assign": 2,
"no-this-before-super": 2,
"no-var": 2,
"no-unreachable": 2,
"valid-typeof": 2,
"quote-props": [ 2, "as-needed" ],
"one-var": [ 2, "never" ],
"prefer-arrow-callback": 2,
"prefer-const": [ 2, { "destructuring": "all" } ],
"arrow-spacing": 2
},
"env": {
"es6": true,
"browser": true,
"node": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
}
}

4
.gitignore

@ -1,9 +1,9 @@
.DS_Store
node_modules
!test/node_modules
/node_modules
.gobble*
dist
_actual
coverage
.commithash
.idea
bin/rollup

1
.travis.yml

@ -3,6 +3,7 @@ language: node_js
node_js:
- "0.12"
- "4"
- "6"
env:
global:
- BUILD_TIMEOUT=10000

170
CHANGELOG.md

@ -1,5 +1,175 @@
# rollup changelog
## 0.34.11
* Prevent leaky state when `bundle` is reused ([#875](https://github.com/rollup/rollup/issues/875))
* Ensure `intro` appears before interop block ([#880](https://github.com/rollup/rollup/issues/880))
## 0.34.10
* Allow custom `options.context` to replace top-level `this` ([#851](https://github.com/rollup/rollup/issues/851))
* Fix `noConflict` when used via `rollup --config` ([#846](https://github.com/rollup/rollup/issues/846))
* Place `outro` block *after* export block ([#852](https://github.com/rollup/rollup/issues/852))
## 0.34.9
* Disable indentation by default, for faster bundle generation ([#812](https://github.com/rollup/rollup/pull/812))
* More helpful error on missing entry file ([#802](https://github.com/rollup/rollup/issues/802))
* Preserve comments before import declarations ([#815](https://github.com/rollup/rollup/pull/815))
## 0.34.8
* Wrap UMD factory function in parens to avoid lazy parsing ([#774](https://github.com/rollup/rollup/pull/774))
## 0.34.7
* Leave it up to resolveId to normalize the entry path ([#835](https://github.com/rollup/rollup/pull/835))
* Cache decoded mappings ([#834](https://github.com/rollup/rollup/pull/834))
## 0.34.5
* Fix circular export ([#813](https://github.com/rollup/rollup/issues/813))
## 0.34.4
* Module render performance tweak ([#823](https://github.com/rollup/rollup/pull/823))
## 0.34.3
* Avoid infinite recursion in `Bundle.sort()` ([#800](https://github.com/rollup/rollup/pull/800))
## 0.34.2
* resolveId calls are cached now to improve incremental build
* Fixed error message recursion in plugins
## 0.34.1
* Support `paths` config ([#754](https://github.com/rollup/rollup/issues/754))
* Allow `export *` from external module, internally
## 0.34.0
* Use resolved IDs for relative imports that are also external modules, to allow `options.globals` to work with them ([#763](https://github.com/rollup/rollup/issues/763))
* Ensure reassigned exports are declared in an ES bundle, and remove empty `exports.foo;` statements ([#755](https://github.com/rollup/rollup/issues/755))
* Add newline after sourcemap comment ([#756](https://github.com/rollup/rollup/issues/756))
## 0.33.2
* Add `bundle` as second argument to `ongenerate` and `onwrite` hooks ([#773](https://github.com/rollup/rollup/pull/773))
* Warn on top-level `this` ([#770](https://github.com/rollup/rollup/issues/770))
## 0.33.1
* Fix `--no-strict` option ([#751](https://github.com/rollup/rollup/pull/751))
* Fix Windows edge case with case-sensitive paths ([#760](https://github.com/rollup/rollup/pull/760))
## 0.33.0
* Downgrade missing transformer sourcemap to a warning, not an error, and print the name of the offending plugin if possible ([#746](https://github.com/rollup/rollup/issues/746))
* Warn if same name is re-exported from two modules ([#722](https://github.com/rollup/rollup/issues/722))
## 0.32.4
* Add `ongenerate` and `onwrite` plugin hooks ([#742](https://github.com/rollup/rollup/pull/742))
## 0.32.3
* Generated correct sourcemaps with reified namespaces ([#668](https://github.com/rollup/rollup/issues/668))
* Exclude plugin helper modules from sourcemaps ([#747](https://github.com/rollup/rollup/pull/747))
## 0.32.2
* Allow `--globals` to work with `--external` or `options.external` in whatever configuration ([#743](https://github.com/rollup/rollup/issues/743))
## 0.32.1
* Preserve side-effects to default exports that coincide with used named exports ([#733](https://github.com/rollup/rollup/issues/733))
* Support `rollup -c node:pkgname` ([#736](https://github.com/rollup/rollup/issues/736))
## 0.32.0
* Deprecate `es6` format in favour of `es` ([#468](https://github.com/rollup/rollup/issues/468))
* Add correct `jsnext:main` build ([#726](https://github.com/rollup/rollup/pull/726))
## 0.31.2
* Allow `load` plugins to provide sourcemap ([#715](https://github.com/rollup/rollup/pull/715))
* Allow `sourceMapFile` in config options ([#717](https://github.com/rollup/rollup/issues/717))
## 0.31.1
* Logging for errors emitted by `rollup-watch` ([#712](https://github.com/rollup/rollup/issues/712))
## 0.31.0
* Rewrite top-level `this` as `undefined` ([#707](https://github.com/rollup/rollup/pull/707))
* Pass `options.acorn` to Acorn ([#564](https://github.com/rollup/rollup/issues/564))
## 0.30.0
* Bundle CLI ([#700](https://github.com/rollup/rollup/issues/700))
* Ensure absolute paths are normalised ([#704](https://github.com/rollup/rollup/issues/704))
* Allow `rollup --watch` to work with targets
## 0.29.1
* Merge `target` options with main options ([#701](https://github.com/rollup/rollup/issues/701))
* Update magic-string ([#690](https://github.com/rollup/rollup/issues/690))
## 0.29.0
* `rollup --watch` ([#284](https://github.com/rollup/rollup/issues/284))
## 0.28.0
* Experimental support for incremental rebuilds ([#658](https://github.com/rollup/rollup/pull/658))
## 0.27.1
* Ensure names exported from a module are not replaced with reserved words ([#696](https://github.com/rollup/rollup/pull/696))
* Revert ([#692](https://github.com/rollup/rollup/pull/692)) – resolved IDs must be strings
## 0.27.0
* Use native promises instead of `es6-promise` ([#689](https://github.com/rollup/rollup/issues/689))
* Support multiple targets in config files ([#655](https://github.com/rollup/rollup/issues/655))
* Allow `resolveId` plugin functions to return non-strings ([#692](https://github.com/rollup/rollup/pull/692))
## 0.26.7
* Distinguish between default and namespace imports of external module ([#637](https://github.com/rollup/rollup/issues/637))
* Add `__esModule` property to named exports in AMD, CJS and UMD modes ([#650](https://github.com/rollup/rollup/issues/650))
## 0.26.6
* Deconflict named imports from external modules in ES bundles ([#659](https://github.com/rollup/rollup/issues/659))
* Support `options.preferConst` to generate `const` declarations for exports rather than `var` declarations ([#653](https://github.com/rollup/rollup/issues/653))
## 0.26.5
* Preserve `debugger` statements ([#664](https://github.com/rollup/rollup/issues/664))
* Allow `options.external` to be a function ([#522](https://github.com/rollup/rollup/issues/522))
## 0.26.4
* Prevent plugin-provided external IDs being normalised ([#630](https://github.com/rollup/rollup/issues/630), [#633](https://github.com/rollup/rollup/issues/633))
* Throw if module exports/re-exports the same name twice, or has multiple default exports ([#679](https://github.com/rollup/rollup/issues/679))
* Warn about `eval` security issue ([#675]((https://github.com/rollup/rollup/issues/675)))
## 0.26.3
* Ensure reference is not incorrectly marked as a reassignment ([#648](https://github.com/rollup/rollup/issues/648))
## 0.26.2
* Sanity check output of `load` hook ([#607](https://github.com/rollup/rollup/issues/607))
* Correct scoping for ID class expressions ([#626](https://github.com/rollup/rollup/issues/626))
* Warn if named and default exports are used together in auto mode ([#587](https://github.com/rollup/rollup/issues/587))
* Allow variable initialisers to be rewritten if necessary ([#632](https://github.com/rollup/rollup/issues/632))
* Prevent double `var` with no-treeshake option ([#639](https://github.com/rollup/rollup/pull/639))
## 0.26.1
* Add `treeshake: false`/`--no-treeshake` option for debugging ([#505](https://github.com/rollup/rollup/issues/505))

2
README.md

@ -18,7 +18,7 @@
alt="dependency status">
</a>
<a href="https://codecov.io/github/rollup/rollup?branch=master">
<img src="https://codecov.io/github/rollup/rollup/coverage.svg?branch=master" alt="Coverage via Codecov" />
<img src="https://codecov.io/gh/rollup/rollup/branch/master/graph/badge.svg" alt="Coverage via Codecov" />
</a>
<a href='https://gitter.im/rollup/rollup?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge'>
<img src='https://badges.gitter.im/rollup/rollup.svg'

1
appveyor.yml

@ -12,6 +12,7 @@ environment:
# node.js
- nodejs_version: 0.12
- nodejs_version: 4
- nodejs_version: 6
install:
- ps: Install-Product node $env:nodejs_version

49
bin/handleError.js

@ -1,49 +0,0 @@
var chalk = require( 'chalk' );
var handlers = {
MISSING_CONFIG: function () {
console.error( chalk.red( 'Config file must export an options object. See https://github.com/rollup/rollup/wiki/Command-Line-Interface#using-a-config-file' ) );
},
MISSING_INPUT_OPTION: function () {
console.error( chalk.red( 'You must specify an --input (-i) option' ) );
},
MISSING_OUTPUT_OPTION: function () {
console.error( chalk.red( 'You must specify an --output (-o) option when creating a file with a sourcemap' ) );
},
MISSING_NAME: function ( err ) {
console.error( chalk.red( 'You must supply a name for UMD exports (e.g. `--name myModule`)' ) );
},
PARSE_ERROR: function ( err ) {
console.error( chalk.red( 'Error parsing ' + err.file + ': ' + err.message ) );
},
ONE_AT_A_TIME: function ( err ) {
console.error( chalk.red( 'rollup can only bundle one file at a time' ) );
},
DUPLICATE_IMPORT_OPTIONS: function ( err ) {
console.error( chalk.red( 'use --input, or pass input path as argument' ) );
}
};
module.exports = function handleError ( err ) {
var handler;
if ( handler = handlers[ err && err.code ] ) {
handler( err );
} else {
console.error( chalk.red( err.message || err ) );
if ( err.stack ) {
console.error( chalk.grey( err.stack ) );
}
}
console.error( 'Type ' + chalk.cyan( 'rollup --help' ) + ' for help, or visit https://github.com/rollup/rollup/wiki' );
process.exit( 1 );
};

37
bin/rollup

@ -1,37 +0,0 @@
#!/usr/bin/env node
var minimist = require( 'minimist' ),
command;
command = minimist( process.argv.slice( 2 ), {
alias: {
// Aliases
strict: 'useStrict',
// Short options
c: 'config',
d: 'indent',
e: 'external',
f: 'format',
g: 'globals',
h: 'help',
i: 'input',
m: 'sourcemap',
n: 'name',
o: 'output',
u: 'id',
v: 'version'
}
});
if ( command.help || ( process.argv.length <= 2 && process.stdin.isTTY ) ) {
require( './showHelp' )();
}
else if ( command.version ) {
console.log( 'rollup version ' + require( '../package.json' ).version );
}
else {
require( './runRollup' )( command );
}

161
bin/runRollup.js

@ -1,161 +0,0 @@
require( 'source-map-support' ).install();
var path = require( 'path' );
var handleError = require( './handleError' );
var rollup = require( '../' );
// log to stderr to keep `rollup main.js > bundle.js` from breaking
var log = console.error.bind(console);
module.exports = function ( command ) {
if ( command._.length > 1 ) {
handleError({ code: 'ONE_AT_A_TIME' });
}
if ( command._.length === 1 ) {
if ( command.input ) {
handleError({ code: 'DUPLICATE_IMPORT_OPTIONS' });
}
command.input = command._[0];
}
if ( command.environment ) {
command.environment.split( ',' ).forEach( function ( pair ) {
var index = pair.indexOf( ':' );
if ( ~index ) {
process.env[ pair.slice( 0, index ) ] = pair.slice( index + 1 );
} else {
process.env[ pair ] = true;
}
});
}
var config = command.config === true ? 'rollup.config.js' : command.config;
if ( config ) {
config = path.resolve( config );
rollup.rollup({
entry: config,
onwarn: function ( message ) {
if ( /Treating .+ as external dependency/.test( message ) ) return;
log( message );
}
}).then( function ( bundle ) {
var code = bundle.generate({
format: 'cjs'
}).code;
// temporarily override require
var defaultLoader = require.extensions[ '.js' ];
require.extensions[ '.js' ] = function ( m, filename ) {
if ( filename === config ) {
m._compile( code, filename );
} else {
defaultLoader( m, filename );
}
};
try {
var options = require( path.resolve( config ) );
if ( Object.keys( options ).length === 0 ) {
handleError({ code: 'MISSING_CONFIG' });
}
} catch ( err ) {
handleError( err );
}
execute( options, command );
require.extensions[ '.js' ] = defaultLoader;
})
.catch(log);
} else {
execute( {}, command );
}
};
var equivalents = {
banner: 'banner',
footer: 'footer',
format: 'format',
globals: 'globals',
id: 'moduleId',
indent: 'indent',
input: 'entry',
intro: 'intro',
name: 'moduleName',
output: 'dest',
outro: 'outro',
sourcemap: 'sourceMap',
treeshake: 'treeshake'
};
function execute ( options, command ) {
var external = ( options.external || [] )
.concat( command.external ? command.external.split( ',' ) : [] );
if ( command.globals ) {
var globals = Object.create( null );
command.globals.split( ',' ).forEach(function ( str ) {
var names = str.split( ':' );
globals[ names[0] ] = names[1];
// Add missing Module IDs to external.
if ( external.indexOf( names[0] ) === -1 ) {
external.push( names[0] );
}
});
command.globals = globals;
}
options.onwarn = options.onwarn || log;
options.external = external;
options.noConflict = command.conflict === false;
delete command.conflict;
// Use any options passed through the CLI as overrides.
Object.keys( equivalents ).forEach( function ( cliOption ) {
if ( command.hasOwnProperty( cliOption ) ) {
options[ equivalents[ cliOption ] ] = command[ cliOption ];
}
});
try {
bundle( options ).catch( handleError );
} catch ( err ) {
handleError( err );
}
}
function bundle ( options ) {
if ( !options.entry ) {
handleError({ code: 'MISSING_INPUT_OPTION' });
}
return rollup.rollup( options ).then( function ( bundle ) {
if ( options.dest ) {
return bundle.write( options );
}
if ( options.sourceMap && options.sourceMap !== 'inline' ) {
handleError({ code: 'MISSING_OUTPUT_OPTION' });
}
var result = bundle.generate( options );
var code = result.code,
map = result.map;
if ( options.sourceMap === 'inline' ) {
code += '\n//# sourceMappingURL=' + map.toUrl();
}
process.stdout.write( code );
});
}

13
bin/showHelp.js

@ -1,13 +0,0 @@
var fs = require( 'fs' );
var path = require( 'path' );
module.exports = function () {
fs.readFile( path.join( __dirname, 'help.md' ), function ( err, result ) {
var help;
if ( err ) throw err;
help = result.toString().replace( '<%= version %>', require( '../package.json' ).version );
console.log( '\n' + help + '\n' );
});
};

65
bin/src/handleError.js

@ -0,0 +1,65 @@
import * as chalk from 'chalk';
function stderr ( msg ) {
console.error( msg ); // eslint-disable-line no-console
}
const handlers = {
MISSING_CONFIG: () => {
stderr( chalk.red( 'Config file must export an options object. See https://github.com/rollup/rollup/wiki/Command-Line-Interface#using-a-config-file' ) );
},
MISSING_EXTERNAL_CONFIG: err => {
stderr( chalk.red( `Could not resolve config file ${err.config}` ) );
},
MISSING_INPUT_OPTION: () => {
stderr( chalk.red( 'You must specify an --input (-i) option' ) );
},
MISSING_OUTPUT_OPTION: () => {
stderr( chalk.red( 'You must specify an --output (-o) option when creating a file with a sourcemap' ) );
},
MISSING_NAME: () => {
stderr( chalk.red( 'You must supply a name for UMD exports (e.g. `--name myModule`)' ) );
},
PARSE_ERROR: err => {
stderr( chalk.red( `Error parsing ${err.file}: ${err.message}` ) );
},
ONE_AT_A_TIME: () => {
stderr( chalk.red( 'rollup can only bundle one file at a time' ) );
},
DUPLICATE_IMPORT_OPTIONS: () => {
stderr( chalk.red( 'use --input, or pass input path as argument' ) );
},
ROLLUP_WATCH_NOT_INSTALLED: () => {
stderr( chalk.red( 'rollup --watch depends on the rollup-watch package, which could not be found. You can install it globally (recommended) with ' ) + chalk.cyan( 'npm install -g rollup-watch' ) );
},
WATCHER_MISSING_INPUT_OR_OUTPUT: () => {
stderr( chalk.red( 'must specify --input and --output when using rollup --watch' ) );
}
};
export default function handleError ( err, recover ) {
const handler = handlers[ err && err.code ];
if ( handler ) {
handler( err );
} else {
stderr( chalk.red( err.message || err ) );
if ( err.stack ) {
stderr( chalk.grey( err.stack ) );
}
}
stderr( `Type ${chalk.cyan( 'rollup --help' )} for help, or visit https://github.com/rollup/rollup/wiki` );
if ( !recover ) process.exit( 1 );
}

3
bin/help.md → bin/src/help.md

@ -1,4 +1,4 @@
rollup version <%= version %>
rollup version __VERSION__
=====================================
Usage: rollup [options] <entry file>
@ -9,6 +9,7 @@ Basic options:
-h, --help Show this help message
-c, --config Use this config file (if argument is used but value
is unspecified, defaults to rollup.config.js)
-w, --watch Watch files in bundle and rebuild on changes
-i, --input Input (alternative to <entry file>)
-o, --output <output> Output (if absent, prints to stdout)
-f, --format [es6] Type of output (amd, cjs, es6, iife, umd)

38
bin/src/index.js

@ -0,0 +1,38 @@
import minimist from 'minimist';
import help from './help.md';
import { version } from '../../package.json';
import runRollup from './runRollup';
const command = minimist( process.argv.slice( 2 ), {
alias: {
// Aliases
strict: 'useStrict',
// Short options
c: 'config',
d: 'indent',
e: 'external',
f: 'format',
g: 'globals',
h: 'help',
i: 'input',
m: 'sourcemap',
n: 'name',
o: 'output',
u: 'id',
v: 'version',
w: 'watch'
}
});
if ( command.help || ( process.argv.length <= 2 && process.stdin.isTTY ) ) {
console.log( `\n${help.replace('__VERSION__', version)}\n` ); // eslint-disable-line no-console
}
else if ( command.version ) {
console.log( `rollup version ${version}` ); // eslint-disable-line no-console
}
else {
runRollup( command );
}

246
bin/src/runRollup.js

@ -0,0 +1,246 @@
import { realpathSync } from 'fs';
import { rollup } from 'rollup';
import relative from 'require-relative';
import handleError from './handleError';
import SOURCEMAPPING_URL from './sourceMappingUrl.js';
import { install as installSourcemapSupport } from 'source-map-support';
installSourcemapSupport();
// stderr to stderr to keep `rollup main.js > bundle.js` from breaking
const stderr = console.error.bind( console ); // eslint-disable-line no-console
export default function runRollup ( command ) {
if ( command._.length > 1 ) {
handleError({ code: 'ONE_AT_A_TIME' });
}
if ( command._.length === 1 ) {
if ( command.input ) {
handleError({ code: 'DUPLICATE_IMPORT_OPTIONS' });
}
command.input = command._[0];
}
if ( command.environment ) {
command.environment.split( ',' ).forEach( pair => {
const index = pair.indexOf( ':' );
if ( ~index ) {
process.env[ pair.slice( 0, index ) ] = pair.slice( index + 1 );
} else {
process.env[ pair ] = true;
}
});
}
let config = command.config === true ? 'rollup.config.js' : command.config;
if ( config ) {
if ( config.slice( 0, 5 ) === 'node:' ) {
const pkgName = config.slice( 5 );
try {
config = relative.resolve( `rollup-config-${pkgName}`, process.cwd() );
} catch ( err ) {
try {
config = relative.resolve( pkgName, process.cwd() );
} catch ( err ) {
if ( err.code === 'MODULE_NOT_FOUND' ) {
handleError({ code: 'MISSING_EXTERNAL_CONFIG', config });
}
throw err;
}
}
} else {
// find real path of config so it matches what Node provides to callbacks in require.extensions
config = realpathSync( config );
}
rollup({
entry: config,
onwarn: message => {
if ( /Treating .+ as external dependency/.test( message ) ) return;
stderr( message );
}
}).then( bundle => {
const { code } = bundle.generate({
format: 'cjs'
});
// temporarily override require
const defaultLoader = require.extensions[ '.js' ];
require.extensions[ '.js' ] = ( m, filename ) => {
if ( filename === config ) {
m._compile( code, filename );
} else {
defaultLoader( m, filename );
}
};
try {
const options = require( config );
if ( Object.keys( options ).length === 0 ) {
handleError({ code: 'MISSING_CONFIG' });
}
execute( options, command );
require.extensions[ '.js' ] = defaultLoader;
} catch ( err ) {
handleError( err );
}
})
.catch( stderr );
} else {
execute( {}, command );
}
}
const equivalents = {
useStrict: 'useStrict',
banner: 'banner',
footer: 'footer',
format: 'format',
globals: 'globals',
id: 'moduleId',
indent: 'indent',
input: 'entry',
intro: 'intro',
name: 'moduleName',
output: 'dest',
outro: 'outro',
sourcemap: 'sourceMap',
treeshake: 'treeshake'
};
function execute ( options, command ) {
let external;
const commandExternal = ( command.external || '' ).split( ',' );
const optionsExternal = options.external;
if ( command.globals ) {
let globals = Object.create( null );
command.globals.split( ',' ).forEach( str => {
const names = str.split( ':' );
globals[ names[0] ] = names[1];
// Add missing Module IDs to external.
if ( commandExternal.indexOf( names[0] ) === -1 ) {
commandExternal.push( names[0] );
}
});
command.globals = globals;
}
if ( typeof optionsExternal === 'function' ) {
external = id => {
return optionsExternal( id ) || ~commandExternal.indexOf( id );
};
} else {
external = ( optionsExternal || [] ).concat( commandExternal );
}
options.onwarn = options.onwarn || stderr;
options.external = external;
// Use any options passed through the CLI as overrides.
Object.keys( equivalents ).forEach( cliOption => {
if ( command.hasOwnProperty( cliOption ) ) {
options[ equivalents[ cliOption ] ] = command[ cliOption ];
}
});
try {
if ( command.watch ) {
if ( !options.entry || ( !options.dest && !options.targets ) ) {
handleError({ code: 'WATCHER_MISSING_INPUT_OR_OUTPUT' });
}
try {
const watch = relative( 'rollup-watch', process.cwd() );
const watcher = watch( rollup, options );
watcher.on( 'event', event => {
switch ( event.code ) {
case 'STARTING':
stderr( 'checking rollup-watch version...' );
break;
case 'BUILD_START':
stderr( 'bundling...' );
break;
case 'BUILD_END':
stderr( 'bundled in ' + event.duration + 'ms. Watching for changes...' );
break;
case 'ERROR':
handleError( event.error, true );
break;
default:
stderr( 'unknown event', event );
}
});
} catch ( err ) {
if ( err.code === 'MODULE_NOT_FOUND' ) {
err.code = 'ROLLUP_WATCH_NOT_INSTALLED';
}
handleError( err );
}
} else {
bundle( options ).catch( handleError );
}
} catch ( err ) {
handleError( err );
}
}
function clone ( object ) {
return assign( {}, object );
}
function assign ( target, source ) {
Object.keys( source ).forEach( key => {
target[ key ] = source[ key ];
});
return target;
}
function bundle ( options ) {
if ( !options.entry ) {
handleError({ code: 'MISSING_INPUT_OPTION' });
}
return rollup( options ).then( bundle => {
if ( options.dest ) {
return bundle.write( options );
}
if ( options.targets ) {
let result = null;
options.targets.forEach( target => {
result = bundle.write( assign( clone( options ), target ) );
});
return result;
}
if ( options.sourceMap && options.sourceMap !== 'inline' ) {
handleError({ code: 'MISSING_OUTPUT_OPTION' });
}
let { code, map } = bundle.generate( options );
if ( options.sourceMap === 'inline' ) {
code += `\n//# ${SOURCEMAPPING_URL}=${map.toUrl()}\n`;
}
process.stdout.write( code );
});
}

4
bin/src/sourceMappingUrl.js

@ -0,0 +1,4 @@
let SOURCEMAPPING_URL = 'sourceMa';
SOURCEMAPPING_URL += 'ppingURL';
export default SOURCEMAPPING_URL;

80
browser/path.js

@ -0,0 +1,80 @@
export const absolutePath = /^(?:\/|(?:[A-Za-z]:)?[\\|\/])/;
export const relativePath = /^\.?\.\//;
export function isAbsolute ( path ) {
return absolutePath.test( path );
}
export function isRelative ( path ) {
return relativePath.test( path );
}
export function normalize ( path ) {
return path.replace( /\\/g, '/' );
}
export function basename ( path ) {
return path.split( /(\/|\\)/ ).pop();
}
export function dirname ( path ) {
const match = /(\/|\\)[^\/\\]*$/.exec( path );
if ( !match ) return '.';
const dir = path.slice( 0, -match[0].length );
// If `dir` is the empty string, we're at root.
return dir ? dir : '/';
}
export function extname ( path ) {
const match = /\.[^\.]+$/.exec( basename( path ) );
if ( !match ) return '';
return match[0];
}
export function relative ( from, to ) {
const fromParts = from.split( /[\/\\]/ ).filter( Boolean );
const toParts = to.split( /[\/\\]/ ).filter( Boolean );
while ( fromParts[0] && toParts[0] && fromParts[0] === toParts[0] ) {
fromParts.shift();
toParts.shift();
}
while ( toParts[0] === '.' || toParts[0] === '..' ) {
const toPart = toParts.shift();
if ( toPart === '..' ) {
fromParts.pop();
}
}
while ( fromParts.pop() ) {
toParts.unshift( '..' );
}
return toParts.join( '/' );
}
export function resolve ( ...paths ) {
let resolvedParts = paths.shift().split( /[\/\\]/ );
paths.forEach( path => {
if ( isAbsolute( path ) ) {
resolvedParts = path.split( /[\/\\]/ );
} else {
const parts = path.split( /[\/\\]/ );
while ( parts[0] === '.' || parts[0] === '..' ) {
const part = parts.shift();
if ( part === '..' ) {
resolvedParts.pop();
}
}
resolvedParts.push.apply( resolvedParts, parts );
}
});
return resolvedParts.join( '/' ); // TODO windows...
}

1
browser/promise.js

@ -1 +0,0 @@
export default window.Promise;

62
package.json

@ -1,23 +1,26 @@
{
"name": "rollup",
"version": "0.26.1",
"version": "0.34.11",
"description": "Next-generation ES6 module bundler",
"main": "dist/rollup.js",
"jsnext:main": "src/rollup.js",
"module": "dist/rollup.es.js",
"jsnext:main": "dist/rollup.es.js",
"bin": {
"rollup": "./bin/rollup"
},
"scripts": {
"pretest": "npm run build",
"pretest": "npm run build && npm run build:cli",
"test": "mocha",
"pretest-coverage": "npm run build",
"test-coverage": "rm -rf coverage/* && istanbul cover --report json node_modules/.bin/_mocha -- -u exports -R spec test/test.js",
"posttest-coverage": "remap-istanbul -i coverage/coverage-final.json -o coverage/coverage-remapped.json -b dist && remap-istanbul -i coverage/coverage-final.json -o coverage/coverage-remapped.lcov -t lcovonly -b dist && remap-istanbul -i coverage/coverage-final.json -o coverage/coverage-remapped -t html -b dist",
"ci": "npm run test-coverage && codecov < coverage/coverage-remapped.lcov",
"build": "git rev-parse HEAD > .commithash && rollup -c -o dist/rollup.js",
"build": "git rev-parse HEAD > .commithash && rollup -c",
"watch": "rollup -c -w",
"build:cli": "rollup -c rollup.config.cli.js",
"build:browser": "git rev-parse HEAD > .commithash && rollup -c rollup.config.browser.js -o dist/rollup.browser.js",
"prepublish": "npm run lint && npm test && npm run build:browser",
"lint": "eslint src browser"
"lint": "eslint src browser test/test.js test/utils test/**/_config.js"
},
"repository": {
"type": "git",
@ -32,7 +35,8 @@
],
"author": "Rich Harris",
"contributors": [
"Oskar Segersvärd <victorystick@gmail.com>"
"Oskar Segersvärd <victorystick@gmail.com>",
"Bogdan Chadkin <trysound@yandex.ru>"
],
"license": "MIT",
"bugs": {
@ -40,35 +44,37 @@
},
"homepage": "https://github.com/rollup/rollup",
"devDependencies": {
"acorn": "^2.6.4",
"babel-core": "^5.8.32",
"acorn": "^3.2.0",
"buble": "^0.12.5",
"chalk": "^1.1.3",
"codecov.io": "^0.1.6",
"console-group": "^0.2.0",
"es6-promise": "^3.0.2",
"eslint": "^1.7.1",
"estree-walker": "^0.2.0",
"istanbul": "^0.4.0",
"magic-string": "^0.10.1",
"mocha": "^2.3.3",
"remap-istanbul": "^0.5.1",
"rollup": "^0.20.2",
"rollup-plugin-buble": "^0.5.0",
"rollup-plugin-node-resolve": "^1.5.0",
"rollup-plugin-replace": "^1.0.1",
"sander": "^0.4.0",
"source-map": "^0.5.3",
"sourcemap-codec": "^1.2.1",
"uglify-js": "^2.6.1"
"console-group": "^0.2.1",
"eslint": "^2.13.0",
"estree-walker": "^0.2.1",
"istanbul": "^0.4.3",
"magic-string": "^0.15.2",
"minimist": "^1.2.0",
"mocha": "^3.0.0",
"remap-istanbul": "^0.6.4",
"require-relative": "^0.8.7",
"rollup": "^0.34.2",
"rollup-plugin-buble": "^0.12.1",
"rollup-plugin-commonjs": "^3.0.0",
"rollup-plugin-json": "^2.0.0",
"rollup-plugin-node-resolve": "^2.0.0",
"rollup-plugin-replace": "^1.1.0",
"rollup-plugin-string": "^2.0.0",
"sander": "^0.5.1",
"source-map": "^0.5.6",
"sourcemap-codec": "^1.3.0",
"uglify-js": "^2.6.2"
},
"dependencies": {
"chalk": "^1.1.1",
"minimist": "^1.2.0",
"source-map-support": "^0.4.0"
},
"files": [
"src",
"dist",
"bin",
"bin/rollup",
"README.md"
]
}

4
rollup.config.browser.js

@ -3,8 +3,8 @@ import config from './rollup.config.js';
config.plugins.push({
load: function ( id ) {
if ( ~id.indexOf( 'fs.js' ) ) return readFileSync( 'browser/fs.js' ).toString();
if ( ~id.indexOf( 'es6-promise' ) ) return readFileSync( 'browser/promise.js' ).toString();
if ( ~id.indexOf( 'fs.js' ) ) return readFileSync( 'browser/fs.js', 'utf-8' );
if ( ~id.indexOf( 'path.js' ) ) return readFileSync( 'browser/path.js', 'utf-8' );
}
});

34
rollup.config.cli.js

@ -0,0 +1,34 @@
import buble from 'rollup-plugin-buble';
import json from 'rollup-plugin-json';
import string from 'rollup-plugin-string';
import nodeResolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
export default {
entry: 'bin/src/index.js',
dest: 'bin/rollup',
format: 'cjs',
banner: '#!/usr/bin/env node',
plugins: [
string({ include: '**/*.md' }),
json(),
buble(),
commonjs({
include: 'node_modules/**',
namedExports: { chalk: [ 'red', 'cyan', 'grey' ] }
}),
nodeResolve({
main: true
})
],
external: [
'fs',
'path',
'module',
'source-map-support',
'rollup'
],
paths: {
rollup: '../dist/rollup.js'
}
};

16
rollup.config.js

@ -1,6 +1,6 @@
import { readFileSync } from 'fs';
import buble from 'rollup-plugin-buble';
import npm from 'rollup-plugin-node-resolve';
import nodeResolve from 'rollup-plugin-node-resolve';
import replace from 'rollup-plugin-replace';
var pkg = JSON.parse( readFileSync( 'package.json', 'utf-8' ) );
@ -20,13 +20,12 @@ var banner = readFileSync( 'src/banner.js', 'utf-8' )
export default {
entry: 'src/rollup.js',
format: 'cjs',
plugins: [
buble({
include: [ 'src/**', 'node_modules/acorn/**' ]
}),
npm({
nodeResolve({
jsnext: true
}),
@ -37,8 +36,15 @@ export default {
values: { 'VERSION': pkg.version }
})
],
external: [ 'fs' ],
external: [
'fs',
'path'
],
banner: banner,
sourceMap: true,
moduleName: 'rollup'
moduleName: 'rollup',
targets: [
{ dest: 'dist/rollup.js', format: 'cjs' },
{ dest: 'dist/rollup.es.js', format: 'es' }
]
};

231
src/Bundle.js

@ -1,5 +1,7 @@
import MagicString from 'magic-string';
import { decode } from 'sourcemap-codec';
import { Bundle as MagicStringBundle } from 'magic-string';
import first from './utils/first.js';
import { find } from './utils/array.js';
import { blank, forOwn, keys } from './utils/object.js';
import Module from './Module.js';
import ExternalModule from './ExternalModule.js';
@ -8,17 +10,23 @@ import ensureArray from './utils/ensureArray.js';
import { load, makeOnwarn, resolveId } from './utils/defaults.js';
import getExportMode from './utils/getExportMode.js';
import getIndentString from './utils/getIndentString.js';
import { unixizePath } from './utils/normalizePlatform.js';
import { mapSequence } from './utils/promise.js';
import transform from './utils/transform.js';
import transformBundle from './utils/transformBundle.js';
import collapseSourcemaps from './utils/collapseSourcemaps.js';
import SOURCEMAPPING_URL from './utils/sourceMappingURL.js';
import callIfFunction from './utils/callIfFunction.js';
import { dirname, isRelative, relative, resolve } from './utils/path.js';
import { dirname, isRelative, isAbsolute, normalize, relative, resolve } from './utils/path.js';
export default class Bundle {
constructor ( options ) {
this.cachedModules = new Map();
if ( options.cache ) {
options.cache.modules.forEach( module => {
this.cachedModules.set( module.id, module );
});
}
this.plugins = ensureArray( options.plugins );
this.plugins.forEach( plugin => {
@ -27,46 +35,53 @@ export default class Bundle {
}
});
this.entry = unixizePath( options.entry );
this.entry = options.entry;
this.entryId = null;
this.entryModule = null;
this.treeshake = options.treeshake !== false;
this.resolveId = first(
[ id => ~this.external.indexOf( id ) ? false : null ]
[ id => this.isExternal( id ) ? false : null ]
.concat( this.plugins.map( plugin => plugin.resolveId ).filter( Boolean ) )
.concat( resolveId )
);
this.load = first(
this.plugins
.map( plugin => plugin.load )
.filter( Boolean )
.concat( load )
);
this.transformers = this.plugins
.map( plugin => plugin.transform )
const loaders = this.plugins
.map( plugin => plugin.load )
.filter( Boolean );
this.hasLoaders = loaders.length !== 0;
this.load = first( loaders.concat( load ) );
this.bundleTransformers = this.plugins
.map( plugin => plugin.transformBundle )
.filter( Boolean );
this.getPath = typeof options.paths === 'function' ?
( id => options.paths( id ) || this.getPathRelativeToEntryDirname( id ) ) :
options.paths ?
( id => options.paths.hasOwnProperty( id ) ? options.paths[ id ] : this.getPathRelativeToEntryDirname( id ) ) :
id => this.getPathRelativeToEntryDirname( id );
this.moduleById = blank();
this.moduleById = new Map();
this.modules = [];
this.externalModules = [];
this.internalNamespaces = [];
this.context = String( options.context );
this.assumedGlobals = blank();
this.external = ensureArray( options.external ).map( id => id.replace( /[\/\\]/g, '/' ) );
if ( typeof options.external === 'function' ) {
this.isExternal = options.external;
} else {
const ids = ensureArray( options.external );
this.isExternal = id => ids.indexOf( id ) !== -1;
}
this.onwarn = options.onwarn || makeOnwarn();
// TODO strictly speaking, this only applies with non-ES6, non-default-only bundles
[ 'module', 'exports', '_interopDefault' ].forEach( global => this.assumedGlobals[ global ] = true );
this.varOrConst = options.preferConst ? 'const' : 'var';
this.acornOptions = options.acorn || {};
}
build () {
@ -75,6 +90,7 @@ export default class Bundle {
// of the entry module's dependencies
return this.resolveId( this.entry, undefined )
.then( id => {
if ( id == null ) throw new Error( `Could not resolve entry (${this.entry})` );
this.entryId = id;
return this.fetchModule( id, undefined );
})
@ -117,7 +133,7 @@ export default class Bundle {
}
deconflict () {
let used = blank();
const used = blank();
// ensure no conflicts with globals
keys( this.assumedGlobals ).forEach( name => used[ name ] = 1 );
@ -156,8 +172,8 @@ export default class Bundle {
fetchModule ( id, importer ) {
// short-circuit cycles
if ( id in this.moduleById ) return null;
this.moduleById[ id ] = null;
if ( this.moduleById.has( id ) ) return null;
this.moduleById.set( id, null );
return this.load( id )
.catch( err => {
@ -167,52 +183,91 @@ export default class Bundle {
msg += `: ${err.message}`;
throw new Error( msg );
})
.then( source => transform( source, id, this.transformers ) )
.then( source => {
const { code, originalCode, ast, sourceMapChain } = source;
if ( typeof source === 'string' ) return source;
if ( source && typeof source === 'object' && source.code ) return source;
const module = new Module({ id, code, originalCode, ast, sourceMapChain, bundle: this });
throw new Error( `Error loading ${id}: load hook should return a string, a { code, map } object, or nothing/null` );
})
.then( source => {
if ( typeof source === 'string' ) {
source = {
code: source,
ast: null
};
}
if ( this.cachedModules.has( id ) && this.cachedModules.get( id ).originalCode === source.code ) {
return this.cachedModules.get( id );
}
return transform( source, id, this.plugins );
})
.then( source => {
const { code, originalCode, originalSourceMap, ast, sourceMapChain, resolvedIds } = source;
const module = new Module({
id,
code,
originalCode,
originalSourceMap,
ast,
sourceMapChain,
resolvedIds,
bundle: this
});
this.modules.push( module );
this.moduleById[ id ] = module;
this.moduleById.set( id, module );
return this.fetchAllDependencies( module ).then( () => module );
return this.fetchAllDependencies( module ).then( () => {
keys( module.exports ).forEach( name => {
module.exportsAll[name] = module.id;
});
module.exportAllSources.forEach( source => {
const id = module.resolvedIds[ source ];
const exportAllModule = this.moduleById.get( id );
if ( exportAllModule.isExternal ) return;
keys( exportAllModule.exportsAll ).forEach( name => {
if ( name in module.exportsAll ) {
this.onwarn( `Conflicting namespaces: ${module.id} re-exports '${name}' from both ${module.exportsAll[ name ]} (will be ignored) and ${exportAllModule.exportsAll[ name ]}.` );
}
module.exportsAll[ name ] = exportAllModule.exportsAll[ name ];
});
});
return module;
});
});
}
fetchAllDependencies ( module ) {
return mapSequence( module.sources, source => {
return this.resolveId( source, module.id )
const resolvedId = module.resolvedIds[ source ];
return ( resolvedId ? Promise.resolve( resolvedId ) : this.resolveId( source, module.id ) )
.then( resolvedId => {
let externalName;
if ( resolvedId ) {
// If the `resolvedId` is supposed to be external, make it so.
externalName = resolvedId.replace( /[\/\\]/g, '/' );
} else if ( isRelative( source ) ) {
// This could be an external, relative dependency, based on the current module's parent dir.
externalName = resolve( module.id, '..', source );
}
const forcedExternal = externalName && ~this.external.indexOf( externalName );
const externalId = resolvedId || (
isRelative( source ) ? resolve( module.id, '..', source ) : source
);
if ( !resolvedId || forcedExternal ) {
let normalizedExternal = source;
let isExternal = this.isExternal( externalId );
if ( !forcedExternal ) {
if ( isRelative( source ) ) throw new Error( `Could not resolve ${source} from ${module.id}` );
if ( !~this.external.indexOf( source ) ) this.onwarn( `Treating '${source}' as external dependency` );
} else if ( resolvedId ) {
normalizedExternal = this.getPathRelativeToEntryDirname( resolvedId );
}
module.resolvedIds[ source ] = normalizedExternal;
if ( !resolvedId && !isExternal ) {
if ( isRelative( source ) ) throw new Error( `Could not resolve '${source}' from ${module.id}` );
if ( !this.moduleById[ normalizedExternal ] ) {
const module = new ExternalModule( normalizedExternal );
this.externalModules.push( module );
this.moduleById[ normalizedExternal ] = module;
}
this.onwarn( `Treating '${source}' as external dependency` );
isExternal = true;
}
else {
if ( isExternal ) {
module.resolvedIds[ source ] = externalId;
if ( !this.moduleById.has( externalId ) ) {
const module = new ExternalModule( externalId, this.getPath( externalId ) );
this.externalModules.push( module );
this.moduleById.set( externalId, module );
}
} else {
if ( resolvedId === module.id ) {
throw new Error( `A module cannot import itself (${resolvedId})` );
}
@ -225,51 +280,53 @@ export default class Bundle {
}
getPathRelativeToEntryDirname ( resolvedId ) {
// Get a path relative to the resolved entry directory
const entryDirname = dirname( this.entryId );
const relativeToEntry = relative( entryDirname, resolvedId );
if ( isRelative( resolvedId ) || isAbsolute( resolvedId ) ) {
const entryDirname = dirname( this.entryId );
const relativeToEntry = normalize( relative( entryDirname, resolvedId ) );
if ( isRelative( relativeToEntry )) {
return relativeToEntry;
return isRelative( relativeToEntry ) ? relativeToEntry : `./${relativeToEntry}`;
}
// The path is missing the `./` prefix
return `./${relativeToEntry}`;
return resolvedId;
}
render ( options = {} ) {
const format = options.format || 'es6';
if ( options.format === 'es6' ) {
this.onwarn( 'The es6 format is deprecated – use `es` instead' );
options.format = 'es';
}
const format = options.format || 'es';
// Determine export mode - 'default', 'named', 'none'
const exportMode = getExportMode( this, options.exports );
const exportMode = getExportMode( this, options.exports, options.moduleName );
let magicString = new MagicString.Bundle({ separator: '\n\n' });
let usedModules = [];
let magicString = new MagicStringBundle({ separator: '\n\n' });
const usedModules = [];
this.orderedModules.forEach( module => {
const source = module.render( format === 'es6' );
const source = module.render( format === 'es' );
if ( source.toString().length ) {
magicString.addSource( source );
usedModules.push( module );
}
});
const intro = [ options.intro ]
let intro = [ options.intro ]
.concat(
this.plugins.map( plugin => plugin.intro && plugin.intro() )
)
.filter( Boolean )
.join( '\n\n' );
if ( intro ) magicString.prepend( intro + '\n' );
if ( options.outro ) magicString.append( '\n' + options.outro );
if ( intro ) intro += '\n';
const indentString = getIndentString( magicString, options );
const finalise = finalisers[ format ];
if ( !finalise ) throw new Error( `You must specify an output type - valid options are ${keys( finalisers ).join( ', ' )}` );
magicString = finalise( this, magicString.trim(), { exportMode, indentString }, options );
magicString = finalise( this, magicString.trim(), { exportMode, indentString, intro }, options );
const banner = [ options.banner ]
.concat( this.plugins.map( plugin => plugin.banner ) )
@ -288,34 +345,38 @@ export default class Bundle {
let code = magicString.toString();
let map = null;
let bundleSourcemapChain = [];
const bundleSourcemapChain = [];
code = transformBundle( code, this.bundleTransformers, bundleSourcemapChain )
code = transformBundle( code, this.plugins, bundleSourcemapChain )
.replace( new RegExp( `\\/\\/#\\s+${SOURCEMAPPING_URL}=.+\\n?`, 'g' ), '' );
if ( options.sourceMap ) {
let file = options.sourceMapFile || options.dest;
if ( file ) file = resolve( typeof process !== 'undefined' ? process.cwd() : '', file );
map = magicString.generateMap({ file, includeContent: true });
if ( this.transformers.length || this.bundleTransformers.length ) {
map = collapseSourcemaps( map, usedModules, bundleSourcemapChain );
if ( this.hasLoaders || find( this.plugins, plugin => plugin.transform || plugin.transformBundle ) ) {
map = magicString.generateMap( {} );
if ( typeof map.mappings === 'string' ) {
map.mappings = decode( map.mappings );
}
map = collapseSourcemaps( file, map, usedModules, bundleSourcemapChain, this.onwarn );
} else {
map = magicString.generateMap({ file, includeContent: true });
}
map.sources = map.sources.map( unixizePath );
map.sources = map.sources.map( normalize );
}
return { code, map };
}
sort () {
let seen = {};
let hasCycles;
let ordered = [];
const seen = {};
const ordered = [];
let stronglyDependsOn = blank();
let dependsOn = blank();
const stronglyDependsOn = blank();
const dependsOn = blank();
this.modules.forEach( module => {
stronglyDependsOn[ module.id ] = blank();
@ -365,15 +426,17 @@ export default class Bundle {
// b imports a, a is placed before b. We need to find the module
// in question, so we can provide a useful error message
let parent = '[[unknown]]';
const visited = {};
const findParent = module => {
if ( dependsOn[ module.id ][ a.id ] && dependsOn[ module.id ][ b.id ] ) {
parent = module.id;
} else {
for ( let i = 0; i < module.dependencies.length; i += 1 ) {
const dependency = module.dependencies[i];
if ( findParent( dependency ) ) return;
}
return true;
}
visited[ module.id ] = true;
for ( let i = 0; i < module.dependencies.length; i += 1 ) {
const dependency = module.dependencies[i];
if ( !visited[ dependency.id ] && findParent( dependency ) ) return true;
}
};

20
src/Declaration.js

@ -1,4 +1,5 @@
import { blank, forOwn, keys } from './utils/object.js';
import makeLegalIdentifier from './utils/makeLegalIdentifier.js';
import run from './utils/run.js';
import { SyntheticReference } from './Reference.js';
@ -17,7 +18,7 @@ export default class Declaration {
}
this.statement = statement;
this.name = null;
this.name = node.id ? node.id.name : node.name;
this.exportName = null;
this.isParam = isParam;
@ -33,13 +34,16 @@ export default class Declaration {
addReference ( reference ) {
reference.declaration = this;
this.name = reference.name; // TODO handle differences of opinion
if ( reference.name !== this.name ) {
this.name = makeLegalIdentifier( reference.name ); // TODO handle differences of opinion
}
if ( reference.isReassignment ) this.isReassigned = true;
}
render ( es6 ) {
if ( es6 ) return this.name;
render ( es ) {
if ( es ) return this.name;
if ( !this.isReassigned || !this.exportName ) return this.name;
return `exports.${this.exportName}`;
@ -241,7 +245,7 @@ export class SyntheticNamespaceDeclaration {
return `${indentString}${name}: ${original.render()}`;
});
return `var ${this.render()} = Object.freeze({\n${members.join( ',\n' )}\n});\n\n`;
return `${this.module.bundle.varOrConst} ${this.render()} = Object.freeze({\n${members.join( ',\n' )}\n});\n\n`;
}
render () {
@ -276,18 +280,18 @@ export class ExternalDeclaration {
}
}
render ( es6 ) {
render ( es ) {
if ( this.name === '*' ) {
return this.module.name;
}
if ( this.name === 'default' ) {
return !es6 && this.module.exportsNames ?
return this.module.exportsNamespace || ( !es && this.module.exportsNames ) ?
`${this.module.name}__default` :
this.module.name;
}
return es6 ? this.safeName : `${this.module.name}.${this.name}`;
return es ? this.safeName : `${this.module.name}.${this.name}`;
}
run () {

11
src/ExternalModule.js

@ -3,9 +3,11 @@ import makeLegalIdentifier from './utils/makeLegalIdentifier.js';
import { ExternalDeclaration } from './Declaration.js';
export default class ExternalModule {
constructor ( id ) {
constructor ( id, relativePath ) {
this.id = id;
this.name = makeLegalIdentifier( id );
this.path = relativePath;
this.name = makeLegalIdentifier( relativePath );
this.nameSuggestions = blank();
this.mostCommonSuggestion = 0;
@ -27,9 +29,8 @@ export default class ExternalModule {
}
traceExport ( name ) {
if ( name !== 'default' && name !== '*' ) {
this.exportsNames = true;
}
if ( name !== 'default' && name !== '*' ) this.exportsNames = true;
if ( name === '*' ) this.exportsNamespace = true;
return this.declarations[ name ] || (
this.declarations[ name ] = new ExternalDeclaration( this, name )

119
src/Module.js

@ -2,7 +2,7 @@ import { parse } from 'acorn/src/index.js';
import MagicString from 'magic-string';
import { walk } from 'estree-walker';
import Statement from './Statement.js';
import { blank, keys } from './utils/object.js';
import { assign, blank, keys } from './utils/object.js';
import { basename, extname } from './utils/path.js';
import getLocation from './utils/getLocation.js';
import makeLegalIdentifier from './utils/makeLegalIdentifier.js';
@ -17,22 +17,25 @@ import { emptyBlockStatement } from './ast/create.js';
import extractNames from './ast/extractNames.js';
export default class Module {
constructor ({ id, code, originalCode, ast, sourceMapChain, bundle }) {
constructor ({ id, code, originalCode, originalSourceMap, ast, sourceMapChain, resolvedIds, bundle }) {
this.code = code;
this.originalCode = originalCode;
this.originalSourceMap = originalSourceMap;
this.sourceMapChain = sourceMapChain;
this.bundle = bundle;
this.id = id;
this.excludeFromSourcemap = /\0/.test( id );
// all dependencies
this.sources = [];
this.dependencies = [];
this.resolvedIds = blank();
this.resolvedIds = resolvedIds || blank();
// imports and exports, indexed by local name
this.imports = blank();
this.exports = blank();
this.exportsAll = blank();
this.reexports = blank();
this.exportAllSources = [];
@ -41,7 +44,7 @@ export default class Module {
// By default, `id` is the filename. Custom resolvers and loaders
// can change that, but it makes sense to use it for the source filename
this.magicString = new MagicString( code, {
filename: id,
filename: this.excludeFromSourcemap ? null : id, // don't include plugin helpers in sourcemap
indentExclusionRanges: []
});
@ -53,7 +56,8 @@ export default class Module {
}
this.comments = [];
this.statements = this.parse( ast );
this.ast = ast;
this.statements = this.parse();
this.declarations = blank();
this.analyse();
@ -77,7 +81,13 @@ export default class Module {
else {
node.specifiers.forEach( specifier => {
this.reexports[ specifier.exported.name ] = {
const name = specifier.exported.name;
if ( this.exports[ name ] || this.reexports[ name ] ) {
throw new Error( `A module cannot have multiple exports with the same name ('${name}')` );
}
this.reexports[ name ] = {
start: specifier.start,
source,
localName: specifier.local.name,
@ -93,6 +103,11 @@ export default class Module {
else if ( node.type === 'ExportDefaultDeclaration' ) {
const identifier = ( node.declaration.id && node.declaration.id.name ) || node.declaration.name;
if ( this.exports.default ) {
// TODO indicate location
throw new Error( 'A module can only have one default export' );
}
this.exports.default = {
localName: 'default',
identifier
@ -107,7 +122,7 @@ export default class Module {
// export var a = 1, b = 2, c = 3;
// export function foo () {}
else if ( node.declaration ) {
let declaration = node.declaration;
const declaration = node.declaration;
if ( declaration.type === 'VariableDeclaration' ) {
declaration.declarations.forEach( decl => {
@ -129,6 +144,10 @@ export default class Module {
const localName = specifier.local.name;
const exportedName = specifier.exported.name;
if ( this.exports[ exportedName ] || this.reexports[ exportedName ] ) {
throw new Error( `A module cannot have multiple exports with the same name ('${exportedName}')` );
}
this.exports[ exportedName ] = { localName };
});
} else {
@ -209,18 +228,18 @@ export default class Module {
const specifier = specifiers[ name ];
const id = this.resolvedIds[ specifier.source ];
specifier.module = this.bundle.moduleById[ id ];
specifier.module = this.bundle.moduleById.get( id );
});
});
this.exportAllModules = this.exportAllSources.map( source => {
const id = this.resolvedIds[ source ];
return this.bundle.moduleById[ id ];
return this.bundle.moduleById.get( id );
});
this.sources.forEach( source => {
const id = this.resolvedIds[ source ];
const module = this.bundle.moduleById[ id ];
const module = this.bundle.moduleById.get( id );
if ( !module.isExternal ) this.dependencies.push( module );
});
@ -256,7 +275,7 @@ export default class Module {
}
getExports () {
let exports = blank();
const exports = blank();
keys( this.exports ).forEach( name => {
exports[ name ] = true;
@ -283,18 +302,18 @@ export default class Module {
return this.declarations['*'];
}
parse ( ast ) {
parse () {
// The ast can be supplied programmatically (but usually won't be)
if ( !ast ) {
if ( !this.ast ) {
// Try to extract a list of top-level statements/declarations. If
// the parse fails, attach file info and abort
try {
ast = parse( this.code, {
this.ast = parse( this.code, assign({
ecmaVersion: 6,
sourceType: 'module',
onComment: ( block, text, start, end ) => this.comments.push({ block, text, start, end }),
preserveParens: true
});
}, this.bundle.acornOptions ));
} catch ( err ) {
err.code = 'PARSE_ERROR';
err.file = this.id; // see above - not necessarily true, but true enough
@ -303,7 +322,7 @@ export default class Module {
}
}
walk( ast, {
walk( this.ast, {
enter: node => {
// eliminate dead branches early
if ( node.type === 'IfStatement' ) {
@ -335,11 +354,11 @@ export default class Module {
}
});
let statements = [];
const statements = [];
let lastChar = 0;
let commentIndex = 0;
ast.body.forEach( node => {
this.ast.body.forEach( node => {
if ( node.type === 'EmptyStatement' ) return;
if (
@ -428,11 +447,16 @@ export default class Module {
return statements;
}
render ( es6 ) {
let magicString = this.magicString.clone();
render ( es ) {
const magicString = this.magicString.clone();
this.statements.forEach( statement => {
if ( !statement.isIncluded ) {
if ( statement.node.type === 'ImportDeclaration' ) {
magicString.remove( statement.node.start, statement.next );
return;
}
magicString.remove( statement.start, statement.next );
return;
}
@ -444,7 +468,7 @@ export default class Module {
if ( statement.node.isSynthetic ) return;
// skip `export { foo, bar, baz }`
if ( statement.node.specifiers.length ) {
if ( statement.node.declaration === null ) {
magicString.remove( statement.start, statement.next );
return;
}
@ -458,13 +482,8 @@ export default class Module {
const declaration = this.declarations[ declarator.id.name ];
if ( declaration.exportName && declaration.isReassigned ) { // `var foo = ...` becomes `exports.foo = ...`
if ( declarator.init ) {
magicString.overwrite( statement.start, declarator.init.start, `exports.${declaration.exportName} = ` );
} else {
magicString.remove( statement.start, declarator.init ? declarator.start : statement.next );
}
return;
magicString.remove( statement.start, declarator.init ? declarator.start : statement.next );
if ( !declarator.init ) return;
}
}
@ -477,19 +496,20 @@ export default class Module {
const declaration = this.declarations[ name ];
if ( declaration.exportName && declaration.isReassigned ) {
magicString.insert( statement.end, `;\nexports.${name} = ${declaration.render( es6 )}` );
magicString.insertLeft( statement.end, `;\nexports.${name} = ${declaration.render( es )}` );
}
});
}
if ( statement.node.isSynthetic ) {
// insert `var/let/const` if necessary
magicString.insert( statement.start, `${statement.node.kind} ` );
magicString.overwrite( statement.end, statement.next, ';\n' ); // TODO account for trailing newlines
magicString.insertRight( statement.start, `${statement.node.kind} ` );
magicString.insertLeft( statement.end, ';' );
magicString.overwrite( statement.end, statement.next, '\n' ); // TODO account for trailing newlines
}
}
let toDeshadow = blank();
const toDeshadow = blank();
statement.references.forEach( reference => {
const { start, end } = reference;
@ -501,7 +521,7 @@ export default class Module {
const declaration = reference.declaration;
if ( declaration ) {
const name = declaration.render( es6 );
const name = declaration.render( es );
// the second part of this check is necessary because of
// namespace optimisation – name of `foo.bar` could be `bar`
@ -516,7 +536,7 @@ export default class Module {
}
if ( reference.isShorthandProperty ) {
magicString.insert( end, `: ${name}` );
magicString.insertLeft( end, `: ${name}` );
} else {
magicString.overwrite( start, end, name, true );
}
@ -541,11 +561,21 @@ export default class Module {
const name = extractNames( statement.node.declaration.declarations[ 0 ].id )[ 0 ];
const declaration = this.declarations[ name ];
// TODO is this even possible?
if ( !declaration ) throw new Error( `Missing declaration for ${name}!` );
const end = declaration.exportName && declaration.isReassigned ?
statement.node.declaration.declarations[0].start :
statement.node.declaration.start;
let end;
if ( es ) {
end = statement.node.declaration.start;
} else {
if ( declaration.exportName && declaration.isReassigned ) {
const declarator = statement.node.declaration.declarations[0];
end = declarator.init ? declarator.start : statement.next;
} else {
end = statement.node.declaration.start;
}
}
magicString.remove( statement.node.start, end );
}
@ -582,7 +612,7 @@ export default class Module {
if ( statement.node.declaration.type === 'FunctionExpression' ) {
magicString.overwrite( statement.node.start, statement.node.declaration.start + 8, `function ${defaultName}` );
} else {
magicString.overwrite( statement.node.start, statement.node.declaration.start, `var ${defaultName} = ` );
magicString.overwrite( statement.node.start, statement.node.declaration.start, `${this.bundle.varOrConst} ${defaultName} = ` );
}
}
@ -611,7 +641,7 @@ export default class Module {
run ( treeshake ) {
if ( !treeshake ) {
this.statements.forEach( statement => {
if ( statement.isImportDeclaration ) return;
if ( statement.isImportDeclaration || ( statement.isExportDeclaration && statement.node.isSynthetic ) ) return;
statement.mark();
});
@ -627,6 +657,17 @@ export default class Module {
return marked;
}
toJSON () {
return {
id: this.id,
code: this.code,
originalCode: this.originalCode,
ast: this.ast,
sourceMapChain: this.sourceMapChain,
resolvedIds: this.resolvedIds
};
}
trace ( name ) {
if ( name in this.declarations ) return this.declarations[ name ];
if ( name in this.imports ) {

51
src/Statement.js

@ -41,13 +41,14 @@ export default class Statement {
// find references
const statement = this;
let { module, references, scope, stringLiteralRanges } = this;
let readDepth = 0;
let contextDepth = 0;
walk( this.node, {
enter ( node, parent, prop ) {
// warn about eval
if ( node.type === 'CallExpression' && node.callee.name === 'eval' && !scope.contains( 'eval' ) ) {
module.bundle.onwarn( `Use of \`eval\` (in ${module.id}) is discouraged, as it may cause issues with minification. See https://github.com/rollup/rollup/wiki/Troubleshooting#avoiding-eval for more details` );
// TODO show location
module.bundle.onwarn( `Use of \`eval\` (in ${module.id}) is strongly discouraged, as it poses security risks and may cause issues with minification. See https://github.com/rollup/rollup/wiki/Troubleshooting#avoiding-eval for more details` );
}
// skip re-export declarations
@ -58,36 +59,44 @@ export default class Statement {
stringLiteralRanges.push([ node.start + 1, node.end - 1 ]);
}
if ( node.type === 'ThisExpression' && contextDepth === 0 ) {
module.magicString.overwrite( node.start, node.end, module.bundle.context );
if ( module.bundle.context === 'undefined' ) module.bundle.onwarn( 'The `this` keyword is equivalent to `undefined` at the top level of an ES module, and has been rewritten' );
}
if ( node._scope ) scope = node._scope;
if ( /Function/.test( node.type ) ) readDepth += 1;
if ( /^Function/.test( node.type ) ) contextDepth += 1;
let isReassignment;
if ( parent && isModifierNode( parent ) ) {
let subject = parent[ modifierNodes[ parent.type ] ];
let depth = 0;
while ( subject.type === 'MemberExpression' ) {
subject = subject.object;
depth += 1;
}
if ( node === subject ) {
let depth = 0;
const importDeclaration = module.imports[ subject.name ];
while ( subject.type === 'MemberExpression' ) {
subject = subject.object;
depth += 1;
}
if ( !scope.contains( subject.name ) && importDeclaration ) {
const minDepth = importDeclaration.name === '*' ?
2 : // cannot do e.g. `namespace.foo = bar`
1; // cannot do e.g. `foo = bar`, but `foo.bar = bar` is fine
const importDeclaration = module.imports[ subject.name ];
if ( depth < minDepth ) {
const err = new Error( `Illegal reassignment to import '${subject.name}'` );
err.file = module.id;
err.loc = getLocation( module.magicString.toString(), subject.start );
throw err;
if ( !scope.contains( subject.name ) && importDeclaration ) {
const minDepth = importDeclaration.name === '*' ?
2 : // cannot do e.g. `namespace.foo = bar`
1; // cannot do e.g. `foo = bar`, but `foo.bar = bar` is fine
if ( depth < minDepth ) {
const err = new Error( `Illegal reassignment to import '${subject.name}'` );
err.file = module.id;
err.loc = getLocation( module.magicString.original, subject.start );
throw err;
}
}
}
isReassignment = !depth;
isReassignment = !depth;
}
}
if ( isReference( node, parent ) ) {
@ -117,7 +126,7 @@ export default class Statement {
},
leave ( node ) {
if ( node._scope ) scope = scope.parent;
if ( /Function/.test( node.type ) ) readDepth -= 1;
if ( /^Function/.test( node.type ) ) contextDepth -= 1;
}
});
}

8
src/ast/attachScopes.js

@ -2,8 +2,8 @@ import { walk } from 'estree-walker';
import Scope from './Scope.js';
const blockDeclarations = {
'const': true,
'let': true
const: true,
let: true
};
export default function attachScopes ( statement ) {
@ -29,7 +29,7 @@ export default function attachScopes ( statement ) {
let newScope;
// create new function scope
if ( /Function/.test( node.type ) ) {
if ( /(Function|Class)/.test( node.type ) ) {
newScope = new Scope({
parent: scope,
block: false,
@ -38,7 +38,7 @@ export default function attachScopes ( statement ) {
// named function expressions - the name is considered
// part of the function's scope
if ( node.type === 'FunctionExpression' && node.id ) {
if ( /(Function|Class)Expression/.test( node.type ) && node.id ) {
newScope.addDeclaration( node, false, false );
}
}

2
src/ast/flatten.js

@ -1,5 +1,5 @@
export default function flatten ( node ) {
let parts = [];
const parts = [];
while ( node.type === 'MemberExpression' ) {
if ( node.computed ) return null;
parts.unshift( node.property.name );

17
src/finalisers/amd.js

@ -1,10 +1,11 @@
import { getName, quoteId } from '../utils/map-helpers.js';
import { getName, quotePath } from '../utils/map-helpers.js';
import getInteropBlock from './shared/getInteropBlock.js';
import getExportBlock from './shared/getExportBlock.js';
import esModuleExport from './shared/esModuleExport.js';
export default function amd ( bundle, magicString, { exportMode, indentString }, options ) {
let deps = bundle.externalModules.map( quoteId );
let args = bundle.externalModules.map( getName );
export default function amd ( bundle, magicString, { exportMode, indentString, intro }, options ) {
const deps = bundle.externalModules.map( quotePath );
const args = bundle.externalModules.map( getName );
if ( exportMode === 'named' ) {
args.unshift( `exports` );
@ -16,17 +17,21 @@ export default function amd ( bundle, magicString, { exportMode, indentString },
( deps.length ? `[${deps.join( ', ' )}], ` : `` );
const useStrict = options.useStrict !== false ? ` 'use strict';` : ``;
const intro = `define(${params}function (${args.join( ', ' )}) {${useStrict}\n\n`;
const wrapperStart = `define(${params}function (${args.join( ', ' )}) {${useStrict}\n\n`;
// var foo__default = 'default' in foo ? foo['default'] : foo;
const interopBlock = getInteropBlock( bundle );
if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' );
if ( intro ) magicString.prepend( intro );
const exportBlock = getExportBlock( bundle.entryModule, exportMode );
if ( exportBlock ) magicString.append( '\n\n' + exportBlock );
if ( exportMode === 'named' ) magicString.append( `\n\n${esModuleExport}` );
if ( options.outro ) magicString.append( `\n${options.outro}` );
return magicString
.indent( indentString )
.append( '\n\n});' )
.prepend( intro );
.prepend( wrapperStart );
}

36
src/finalisers/cjs.js

@ -1,30 +1,41 @@
import getExportBlock from './shared/getExportBlock.js';
import esModuleExport from './shared/esModuleExport.js';
export default function cjs ( bundle, magicString, { exportMode }, options ) {
let intro = options.useStrict === false ? `` : `'use strict';\n\n`;
export default function cjs ( bundle, magicString, { exportMode, intro }, options ) {
intro = ( options.useStrict === false ? intro : `'use strict';\n\n${intro}` ) +
( exportMode === 'named' ? `${esModuleExport}\n\n` : '' );
const hasDefaultImport = bundle.externalModules.some( mod => mod.declarations.default);
let needsInterop = false;
if (hasDefaultImport) {
intro += `function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }\n\n`;
}
const varOrConst = bundle.varOrConst;
// TODO handle empty imports, once they're supported
const importBlock = bundle.externalModules
.map( module => {
if ( module.declarations.default ) {
if (module.exportsNames) {
return `var ${module.name} = require('${module.id}');` +
`\nvar ${module.name}__default = _interopDefault(${module.name});`;
} else {
return `var ${module.name} = _interopDefault(require('${module.id}'));`;
if ( module.exportsNamespace ) {
return `${varOrConst} ${module.name} = require('${module.path}');` +
`\n${varOrConst} ${module.name}__default = ${module.name}['default'];`;
}
needsInterop = true;
if ( module.exportsNames ) {
return `${varOrConst} ${module.name} = require('${module.path}');` +
`\n${varOrConst} ${module.name}__default = _interopDefault(${module.name});`;
}
return `${varOrConst} ${module.name} = _interopDefault(require('${module.path}'));`;
} else {
return `var ${module.name} = require('${module.id}');`;
return `${varOrConst} ${module.name} = require('${module.path}');`;
}
})
.join( '\n' );
if ( needsInterop ) {
intro += `function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }\n\n`;
}
if ( importBlock ) {
intro += importBlock + '\n\n';
}
@ -33,6 +44,7 @@ export default function cjs ( bundle, magicString, { exportMode }, options ) {
const exportBlock = getExportBlock( bundle.entryModule, exportMode, 'module.exports =' );
if ( exportBlock ) magicString.append( '\n\n' + exportBlock );
if ( options.outro ) magicString.append( `\n${options.outro}` );
return magicString;
}

30
src/finalisers/es6.js → src/finalisers/es.js

@ -4,16 +4,26 @@ function notDefault ( name ) {
return name !== 'default';
}
export default function es6 ( bundle, magicString ) {
export default function es ( bundle, magicString, { intro }, options ) {
const importBlock = bundle.externalModules
.map( module => {
const specifiers = [];
const specifiersList = [specifiers];
const importedNames = keys( module.declarations )
.filter( name => name !== '*' && name !== 'default' );
.filter( name => name !== '*' && name !== 'default' )
.map( name => {
const declaration = module.declarations[ name ];
if ( declaration.name === declaration.safeName ) return declaration.name;
return `${declaration.name} as ${declaration.safeName}`;
});
if ( module.declarations.default ) {
specifiers.push( module.name );
if ( module.exportsNamespace ) {
specifiersList.push([ `${module.name}__default` ]);
} else {
specifiers.push( module.name );
}
}
const namespaceSpecifier = module.declarations['*'] ? `* as ${module.name}` : null;
@ -32,16 +42,15 @@ export default function es6 ( bundle, magicString ) {
return specifiersList
.map( specifiers =>
specifiers.length ?
`import ${specifiers.join( ', ' )} from '${module.id}';` :
`import '${module.id}';`
`import ${specifiers.join( ', ' )} from '${module.path}';` :
`import '${module.path}';`
)
.join( '\n' );
})
.join( '\n' );
if ( importBlock ) {
magicString.prepend( importBlock + '\n\n' );
}
if ( importBlock ) intro += importBlock + '\n\n';
if ( intro ) magicString.prepend( intro );
const module = bundle.entryModule;
@ -61,9 +70,8 @@ export default function es6 ( bundle, magicString ) {
exportBlock += `export default ${module.traceExport( 'default' ).render( true )};`;
}
if ( exportBlock ) {
magicString.append( '\n\n' + exportBlock.trim() );
}
if ( exportBlock ) magicString.append( '\n\n' + exportBlock.trim() );
if ( options.outro ) magicString.append( `\n${options.outro}` );
return magicString.trim();
}

27
src/finalisers/iife.js

@ -5,7 +5,7 @@ import getExportBlock from './shared/getExportBlock.js';
import getGlobalNameMaker from './shared/getGlobalNameMaker.js';
function setupNamespace ( keypath ) {
let parts = keypath.split( '.' ); // TODO support e.g. `foo['something-hyphenated']`?
const parts = keypath.split( '.' ); // TODO support e.g. `foo['something-hyphenated']`?
parts.pop();
@ -16,15 +16,15 @@ function setupNamespace ( keypath ) {
.join( '\n' ) + '\n';
}
export default function iife ( bundle, magicString, { exportMode, indentString }, options ) {
export default function iife ( bundle, magicString, { exportMode, indentString, intro }, options ) {
const globalNameMaker = getGlobalNameMaker( options.globals || blank(), bundle.onwarn );
const name = options.moduleName;
const isNamespaced = name && ~name.indexOf( '.' );
let dependencies = bundle.externalModules.map( globalNameMaker );
const dependencies = bundle.externalModules.map( globalNameMaker );
let args = bundle.externalModules.map( getName );
const args = bundle.externalModules.map( getName );
if ( exportMode !== 'none' && !name ) {
throw new Error( 'You must supply options.moduleName for IIFE bundles' );
@ -35,28 +35,31 @@ export default function iife ( bundle, magicString, { exportMode, indentString }
args.unshift( 'exports' );
}
const useStrict = options.useStrict !== false ? `'use strict';` : ``;
const useStrict = options.useStrict !== false ? `${indentString}'use strict';\n\n` : ``;
let intro = `(function (${args}) {\n`;
let outro = `\n\n}(${dependencies}));`;
let wrapperIntro = `(function (${args}) {\n${useStrict}`;
const wrapperOutro = `\n\n}(${dependencies}));`;
if ( exportMode === 'default' ) {
intro = ( isNamespaced ? `this.` : `var ` ) + `${name} = ${intro}`;
wrapperIntro = ( isNamespaced ? `this.` : `${bundle.varOrConst} ` ) + `${name} = ${wrapperIntro}`;
}
if ( isNamespaced ) {
intro = setupNamespace( name ) + intro;
wrapperIntro = setupNamespace( name ) + wrapperIntro;
}
// var foo__default = 'default' in foo ? foo['default'] : foo;
const interopBlock = getInteropBlock( bundle );
if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' );
if ( useStrict ) magicString.prepend( useStrict + '\n\n' );
if ( intro ) magicString.prepend( intro );
const exportBlock = getExportBlock( bundle.entryModule, exportMode );
if ( exportBlock ) magicString.append( '\n\n' + exportBlock );
if ( options.outro ) magicString.append( `\n${options.outro}` );
return magicString
.indent( indentString )
.prepend( intro )
.append( outro );
.prepend( wrapperIntro )
.append( wrapperOutro );
}

4
src/finalisers/index.js

@ -1,7 +1,7 @@
import amd from './amd.js';
import cjs from './cjs.js';
import es6 from './es6.js';
import es from './es.js';
import iife from './iife.js';
import umd from './umd.js';
export default { amd, cjs, es6, iife, umd };
export default { amd, cjs, es, iife, umd };

1
src/finalisers/shared/esModuleExport.js

@ -0,0 +1 @@
export default `Object.defineProperty(exports, '__esModule', { value: true });`;

16
src/finalisers/shared/getInteropBlock.js

@ -1,11 +1,17 @@
export default function getInteropBlock ( bundle ) {
return bundle.externalModules
.map( module => {
return module.declarations.default ?
( module.exportsNames ?
`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;
if ( !module.declarations.default ) return null;
if ( module.exportsNamespace ) {
return `${bundle.varOrConst} ${module.name}__default = ${module.name}['default'];`;
}
if ( module.exportsNames ) {
return `${bundle.varOrConst} ${module.name}__default = 'default' in ${module.name} ? ${module.name}['default'] : ${module.name};`;
}
return `${module.name} = 'default' in ${module.name} ? ${module.name}['default'] : ${module.name};`;
})
.filter( Boolean )
.join( '\n' );

27
src/finalisers/umd.js

@ -1,8 +1,9 @@
import { blank } from '../utils/object.js';
import { getName, quoteId, req } from '../utils/map-helpers.js';
import { getName, quotePath, req } from '../utils/map-helpers.js';
import getInteropBlock from './shared/getInteropBlock.js';
import getExportBlock from './shared/getExportBlock.js';
import getGlobalNameMaker from './shared/getGlobalNameMaker.js';
import esModuleExport from './shared/esModuleExport.js';
function setupNamespace ( name ) {
const parts = name.split( '.' );
@ -15,18 +16,20 @@ function setupNamespace ( name ) {
.join( ', ' );
}
export default function umd ( bundle, magicString, { exportMode, indentString }, options ) {
const wrapperOutro = '\n\n})));';
export default function umd ( bundle, magicString, { exportMode, indentString, intro }, options ) {
if ( exportMode !== 'none' && !options.moduleName ) {
throw new Error( 'You must supply options.moduleName for UMD bundles' );
}
const globalNameMaker = getGlobalNameMaker( options.globals || blank(), bundle.onwarn );
let amdDeps = bundle.externalModules.map( quoteId );
let cjsDeps = bundle.externalModules.map( req );
let globalDeps = bundle.externalModules.map( module => `global.${globalNameMaker( module )}` );
const amdDeps = bundle.externalModules.map( quotePath );
const cjsDeps = bundle.externalModules.map( req );
const globalDeps = bundle.externalModules.map( module => `global.${globalNameMaker( module )}` );
let args = bundle.externalModules.map( getName );
const args = bundle.externalModules.map( getName );
if ( exportMode === 'named' ) {
amdDeps.unshift( `'exports'` );
@ -53,12 +56,12 @@ export default function umd ( bundle, magicString, { exportMode, indentString },
exports.noConflict = function() { global.${options.moduleName} = current; return exports; };
})()` : `(${defaultExport}factory(${globalDeps}))`;
const intro =
const wrapperIntro =
`(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? ${cjsExport}factory(${cjsDeps.join( ', ' )}) :
typeof define === 'function' && define.amd ? define(${amdParams}factory) :
${globalExport};
}(this, function (${args}) {${useStrict}
}(this, (function (${args}) {${useStrict}
`.replace( /^\t\t/gm, '' ).replace( /^\t/gm, magicString.getIndentString() );
@ -66,12 +69,16 @@ export default function umd ( bundle, magicString, { exportMode, indentString },
const interopBlock = getInteropBlock( bundle );
if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' );
if ( intro ) magicString.prepend( intro );
const exportBlock = getExportBlock( bundle.entryModule, exportMode );
if ( exportBlock ) magicString.append( '\n\n' + exportBlock );
if ( exportMode === 'named' ) magicString.append( `\n\n${esModuleExport}` );
if ( options.outro ) magicString.append( `\n${options.outro}` );
return magicString
.trim()
.indent( indentString )
.append( '\n\n}));' )
.prepend( intro );
.append( wrapperOutro )
.prepend( wrapperIntro );
}

50
src/rollup.js

@ -1,7 +1,7 @@
import Promise from 'es6-promise/lib/es6-promise/promise.js';
import { basename } from './utils/path.js';
import { writeFile } from './utils/fs.js';
import { keys } from './utils/object.js';
import { assign, keys } from './utils/object.js';
import { mapSequence } from './utils/promise.js';
import validateKeys from './utils/validateKeys.js';
import SOURCEMAPPING_URL from './utils/sourceMappingURL.js';
import Bundle from './Bundle.js';
@ -9,7 +9,10 @@ import Bundle from './Bundle.js';
export const VERSION = '<@VERSION@>';
const ALLOWED_KEYS = [
'acorn',
'banner',
'cache',
'context',
'dest',
'entry',
'exports',
@ -24,8 +27,12 @@ const ALLOWED_KEYS = [
'noConflict',
'onwarn',
'outro',
'paths',
'plugins',
'preferConst',
'sourceMap',
'sourceMapFile',
'targets',
'treeshake',
'useStrict'
];
@ -48,23 +55,36 @@ export function rollup ( options ) {
const bundle = new Bundle( options );
return bundle.build().then( () => {
return {
function generate ( options ) {
const rendered = bundle.render( options );
bundle.plugins.forEach( plugin => {
if ( plugin.ongenerate ) {
plugin.ongenerate( assign({
bundle: result
}, options ), rendered);
}
});
return rendered;
}
const result = {
imports: bundle.externalModules.map( module => module.id ),
exports: keys( bundle.entryModule.exports ),
modules: bundle.orderedModules.map( module => {
return { id: module.id };
}),
modules: bundle.orderedModules.map( module => module.toJSON() ),
generate: options => bundle.render( options ),
generate,
write: options => {
if ( !options || !options.dest ) {
throw new Error( 'You must supply options.dest to bundle.write' );
}
const dest = options.dest;
let { code, map } = bundle.render( options );
const output = generate( options );
let { code, map } = output;
let promises = [];
const promises = [];
if ( options.sourceMap ) {
let url;
@ -76,12 +96,20 @@ export function rollup ( options ) {
promises.push( writeFile( dest + '.map', map.toString() ) );
}
code += `\n//# ${SOURCEMAPPING_URL}=${url}`;
code += `\n//# ${SOURCEMAPPING_URL}=${url}\n`;
}
promises.push( writeFile( dest, code ) );
return Promise.all( promises );
return Promise.all( promises ).then( () => {
return mapSequence( bundle.plugins.filter( plugin => plugin.onwrite ), plugin => {
return Promise.resolve( plugin.onwrite( assign({
bundle: result
}, options ), output));
});
});
}
};
return result;
});
}

7
src/utils/array.js

@ -0,0 +1,7 @@
export function find ( array, fn ) {
for ( let i = 0; i < array.length; i += 1 ) {
if ( fn( array[i], i ) ) return array[i];
}
return null;
}

117
src/utils/collapseSourcemaps.js

@ -1,44 +1,61 @@
import { encode, decode } from 'sourcemap-codec';
function Source ( index ) {
this.isOriginal = true;
this.index = index;
}
import { encode } from 'sourcemap-codec';
import { dirname, relative, resolve } from './path.js';
class Source {
constructor ( filename, content ) {
this.isOriginal = true;
this.filename = filename;
this.content = content;
}
Source.prototype = {
traceSegment ( line, column, name ) {
return { line, column, name, index: this.index };
return { line, column, name, source: this };
}
};
function Link ( map, sources ) {
if ( !map ) throw new Error( 'Cannot generate a sourcemap if non-sourcemap-generating transformers are used' );
this.sources = sources;
this.names = map.names;
this.mappings = decode( map.mappings );
}
Link.prototype = { // TODO bring into line with others post-https://github.com/rollup/rollup/pull/386
class Link {
constructor ( map, sources ) {
this.sources = sources;
this.names = map.names;
this.mappings = map.mappings;
}
traceMappings () {
let names = [];
const sources = [];
const sourcesContent = [];
const names = [];
const mappings = this.mappings.map( line => {
let tracedLine = [];
const tracedLine = [];
line.forEach( segment => {
const source = this.sources[ segment[1] ];
const traced = source.traceSegment( segment[2], segment[3], this.names[ segment[4] ] );
if ( traced ) {
let sourceIndex = null;
let nameIndex = null;
segment = [
segment[0],
traced.index,
null,
traced.line,
traced.column
];
// newer sources are more likely to be used, so search backwards.
sourceIndex = sources.lastIndexOf( traced.source.filename );
if ( sourceIndex === -1 ) {
sourceIndex = sources.length;
sources.push( traced.source.filename );
sourcesContent[ sourceIndex ] = traced.source.content;
} else if ( sourcesContent[ sourceIndex ] == null ) {
sourcesContent[ sourceIndex ] = traced.source.content;
} else if ( traced.source.content != null && sourcesContent[ sourceIndex ] !== traced.source.content ) {
throw new Error( `Multiple conflicting contents for sourcemap source ${source.filename}` );
}
segment[1] = sourceIndex;
if ( traced.name ) {
nameIndex = names.indexOf( traced.name );
if ( nameIndex === -1 ) {
@ -56,8 +73,8 @@ Link.prototype = { // TODO bring into line with others post-https://github.com/r
return tracedLine;
});
return { names, mappings };
},
return { sources, sourcesContent, names, mappings };
}
traceSegment ( line, column, name ) {
const segments = this.mappings[ line ];
@ -79,31 +96,69 @@ Link.prototype = { // TODO bring into line with others post-https://github.com/r
return null;
}
};
}
export default function collapseSourcemaps ( file, map, modules, bundleSourcemapChain, onwarn ) {
const moduleSources = modules.filter( module => !module.excludeFromSourcemap ).map( module => {
let sourceMapChain = module.sourceMapChain;
let source;
if ( module.originalSourceMap == null ) {
source = new Source( module.id, module.originalCode );
} else {
const sources = module.originalSourceMap.sources;
const sourcesContent = module.originalSourceMap.sourcesContent || [];
if ( sources == null || ( sources.length <= 1 && sources[0] == null ) ) {
source = new Source( module.id, sourcesContent[0] );
sourceMapChain = [ module.originalSourceMap ].concat( sourceMapChain );
} else {
// TODO indiscriminately treating IDs and sources as normal paths is probably bad.
const directory = dirname( module.id ) || '.';
const sourceRoot = module.originalSourceMap.sourceRoot || '.';
const baseSources = sources.map( (source, i) => {
return new Source( resolve( directory, sourceRoot, source ), sourcesContent[i] );
});
source = new Link( module.originalSourceMap, baseSources );
}
}
sourceMapChain.forEach( map => {
if ( map.missing ) {
onwarn( `Sourcemap is likely to be incorrect: a plugin${map.plugin ? ` ('${map.plugin}')` : ``} was used to transform files, but didn't generate a sourcemap for the transformation. Consult https://github.com/rollup/rollup/wiki/Troubleshooting and the plugin documentation for more information` );
export default function collapseSourcemaps ( map, modules, bundleSourcemapChain ) {
const sources = modules.map( ( module, i ) => {
let source = new Source( i );
map = {
names: [],
mappings: ''
};
}
module.sourceMapChain.forEach( map => {
source = new Link( map, [ source ]);
});
return source;
});
let source = new Link( map, sources );
let source = new Link( map, moduleSources );
bundleSourcemapChain.forEach( map => {
source = new Link( map, [ source ] );
});
const { names, mappings } = source.traceMappings();
let { sources, sourcesContent, names, mappings } = source.traceMappings();
if ( file ) {
const directory = dirname( file );
sources = sources.map( source => relative( directory, source ) );
}
// we re-use the `map` object because it has convenient toString/toURL methods
map.sourcesContent = modules.map( module => module.originalCode );
map.mappings = encode( mappings );
map.sources = sources;
map.sourcesContent = sourcesContent;
map.names = names;
map.mappings = encode( mappings );
return map;
}

4
src/utils/defaults.js

@ -20,7 +20,7 @@ export function resolveId ( importee, importer ) {
if ( typeof process === 'undefined' ) throw new Error( `It looks like you're using Rollup in a non-Node.js environment. This means you must supply a plugin with custom resolveId and load functions. See https://github.com/rollup/rollup/wiki/Plugins for more information` );
// absolute paths are left untouched
if ( isAbsolute( importee ) ) return addJsExtensionIfNecessary( importee );
if ( isAbsolute( importee ) ) return addJsExtensionIfNecessary( resolve( importee ) );
// if this is the entry point, resolve against cwd
if ( importer === undefined ) return addJsExtensionIfNecessary( resolve( process.cwd(), importee ) );
@ -33,7 +33,7 @@ export function resolveId ( importee, importer ) {
export function makeOnwarn () {
let warned = blank();
const warned = blank();
return msg => {
if ( msg in warned ) return;

2
src/utils/first.js

@ -1,5 +1,3 @@
import Promise from 'es6-promise/lib/es6-promise/promise.js';
// Return the first non-falsy result from an array of
// maybe-sync, maybe-promise-returning functions
export default function first ( candidates ) {

1
src/utils/fs.js

@ -1,4 +1,3 @@
import Promise from 'es6-promise/lib/es6-promise/promise.js';
import * as fs from 'fs';
import { dirname } from './path.js';

5
src/utils/getExportMode.js

@ -4,7 +4,7 @@ function badExports ( option, keys ) {
throw new Error( `'${option}' was specified for options.exports, but entry module has following exports: ${keys.join(', ')}` );
}
export default function getExportMode ( bundle, exportMode ) {
export default function getExportMode ( bundle, exportMode, moduleName ) {
const exportKeys = keys( bundle.entryModule.exports )
.concat( keys( bundle.entryModule.reexports ) )
.concat( bundle.entryModule.exportAllSources ); // not keys, but makes our job easier this way
@ -23,6 +23,9 @@ export default function getExportMode ( bundle, exportMode ) {
} else if ( exportKeys.length === 1 && exportKeys[0] === 'default' ) {
exportMode = 'default';
} else {
if ( bundle.entryModule.exports.default ) {
bundle.onwarn( `Using named and default exports together. Consumers of your bundle will have to use ${moduleName || 'bundle'}['default'] to access the default export, which may not be what you want. Use \`exports: 'named'\` to disable this warning. See https://github.com/rollup/rollup/wiki/JavaScript-API#exports for more information` );
}
exportMode = 'named';
}
}

2
src/utils/getIndentString.js

@ -1,5 +1,5 @@
export default function getIndentString ( magicString, options ) {
if ( !( 'indent' in options ) || options.indent === true ) {
if ( options.indent === true ) {
return magicString.getIndentString();
}

2
src/utils/makeLegalIdentifier.js

@ -3,7 +3,7 @@ import { blank } from './object.js';
const reservedWords = 'break case class catch const continue debugger default delete do else export extends finally for function if import in instanceof let new return super switch this throw try typeof var void while with yield enum await implements package protected static interface private public'.split( ' ' );
const builtins = 'Infinity NaN undefined null true false eval uneval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Symbol Error EvalError InternalError RangeError ReferenceError SyntaxError TypeError URIError Number Math Date String RegExp Array Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array Map Set WeakMap WeakSet SIMD ArrayBuffer DataView JSON Promise Generator GeneratorFunction Reflect Proxy Intl'.split( ' ' );
let blacklisted = blank();
const blacklisted = blank();
reservedWords.concat( builtins ).forEach( word => blacklisted[ word ] = true );

6
src/utils/map-helpers.js

@ -2,10 +2,10 @@ export function getName ( x ) {
return x.name;
}
export function quoteId ( x ) {
return `'${x.id}'`;
export function quotePath ( x ) {
return `'${x.path}'`;
}
export function req ( x ) {
return `require('${x.id}')`;
return `require('${x.path}')`;
}

3
src/utils/normalizePlatform.js

@ -1,3 +0,0 @@
export function unixizePath ( path ) {
return path.split( /[\/\\]/ ).join( '/' );
}

10
src/utils/object.js

@ -7,3 +7,13 @@ export function blank () {
export function forOwn ( object, func ) {
Object.keys( object ).forEach( key => func( object[ key ], key ) );
}
export function assign ( target, ...sources ) {
sources.forEach( source => {
for ( const key in source ) {
if ( source.hasOwnProperty( key ) ) target[ key ] = source[ key ];
}
});
return target;
}

68
src/utils/path.js

@ -1,5 +1,3 @@
// TODO does this all work on windows?
export const absolutePath = /^(?:\/|(?:[A-Za-z]:)?[\\|\/])/;
export const relativePath = /^\.?\.\//;
@ -11,68 +9,8 @@ export function isRelative ( path ) {
return relativePath.test( path );
}
export function basename ( path ) {
return path.split( /(\/|\\)/ ).pop();
}
export function dirname ( path ) {
const match = /(\/|\\)[^\/\\]*$/.exec( path );
if ( !match ) return '.';
const dir = path.slice( 0, -match[0].length );
// If `dir` is the empty string, we're at root.
return dir ? dir : '/';
}
export function extname ( path ) {
const match = /\.[^\.]+$/.exec( basename( path ) );
if ( !match ) return '';
return match[0];
}
export function relative ( from, to ) {
const fromParts = from.split( /[\/\\]/ ).filter( Boolean );
const toParts = to.split( /[\/\\]/ ).filter( Boolean );
while ( fromParts[0] && toParts[0] && fromParts[0] === toParts[0] ) {
fromParts.shift();
toParts.shift();
}
while ( toParts[0] === '.' || toParts[0] === '..' ) {
const toPart = toParts.shift();
if ( toPart === '..' ) {
fromParts.pop();
}
}
while ( fromParts.pop() ) {
toParts.unshift( '..' );
}
return toParts.join( '/' );
export function normalize ( path ) {
return path.replace( /\\/g, '/' );
}
export function resolve ( ...paths ) {
let resolvedParts = paths.shift().split( /[\/\\]/ );
paths.forEach( path => {
if ( isAbsolute( path ) ) {
resolvedParts = path.split( /[\/\\]/ );
} else {
const parts = path.split( /[\/\\]/ );
while ( parts[0] === '.' || parts[0] === '..' ) {
const part = parts.shift();
if ( part === '..' ) {
resolvedParts.pop();
}
}
resolvedParts.push.apply( resolvedParts, parts );
}
});
return resolvedParts.join( '/' ); // TODO windows...
}
export * from 'path';

4
src/utils/promise.js

@ -1,7 +1,5 @@
import Promise from 'es6-promise/lib/es6-promise/promise.js';
export function mapSequence ( array, fn ) {
let results = [];
const results = [];
let promise = Promise.resolve();
function next ( member, i ) {

4
src/utils/pureFunctions.js

@ -1,9 +1,9 @@
let pureFunctions = {};
const pureFunctions = {};
const arrayTypes = 'Array Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array'.split( ' ' );
const simdTypes = 'Int8x16 Int16x8 Int32x4 Float32x4 Float64x2'.split( ' ' );
const simdMethods = 'abs add and bool check div equal extractLane fromFloat32x4 fromFloat32x4Bits fromFloat64x2 fromFloat64x2Bits fromInt16x8Bits fromInt32x4 fromInt32x4Bits fromInt8x16Bits greaterThan greaterThanOrEqual lessThan lessThanOrEqual load max maxNum min minNum mul neg not notEqual or reciprocalApproximation reciprocalSqrtApproximation replaceLane select selectBits shiftLeftByScalar shiftRightArithmeticByScalar shiftRightLogicalByScalar shuffle splat sqrt store sub swizzle xor'.split( ' ' );
let allSimdMethods = [];
const allSimdMethods = [];
simdTypes.forEach( t => {
simdMethods.forEach( m => {
allSimdMethods.push( `SIMD.${t}.${m}` );

6
src/utils/run.js

@ -75,6 +75,10 @@ export default function run ( node, scope, statement, strongDependencies, force
}
}
else if ( node.type === 'DebuggerStatement' ) {
hasSideEffect = true;
}
else if ( node.type === 'ThrowStatement' ) {
// we only care about errors thrown at the top level, otherwise
// any function with error checking gets included if called
@ -100,7 +104,7 @@ export default function run ( node, scope, statement, strongDependencies, force
} else {
declaration = statement.module.trace( subject.name );
if ( !declaration || declaration.isExternal || declaration.isUsed ) {
if ( !declaration || declaration.isExternal || declaration.isUsed || ( declaration.original && declaration.original.isUsed ) ) {
hasSideEffect = true;
}
}

45
src/utils/transform.js

@ -1,21 +1,22 @@
import Promise from 'es6-promise/lib/es6-promise/promise.js';
import { decode } from 'sourcemap-codec';
export default function transform ( source, id, transformers ) {
let sourceMapChain = [];
export default function transform ( source, id, plugins ) {
const sourceMapChain = [];
if ( typeof source === 'string' ) {
source = {
code: source,
ast: null
};
const originalSourceMap = typeof source.map === 'string' ? JSON.parse( source.map ) : source.map;
if ( originalSourceMap && typeof originalSourceMap.mappings === 'string' ) {
originalSourceMap.mappings = decode( originalSourceMap.mappings );
}
let originalCode = source.code;
const originalCode = source.code;
let ast = source.ast;
return transformers.reduce( ( promise, transformer ) => {
return plugins.reduce( ( promise, plugin ) => {
return promise.then( previous => {
return Promise.resolve( transformer( previous, id ) ).then( result => {
if ( !plugin.transform ) return previous;
return Promise.resolve( plugin.transform( previous, id ) ).then( result => {
if ( result == null ) return previous;
if ( typeof result === 'string' ) {
@ -30,19 +31,25 @@ export default function transform ( source, id, transformers ) {
result.map = JSON.parse( result.map );
}
sourceMapChain.push( result.map );
if ( result.map && typeof result.map.mappings === 'string' ) {
result.map.mappings = decode( result.map.mappings );
}
sourceMapChain.push( result.map || { missing: true, plugin: plugin.name }); // lil' bit hacky but it works
ast = result.ast;
return result.code;
});
}).catch( err => {
if ( !err.rollupTransform ) {
err.rollupTransform = true;
err.id = id;
err.plugin = plugin.name;
err.message = `Error transforming ${id}${plugin.name ? ` with '${plugin.name}' plugin` : ''}: ${err.message}`;
}
throw err;
});
}, Promise.resolve( source.code ) )
.then( code => ({ code, originalCode, ast, sourceMapChain }) )
.catch( err => {
err.id = id;
err.message = `Error loading ${id}: ${err.message}`;
throw err;
});
.then( code => ({ code, originalCode, originalSourceMap, ast, sourceMapChain }) );
}

24
src/utils/transformBundle.js

@ -1,6 +1,18 @@
export default function transformBundle ( code, transformers, sourceMapChain ) {
return transformers.reduce( ( code, transformer ) => {
let result = transformer( code );
import { decode } from 'sourcemap-codec';
export default function transformBundle ( code, plugins, sourceMapChain ) {
return plugins.reduce( ( code, plugin ) => {
if ( !plugin.transformBundle ) return code;
let result;
try {
result = plugin.transformBundle( code );
} catch ( err ) {
err.plugin = plugin.name;
err.message = `Error transforming bundle${plugin.name ? ` with '${plugin.name}' plugin` : ''}: ${err.message}`;
throw err;
}
if ( result == null ) return code;
@ -11,7 +23,11 @@ export default function transformBundle ( code, transformers, sourceMapChain ) {
};
}
const map = typeof result.map === 'string' ? JSON.parse( result.map ) : map;
const map = typeof result.map === 'string' ? JSON.parse( result.map ) : result.map;
if ( map && typeof map.mappings === 'string' ) {
map.mappings = decode( map.mappings );
}
sourceMapChain.push( map );
return result.code;

20
test/.babelrc

@ -1,20 +0,0 @@
{
"whitelist": [
"es6.arrowFunctions",
"es6.blockScoping",
"es6.classes",
"es6.constants",
"es6.destructuring",
"es6.modules",
"es6.parameters",
"es6.properties.shorthand",
"es6.spread",
"es6.templateLiterals"
],
"loose": [
"es6.classes",
"es6.destructuring"
],
"compact": false,
"sourceMap": true
}

9
test/.eslintrc

@ -0,0 +1,9 @@
{
"rules": {
"no-console": [ 0 ],
"no-unused-vars": [ "error", { "vars": "all", "args": "none" } ]
},
"env": {
"mocha": true
}
}

2
test/cli/banner-intro-outro-footer/_config.js

@ -1,4 +1,4 @@
module.exports = {
description: 'adds banner/intro/outro/footer',
command: 'rollup -i main.js -f iife --banner "// banner" --intro "// intro" --outro "// outro" --footer "// footer"'
command: 'rollup -i main.js -f iife --indent --banner "// banner" --intro "// intro" --outro "// outro" --footer "// footer"'
};

13
test/cli/config-cwd-case-insensitive/_config.js

@ -0,0 +1,13 @@
var os = require( 'os' );
function toggleCase ( s ) {
return ( s == s.toLowerCase() ) ? s.toUpperCase() : s.toLowerCase();
}
module.exports = {
skip: os.platform() !== 'win32',
description: "can load config with cwd that doesn't match realpath",
command: 'rollup -c',
cwd: __dirname.replace( /^[A-Z]:\\/i, toggleCase ),
execute: true
};

1
test/cli/config-cwd-case-insensitive/main.js

@ -0,0 +1 @@
assert.equal( ANSWER, 42 );

9
test/cli/config-cwd-case-insensitive/rollup.config.js

@ -0,0 +1,9 @@
var replace = require( 'rollup-plugin-replace' );
module.exports = {
entry: 'main.js',
format: 'cjs',
plugins: [
replace({ 'ANSWER': 42 })
]
};

4
test/cli/config-external-function/_config.js

@ -0,0 +1,4 @@
module.exports = {
description: 'external option gets passed from config',
command: 'rollup -c -e assert,external-module'
};

8
test/cli/config-external-function/_expected.js

@ -0,0 +1,8 @@
'use strict';
var ___config_js = require('./_config.js');
var assert = require('assert');
var externalModule = require('external-module');
assert.ok( ___config_js.execute );
externalModule.method();

6
test/cli/config-external-function/main.js

@ -0,0 +1,6 @@
import { execute } from './_config.js';
import { ok } from 'assert';
import { method } from 'external-module';
ok( execute );
method();

21
test/cli/config-external-function/rollup.config.js

@ -0,0 +1,21 @@
import assert from 'assert';
import { resolve } from 'path';
var config = resolve( './_config.js' );
export default {
entry: 'main.js',
format: 'cjs',
external: function ( id ) {
return id === config;
},
plugins: [
{
load: function ( id ) {
assert.notEqual( id, config );
}
}
]
};

5
test/cli/external-modules-auto-global/_config.js

@ -0,0 +1,5 @@
module.exports = {
description: 'populates options.external with --global keys',
command: 'rollup main.js --format iife --globals mathematics:Math',
execute: true
};

3
test/cli/external-modules-auto-global/main.js

@ -0,0 +1,3 @@
import { max } from 'mathematics';
assert.equal( max( 1, 2, 3 ), 3 );

2
test/cli/indent-none/_config.js

@ -1,5 +1,3 @@
var assert = require( 'assert' );
module.exports = {
description: 'disables indentation with --no-indent',
command: 'rollup main.js --format umd --no-indent'

4
test/cli/indent-none/_expected.js

@ -2,8 +2,8 @@
typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
typeof define === 'function' && define.amd ? define(factory) :
(factory());
}(this, function () { 'use strict';
}(this, (function () { 'use strict';
assert.equal( 1 + 1, 2 );
}));
})));

2
test/cli/module-name/_config.js

@ -1,4 +1,4 @@
module.exports = {
description: 'generates UMD export with correct moduleName',
command: 'rollup main.js --format umd --name myBundle'
command: 'rollup main.js --format umd --name myBundle --indent'
};

4
test/cli/module-name/_expected.js

@ -2,10 +2,10 @@
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.myBundle = factory());
}(this, function () { 'use strict';
}(this, (function () { 'use strict';
var main = 42;
return main;
}));
})));

4
test/cli/multiple-targets-shared-config/_config.js

@ -0,0 +1,4 @@
module.exports = {
description: 'uses shared config for each target',
command: 'rollup -c'
};

6
test/cli/multiple-targets-shared-config/_expected/cjs.js

@ -0,0 +1,6 @@
'use strict';
var main = 0;
module.exports = main;
//# sourceMappingURL=cjs.js.map

1
test/cli/multiple-targets-shared-config/_expected/cjs.js.map

@ -0,0 +1 @@
{"version":3,"file":"cjs.js","sources":["../main.js"],"sourcesContent":["export default 0;\n"],"names":[],"mappings":";;AAAA,WAAe,CAAC,CAAC,;;"}

4
test/cli/multiple-targets-shared-config/_expected/es.js

@ -0,0 +1,4 @@
var main = 0;
export default main;
//# sourceMappingURL=es.js.map

1
test/cli/multiple-targets-shared-config/_expected/es.js.map

@ -0,0 +1 @@
{"version":3,"file":"es.js","sources":["../main.js"],"sourcesContent":["export default 0;\n"],"names":[],"mappings":"AAAA,WAAe,CAAC,CAAC,;;"}

1
test/cli/multiple-targets-shared-config/main.js

@ -0,0 +1 @@
export default 0;

14
test/cli/multiple-targets-shared-config/rollup.config.js

@ -0,0 +1,14 @@
export default {
entry: 'main.js',
sourceMap: true,
targets: [
{
format: 'cjs',
dest: '_actual/cjs.js'
},
{
format: 'es',
dest: '_actual/es.js'
}
]
};

4
test/cli/multiple-targets/_config.js

@ -0,0 +1,4 @@
module.exports = {
description: 'generates multiple output files when multiple targets are specified',
command: 'rollup -c'
};

5
test/cli/multiple-targets/_expected/cjs.js

@ -0,0 +1,5 @@
'use strict';
var main = 0;
module.exports = main;

3
test/cli/multiple-targets/_expected/es.js

@ -0,0 +1,3 @@
var main = 0;
export default main;

1
test/cli/multiple-targets/main.js

@ -0,0 +1 @@
export default 0;

13
test/cli/multiple-targets/rollup.config.js

@ -0,0 +1,13 @@
export default {
entry: 'main.js',
targets: [
{
format: 'cjs',
dest: '_actual/cjs.js'
},
{
format: 'es',
dest: '_actual/es.js'
}
]
};

4
test/cli/no-conflict/_config.js

@ -0,0 +1,4 @@
module.exports = {
description: 'respects noConflict option',
command: 'rollup --config rollup.config.js'
};

16
test/cli/no-conflict/_expected.js

@ -0,0 +1,16 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(function() {
var current = global.conflictyName;
var exports = factory();
global.conflictyName = exports;
exports.noConflict = function() { global.conflictyName = current; return exports; };
})();
}(this, (function () { 'use strict';
var main = {};
return main;
})));

1
test/cli/no-conflict/main.js

@ -0,0 +1 @@
export default {};

6
test/cli/no-conflict/rollup.config.js

@ -0,0 +1,6 @@
module.exports = {
entry: 'main.js',
format: 'umd',
moduleName: 'conflictyName',
noConflict: true
};

4
test/cli/no-strict/_config.js

@ -0,0 +1,4 @@
module.exports = {
description: 'use no strict option',
command: 'rollup -i main.js -f iife --no-strict --indent'
};

4
test/cli/no-strict/_expected.js

@ -0,0 +1,4 @@
(function () {
console.log( 42 );
}());

1
test/cli/no-strict/main.js

@ -0,0 +1 @@
console.log( 42 );

2
test/cli/no-treeshake/_config.js

@ -1,4 +1,4 @@
module.exports = {
description: 'generates IIFE export with all code',
command: 'rollup main.js --format iife --name shakeless --no-treeshake'
command: 'rollup main.js --format iife --name shakeless --no-treeshake --indent'
};

5
test/cli/node-config-auto-prefix/_config.js

@ -0,0 +1,5 @@
module.exports = {
description: 'uses config file installed from npm',
command: 'rollup --config node:foo',
execute: true
};

11
test/cli/node-config-auto-prefix/_expected.js

@ -0,0 +1,11 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
global.myBundle = factory();
}(this, function () { 'use strict';
var main = 42;
return main;
}));

1
test/cli/node-config-auto-prefix/main.js

@ -0,0 +1 @@
assert.equal( ANSWER, 42 );

9
test/cli/node-config-auto-prefix/node_modules/rollup-config-foo/lib/config.js

@ -0,0 +1,9 @@
var replace = require( 'rollup-plugin-replace' );
module.exports = {
entry: 'main.js',
format: 'cjs',
plugins: [
replace({ 'ANSWER': 42 })
]
};

3
test/cli/node-config-auto-prefix/node_modules/rollup-config-foo/package.json

@ -0,0 +1,3 @@
{
"main": "lib/config.js"
}

5
test/cli/node-config/_config.js

@ -0,0 +1,5 @@
module.exports = {
description: 'uses config file installed from npm',
command: 'rollup --config node:foo',
execute: true
};

11
test/cli/node-config/_expected.js

@ -0,0 +1,11 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
global.myBundle = factory();
}(this, function () { 'use strict';
var main = 42;
return main;
}));

1
test/cli/node-config/main.js

@ -0,0 +1 @@
assert.equal( ANSWER, 42 );

9
test/cli/node-config/node_modules/foo/lib/config.js

@ -0,0 +1,9 @@
var replace = require( 'rollup-plugin-replace' );
module.exports = {
entry: 'main.js',
format: 'cjs',
plugins: [
replace({ 'ANSWER': 42 })
]
};

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

Loading…
Cancel
Save