From 2379fae6079246ddf97cc0185027da0b808d31cb Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 14:59:22 -0400 Subject: [PATCH 01/16] add bin dir to npm package --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index d95d776..f50198b 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "files": [ "src", "dist", + "bin", "README.md" ] } From cdeace10ca87a977d5a3117d1cd3b21db03d24a1 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 15:00:05 -0400 Subject: [PATCH 02/16] BREAKING: API methods take a single argument, and external modules must be declared as such --- README.md | 5 ++++- bin/runRollup.js | 7 +++++-- gobblefile.js | 2 +- src/Bundle.js | 15 +++++++++++++-- src/rollup.js | 17 +++++++++++------ src/utils/ensureArray.js | 5 +++++ src/utils/load.js | 6 ++++++ src/utils/resolvePath.js | 19 +++++++++++++++---- .../_config.js | 5 ++++- .../import-default-from-external/_config.js | 7 +++++-- .../import-named-from-external/_config.js | 7 +++++-- .../_config.js | 5 ++++- .../_config.js | 7 +++++-- .../imports-are-deconflicted-b/_config.js | 5 ++++- .../imports-are-deconflicted/_config.js | 5 ++++- .../shadowed-external-export/_config.js | 5 ++++- test/test.js | 18 +++++++++++++++--- 17 files changed, 110 insertions(+), 30 deletions(-) create mode 100644 src/utils/ensureArray.js create mode 100644 src/utils/load.js diff --git a/README.md b/README.md index 5b4237c..fdbc700 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,10 @@ This is not a trivial task. There are almost certainly a great many complex edge The example below is aspirational. It isn't yet implemented - it exists in the name of [README driven development](http://tom.preston-werner.com/2010/08/23/readme-driven-development.html). ```js -rollup.rollup( 'app.js', { +rollup.rollup({ + // The bundle's starting point + entry: 'app.js', + // Override the default path resolution resolvePath: function ( importee, importer ) { // return a string or a falsy value - if falsy, diff --git a/bin/runRollup.js b/bin/runRollup.js index e33ef4c..585c1fc 100644 --- a/bin/runRollup.js +++ b/bin/runRollup.js @@ -33,13 +33,16 @@ function bundle ( options, method ) { handleError({ code: 'MISSING_INPUT_OPTION' }); } - return rollup.rollup( options.input ).then( function ( bundle ) { + return rollup.rollup({ + entry: options.input + }).then( function ( bundle ) { var generateOptions = { + dest: options.output, format: options.format }; if ( options.output ) { - return bundle.write( options.output, generateOptions ); + return bundle.write( generateOptions ); } if ( options.sourcemap && options.sourcemap !== 'inline' ) { diff --git a/gobblefile.js b/gobblefile.js index cda2c18..fe4cde5 100644 --- a/gobblefile.js +++ b/gobblefile.js @@ -1,6 +1,6 @@ var gobble = require( 'gobble' ); -var selfhost = 1; +var selfhost = 0; module.exports = selfhost ? gobble( 'src' ) diff --git a/src/Bundle.js b/src/Bundle.js index 151f2a4..db13779 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -6,7 +6,9 @@ import Module from './Module'; import ExternalModule from './ExternalModule'; import finalisers from './finalisers/index'; import makeLegalIdentifier from './utils/makeLegalIdentifier'; +import ensureArray from './utils/ensureArray'; import { defaultResolver } from './utils/resolvePath'; +import { defaultLoader } from './utils/load'; function badExports ( option, keys ) { throw new Error( `'${option}' was specified for options.exports, but entry module has following exports: ${keys.join(', ')}` ); @@ -18,6 +20,15 @@ export default class Bundle { this.base = dirname( this.entryPath ); this.resolvePath = options.resolvePath || defaultResolver; + this.load = options.load || defaultLoader; + + this.resolvePathOptions = { + external: ensureArray( options.external ) + }; + + this.loadOptions = { + transform: ensureArray( options.transform ) + }; this.entryModule = null; this.modulePromises = {}; @@ -28,7 +39,7 @@ export default class Bundle { } fetchModule ( importee, importer ) { - return Promise.resolve( importer === null ? importee : this.resolvePath( importee, importer ) ) + return Promise.resolve( importer === null ? importee : this.resolvePath( importee, importer, this.resolvePathOptions ) ) .then( path => { if ( !path ) { // external module @@ -42,7 +53,7 @@ export default class Bundle { } if ( !has( this.modulePromises, path ) ) { - this.modulePromises[ path ] = readFile( path, { encoding: 'utf-8' }) + this.modulePromises[ path ] = Promise.resolve( this.load( path, this.loadOptions ) ) .then( source => { const module = new Module({ path, diff --git a/src/rollup.js b/src/rollup.js index d182df3..09baf6f 100644 --- a/src/rollup.js +++ b/src/rollup.js @@ -5,16 +5,21 @@ import Bundle from './Bundle'; let SOURCEMAPPING_URL = 'sourceMa'; SOURCEMAPPING_URL += 'ppingURL'; -export function rollup ( entry, options = {} ) { - const bundle = new Bundle({ - entry, - resolvePath: options.resolvePath - }); +export function rollup ( options ) { + if ( !options || !options.entry ) { + throw new Error( 'You must supply options.entry to rollup' ); + } + + const bundle = new Bundle( options ); return bundle.build().then( () => { return { generate: options => bundle.generate( options ), - write: ( dest, options = {} ) => { + write: options => { + if ( !options || !options.dest ) { + throw new Error( 'You must supply options.dest to bundle.write' ); + } + let { code, map } = bundle.generate({ dest, format: options.format, diff --git a/src/utils/ensureArray.js b/src/utils/ensureArray.js new file mode 100644 index 0000000..2669345 --- /dev/null +++ b/src/utils/ensureArray.js @@ -0,0 +1,5 @@ +export default function ensureArray ( thing ) { + if ( Array.isArray( thing ) ) return thing; + if ( thing == undefined ) return []; + return [ thing ]; +} diff --git a/src/utils/load.js b/src/utils/load.js new file mode 100644 index 0000000..1d8d826 --- /dev/null +++ b/src/utils/load.js @@ -0,0 +1,6 @@ +import { readFileSync } from 'sander'; + +export function defaultLoader ( path, options ) { + // TODO support plugins and transformers? + return readFileSync( path, { encoding: 'utf-8' }); +} diff --git a/src/utils/resolvePath.js b/src/utils/resolvePath.js index 4b07512..08de65d 100644 --- a/src/utils/resolvePath.js +++ b/src/utils/resolvePath.js @@ -1,11 +1,22 @@ import { dirname, isAbsolute, resolve } from 'path'; -export function defaultResolver ( importee, importer ) { +export function defaultResolver ( importee, importer, options ) { // absolute paths are left untouched if ( isAbsolute( importee ) ) return importee; - // external modules stay external - if ( importee[0] !== '.' ) return false; + // we try to resolve external modules + if ( importee[0] !== '.' ) { + // unless we want to keep it external, that is + if ( ~options.external.indexOf( importee ) ) return null; + + return resolveExternal( importee, importer, options ); + } return resolve( dirname( importer ), importee ).replace( /\.js$/, '' ) + '.js'; -} \ No newline at end of file +} + +function resolveExternal ( id, importer, options ) { + // for now, only node_modules is supported, and only jsnext:main + + throw new Error( "TODO" ); +} diff --git a/test/function/allows-external-modules-from-nested-module/_config.js b/test/function/allows-external-modules-from-nested-module/_config.js index 918a11a..d0c980e 100644 --- a/test/function/allows-external-modules-from-nested-module/_config.js +++ b/test/function/allows-external-modules-from-nested-module/_config.js @@ -1,3 +1,6 @@ module.exports = { - description: 'imports external modules from nested internal modules' + description: 'imports external modules from nested internal modules', + options: { + external: [ 'path' ] + } }; diff --git a/test/function/import-default-from-external/_config.js b/test/function/import-default-from-external/_config.js index d2d3885..98651b7 100644 --- a/test/function/import-default-from-external/_config.js +++ b/test/function/import-default-from-external/_config.js @@ -1,3 +1,6 @@ module.exports = { - description: 'imports default from external module' -}; \ No newline at end of file + description: 'imports default from external module', + options: { + external: [ 'path' ] + } +}; diff --git a/test/function/import-named-from-external/_config.js b/test/function/import-named-from-external/_config.js index 243412e..cab932b 100644 --- a/test/function/import-named-from-external/_config.js +++ b/test/function/import-named-from-external/_config.js @@ -1,3 +1,6 @@ module.exports = { - description: 'imports names from an external module' -}; \ No newline at end of file + description: 'imports names from an external module', + options: { + external: [ 'path' ] + } +}; diff --git a/test/function/import-namespace-from-external-module-renamed/_config.js b/test/function/import-namespace-from-external-module-renamed/_config.js index 86394b3..4cb00de 100644 --- a/test/function/import-namespace-from-external-module-renamed/_config.js +++ b/test/function/import-namespace-from-external-module-renamed/_config.js @@ -1,3 +1,6 @@ module.exports = { - description: 'imports a namespace from an external module and renames it' + description: 'imports a namespace from an external module and renames it', + options: { + external: [ 'path' ] + } }; diff --git a/test/function/import-namespace-from-external-module/_config.js b/test/function/import-namespace-from-external-module/_config.js index f107139..3704e5c 100644 --- a/test/function/import-namespace-from-external-module/_config.js +++ b/test/function/import-namespace-from-external-module/_config.js @@ -1,3 +1,6 @@ module.exports = { - description: 'imports a namespace from an external module' -}; \ No newline at end of file + description: 'imports a namespace from an external module', + options: { + external: [ 'path' ] + } +}; diff --git a/test/function/imports-are-deconflicted-b/_config.js b/test/function/imports-are-deconflicted-b/_config.js index a7c1041..eceba93 100644 --- a/test/function/imports-are-deconflicted-b/_config.js +++ b/test/function/imports-are-deconflicted-b/_config.js @@ -1,3 +1,6 @@ module.exports = { - description: 'deconflicts imports (redux)' + description: 'deconflicts imports (redux)', + options: { + external: [ 'path' ] + } }; diff --git a/test/function/imports-are-deconflicted/_config.js b/test/function/imports-are-deconflicted/_config.js index 49dbfd8..345f6b8 100644 --- a/test/function/imports-are-deconflicted/_config.js +++ b/test/function/imports-are-deconflicted/_config.js @@ -1,3 +1,6 @@ module.exports = { - description: 'deconflicts imports' + description: 'deconflicts imports', + options: { + external: [ 'path' ] + } }; diff --git a/test/function/shadowed-external-export/_config.js b/test/function/shadowed-external-export/_config.js index b39af37..2a744dc 100644 --- a/test/function/shadowed-external-export/_config.js +++ b/test/function/shadowed-external-export/_config.js @@ -1,3 +1,6 @@ module.exports = { - description: 'external modules are not shadowed' + description: 'external modules are not shadowed', + options: { + external: [ 'path' ] + } }; diff --git a/test/test.js b/test/test.js index 11ef072..5f71f6e 100644 --- a/test/test.js +++ b/test/test.js @@ -54,7 +54,11 @@ describe( 'rollup', function () { } ( config.skip ? it.skip : config.solo ? it.only : it )( dir, function () { - return rollup.rollup( FUNCTION + '/' + dir + '/main.js', extend( {}, config.options ) ) + var options = extend( {}, config.options, { + entry: FUNCTION + '/' + dir + '/main.js' + }) + + return rollup.rollup( options ) .then( function ( bundle ) { var unintendedError; @@ -142,7 +146,11 @@ describe( 'rollup', function () { config = { description: dir }; } - var bundlePromise = rollup.rollup( FORM + '/' + dir + '/main.js', extend( {}, config.options ) ); + var options = extend( {}, config.options, { + entry: FORM + '/' + dir + '/main.js' + }); + + var bundlePromise = rollup.rollup( options ); PROFILES.forEach( function ( profile ) { ( config.skip ? it.skip : config.solo ? it.only : it )( 'generates ' + profile.format, function () { @@ -172,7 +180,11 @@ describe( 'rollup', function () { describe( dir, function () { var config = require( SOURCEMAPS + '/' + dir + '/_config' ); - var bundlePromise = rollup.rollup( SOURCEMAPS + '/' + dir + '/main.js', extend( {}, config.options ) ); + var options = extend( {}, config.options, { + entry: SOURCEMAPS + '/' + dir + '/main.js' + }); + + var bundlePromise = rollup.rollup( options ); PROFILES.forEach( function ( profile ) { ( config.skip ? it.skip : config.solo ? it.only : it )( 'generates ' + profile.format, function () { From f177eb057c009258adb570cd7382ca34a7a35de7 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 15:26:52 -0400 Subject: [PATCH 03/16] update README --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fdbc700..3a125f8 100644 --- a/README.md +++ b/README.md @@ -95,12 +95,17 @@ rollup.rollup({ // The bundle's starting point entry: 'app.js', + // Any external modules you don't want to include + // in the bundle (includes node built-ins) + external: [ 'path', 'fs', 'some-other-lib' ], + // Override the default path resolution - resolvePath: function ( importee, importer ) { + resolvePath: function ( importee, importer, options ) { // return a string or a falsy value - if falsy, - // import is kept external to the bundle. - // Alternative, return a Promise that fulfils - // with a string or falsy value + // import is kept external to the bundle. Alternatively, + // return a Promise that fulfils with a string or falsy + // value. `options` is whatever you passed to `rollup` + // initially } }).then( function ( bundle ) { // generate code and a sourcemap From c5a783c5dd889243c148c3ea0a8d7553489edde2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 15:31:01 -0400 Subject: [PATCH 04/16] on second thoughts, remove advanced API stuff from readme --- README.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/README.md b/README.md index 3a125f8..6d9fefe 100644 --- a/README.md +++ b/README.md @@ -97,16 +97,7 @@ rollup.rollup({ // Any external modules you don't want to include // in the bundle (includes node built-ins) - external: [ 'path', 'fs', 'some-other-lib' ], - - // Override the default path resolution - resolvePath: function ( importee, importer, options ) { - // return a string or a falsy value - if falsy, - // import is kept external to the bundle. Alternatively, - // return a Promise that fulfils with a string or falsy - // value. `options` is whatever you passed to `rollup` - // initially - } + external: [ 'path', 'fs', 'some-other-lib' ] }).then( function ( bundle ) { // generate code and a sourcemap const { code, map } = bundle.generate({ From 8bad2598b78fee8c763968230a4ca25a0dd5b714 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 16:01:42 -0400 Subject: [PATCH 05/16] resolve jsnext:main in node_modules by default, allow resolveExternal to be overridden --- src/Bundle.js | 5 ++- src/rollup.js | 12 +----- src/utils/resolvePath.js | 39 +++++++++++++++++-- .../custom-external-resolver-async/_config.js | 15 +++++++ .../js_modules/external.js | 1 + .../custom-external-resolver-async/main.js | 3 ++ .../custom-external-resolver-sync/_config.js | 14 +++++++ .../js_modules/external.js | 1 + .../custom-external-resolver-sync/main.js | 3 ++ 9 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 test/function/custom-external-resolver-async/_config.js create mode 100644 test/function/custom-external-resolver-async/js_modules/external.js create mode 100644 test/function/custom-external-resolver-async/main.js create mode 100644 test/function/custom-external-resolver-sync/_config.js create mode 100644 test/function/custom-external-resolver-sync/js_modules/external.js create mode 100644 test/function/custom-external-resolver-sync/main.js diff --git a/src/Bundle.js b/src/Bundle.js index db13779..6583efc 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -7,7 +7,7 @@ import ExternalModule from './ExternalModule'; import finalisers from './finalisers/index'; import makeLegalIdentifier from './utils/makeLegalIdentifier'; import ensureArray from './utils/ensureArray'; -import { defaultResolver } from './utils/resolvePath'; +import { defaultResolver, defaultExternalResolver } from './utils/resolvePath'; import { defaultLoader } from './utils/load'; function badExports ( option, keys ) { @@ -23,7 +23,8 @@ export default class Bundle { this.load = options.load || defaultLoader; this.resolvePathOptions = { - external: ensureArray( options.external ) + external: ensureArray( options.external ), + resolveExternal: options.resolveExternal || defaultExternalResolver }; this.loadOptions = { diff --git a/src/rollup.js b/src/rollup.js index 09baf6f..adad83f 100644 --- a/src/rollup.js +++ b/src/rollup.js @@ -20,16 +20,8 @@ export function rollup ( options ) { throw new Error( 'You must supply options.dest to bundle.write' ); } - let { code, map } = bundle.generate({ - dest, - format: options.format, - globalName: options.globalName, - - // sourcemap options - sourceMap: !!options.sourceMap, - sourceMapFile: options.sourceMapFile, - // sourceMapRoot: options.sourceMapRoot - }); + const dest = options.dest; + let { code, map } = bundle.generate( options ); let promises = [ writeFile( dest, code ) ]; diff --git a/src/utils/resolvePath.js b/src/utils/resolvePath.js index 08de65d..ac8a376 100644 --- a/src/utils/resolvePath.js +++ b/src/utils/resolvePath.js @@ -1,4 +1,5 @@ import { dirname, isAbsolute, resolve } from 'path'; +import { readFileSync } from 'sander'; export function defaultResolver ( importee, importer, options ) { // absolute paths are left untouched @@ -9,14 +10,46 @@ export function defaultResolver ( importee, importer, options ) { // unless we want to keep it external, that is if ( ~options.external.indexOf( importee ) ) return null; - return resolveExternal( importee, importer, options ); + return options.resolveExternal( importee, importer, options ); } return resolve( dirname( importer ), importee ).replace( /\.js$/, '' ) + '.js'; } -function resolveExternal ( id, importer, options ) { +export function defaultExternalResolver ( id, importer, options ) { // for now, only node_modules is supported, and only jsnext:main + let dir = dirname( importer ); - throw new Error( "TODO" ); + while ( dir !== '/' ) { + const pkgPath = resolve( dir, 'node_modules', id, 'package.json' ); + let pkgJson; + + try { + pkgJson = readFileSync( pkgPath ).toString(); + } catch ( err ) { + // noop + } + + if ( pkgJson ) { + let pkg; + + try { + pkg = JSON.parse( pkgJson ); + } catch ( err ) { + throw new Error( `Malformed JSON: ${pkgPath}` ); + } + + const main = pkg[ 'jsnext:main' ]; + + if ( !main ) { + throw new Error( `Package ${id} does not have a jsnext:main field, and so cannot be included in your rollup. Try adding it as an external module instead (e.g. options.external = ['${id}']). See https://github.com/rollup/rollup/wiki/jsnext:main for more info` ); + } + + return resolve( dirname( pkgPath ), main ).replace( /\.js$/, '' ) + '.js'; + } + + dir = dirname( dir ); + } + + throw new Error( `Could not find package ${id} (required by ${importer})` ); } diff --git a/test/function/custom-external-resolver-async/_config.js b/test/function/custom-external-resolver-async/_config.js new file mode 100644 index 0000000..34faf37 --- /dev/null +++ b/test/function/custom-external-resolver-async/_config.js @@ -0,0 +1,15 @@ +var path = require( 'path' ); +var assert = require( 'assert' ); +var Promise = require( 'sander' ).Promise; + +module.exports = { + description: 'uses a custom external path resolver (asynchronous)', + options: { + resolveExternal: function ( id, importer, options ) { + return Promise.resolve( path.resolve( __dirname, 'js_modules', id + '.js' ) ); + } + }, + exports: function ( exports ) { + assert.ok( exports.success ); + } +}; diff --git a/test/function/custom-external-resolver-async/js_modules/external.js b/test/function/custom-external-resolver-async/js_modules/external.js new file mode 100644 index 0000000..c1547ab --- /dev/null +++ b/test/function/custom-external-resolver-async/js_modules/external.js @@ -0,0 +1 @@ +export default { isExternal: true }; diff --git a/test/function/custom-external-resolver-async/main.js b/test/function/custom-external-resolver-async/main.js new file mode 100644 index 0000000..32214bd --- /dev/null +++ b/test/function/custom-external-resolver-async/main.js @@ -0,0 +1,3 @@ +import external from 'external'; + +export default { success: external.isExternal }; diff --git a/test/function/custom-external-resolver-sync/_config.js b/test/function/custom-external-resolver-sync/_config.js new file mode 100644 index 0000000..2cf6de7 --- /dev/null +++ b/test/function/custom-external-resolver-sync/_config.js @@ -0,0 +1,14 @@ +var path = require( 'path' ); +var assert = require( 'assert' ); + +module.exports = { + description: 'uses a custom external path resolver (synchronous)', + options: { + resolveExternal: function ( id, importer, options ) { + return path.resolve( __dirname, 'js_modules', id + '.js' ); + } + }, + exports: function ( exports ) { + assert.ok( exports.success ); + } +}; diff --git a/test/function/custom-external-resolver-sync/js_modules/external.js b/test/function/custom-external-resolver-sync/js_modules/external.js new file mode 100644 index 0000000..c1547ab --- /dev/null +++ b/test/function/custom-external-resolver-sync/js_modules/external.js @@ -0,0 +1 @@ +export default { isExternal: true }; diff --git a/test/function/custom-external-resolver-sync/main.js b/test/function/custom-external-resolver-sync/main.js new file mode 100644 index 0000000..32214bd --- /dev/null +++ b/test/function/custom-external-resolver-sync/main.js @@ -0,0 +1,3 @@ +import external from 'external'; + +export default { success: external.isExternal }; From bd97a02dda17def9306421d4e0033927defca716 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 16:10:04 -0400 Subject: [PATCH 06/16] -> 0.6.0 --- CHANGELOG.md | 8 ++++++++ package.json | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b24f5a..0df0aae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # rollup changelog +## 0.6.0 + +* BREAKING - `rollup.rollup` and `bundle.write` both take a single options argument +* BREAKING - external modules must be declared upfront with `options.external: [...]` +* Non-relative module paths will be resolved by looking for `jsnext:main` fields in the appropriate `package.json` files. This behaviour can be overridden by passing an alternative `resolveExternal` function +* Fix sourcemap options +* Include CLI files in npm package (duh) + ## 0.5.0 * Command line interface diff --git a/package.json b/package.json index f50198b..0aef661 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.5.0", + "version": "0.6.0", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "jsnext:main": "src/rollup.js", @@ -37,7 +37,7 @@ "gobble-babel": "^5.1.0", "gobble-cli": "^0.4.2", "gobble-esperanto-bundle": "^0.2.0", - "gobble-rollup": "^0.1.1", + "gobble-rollup": "^0.2.0", "mocha": "^2.2.4", "source-map-support": "^0.2.10", "source-map": "^0.1.40" From 836f6b93b32a7c3d8cd7fa310d9e3a77dae1d7c6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 16:25:06 -0400 Subject: [PATCH 07/16] support for transformers --- src/utils/load.js | 8 +++++-- test/function/transformer-multiple/_config.js | 21 +++++++++++++++++++ test/function/transformer-multiple/foo.js | 1 + test/function/transformer-multiple/main.js | 3 +++ test/function/transformer-single/_config.js | 13 ++++++++++++ test/function/transformer-single/foo.js | 1 + test/function/transformer-single/main.js | 3 +++ 7 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 test/function/transformer-multiple/_config.js create mode 100644 test/function/transformer-multiple/foo.js create mode 100644 test/function/transformer-multiple/main.js create mode 100644 test/function/transformer-single/_config.js create mode 100644 test/function/transformer-single/foo.js create mode 100644 test/function/transformer-single/main.js diff --git a/src/utils/load.js b/src/utils/load.js index 1d8d826..9b79d9a 100644 --- a/src/utils/load.js +++ b/src/utils/load.js @@ -1,6 +1,10 @@ import { readFileSync } from 'sander'; export function defaultLoader ( path, options ) { - // TODO support plugins and transformers? - return readFileSync( path, { encoding: 'utf-8' }); + // TODO support plugins e.g. !css and !json? + const source = readFileSync( path, { encoding: 'utf-8' }); + + return options.transform.reduce( ( source, transformer ) => { + return transformer( source, path ); + }, source ); } diff --git a/test/function/transformer-multiple/_config.js b/test/function/transformer-multiple/_config.js new file mode 100644 index 0000000..34ddbf7 --- /dev/null +++ b/test/function/transformer-multiple/_config.js @@ -0,0 +1,21 @@ +var assert = require( 'assert' ); + +module.exports = { + description: 'accepts multiple transformer functions', + options: { + transform: [ + function ( code, path ) { + return code.replace( /MAGIC_NUMBER/g, 3 ); + }, + + function ( code, path ) { + return code.replace( /\d+/g, function ( match ) { + return 2 * +match; + }); + } + ] + }, + exports: function ( exports ) { + assert.equal( exports.magicNumber, 6 ); + } +} diff --git a/test/function/transformer-multiple/foo.js b/test/function/transformer-multiple/foo.js new file mode 100644 index 0000000..119481e --- /dev/null +++ b/test/function/transformer-multiple/foo.js @@ -0,0 +1 @@ +export default MAGIC_NUMBER; diff --git a/test/function/transformer-multiple/main.js b/test/function/transformer-multiple/main.js new file mode 100644 index 0000000..0fb821d --- /dev/null +++ b/test/function/transformer-multiple/main.js @@ -0,0 +1,3 @@ +import foo from './foo'; + +export var magicNumber = foo; diff --git a/test/function/transformer-single/_config.js b/test/function/transformer-single/_config.js new file mode 100644 index 0000000..4cf7a76 --- /dev/null +++ b/test/function/transformer-single/_config.js @@ -0,0 +1,13 @@ +var assert = require( 'assert' ); + +module.exports = { + description: 'accepts a single transformer function', + options: { + transform: function ( code, path ) { + return code.replace( /MAGIC_NUMBER/g, 3 ); + } + }, + exports: function ( exports ) { + assert.equal( exports.magicNumber, 3 ); + } +} diff --git a/test/function/transformer-single/foo.js b/test/function/transformer-single/foo.js new file mode 100644 index 0000000..119481e --- /dev/null +++ b/test/function/transformer-single/foo.js @@ -0,0 +1 @@ +export default MAGIC_NUMBER; diff --git a/test/function/transformer-single/main.js b/test/function/transformer-single/main.js new file mode 100644 index 0000000..0fb821d --- /dev/null +++ b/test/function/transformer-single/main.js @@ -0,0 +1,3 @@ +import foo from './foo'; + +export var magicNumber = foo; From b2cdb7a06ed219ae75f0601b92488ce975e5fa0d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 16:25:13 -0400 Subject: [PATCH 08/16] -> 0.6.1 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0df0aae..a8db502 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.6.1 + +* Support for basic transformers + ## 0.6.0 * BREAKING - `rollup.rollup` and `bundle.write` both take a single options argument diff --git a/package.json b/package.json index 0aef661..b843223 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.6.0", + "version": "0.6.1", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "jsnext:main": "src/rollup.js", From 5972bf8346fe0e749e909fb815e629ca3467af60 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 16:43:09 -0400 Subject: [PATCH 09/16] assignments to properties of bindings is permitted --- src/Statement.js | 23 +++++++++++++++---- .../legal-import-modification/_config.js | 3 +++ .../function/legal-import-modification/foo.js | 1 + .../legal-import-modification/main.js | 5 ++++ 4 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 test/function/legal-import-modification/_config.js create mode 100644 test/function/legal-import-modification/foo.js create mode 100644 test/function/legal-import-modification/main.js diff --git a/src/Statement.js b/src/Statement.js index 94d43a2..042d939 100644 --- a/src/Statement.js +++ b/src/Statement.js @@ -156,16 +156,29 @@ export default class Statement { checkForWrites ( scope, node ) { const addNode = ( node, disallowImportReassignments ) => { + let depth = 0; // determine whether we're illegally modifying a binding or namespace + while ( node.type === 'MemberExpression' ) { node = node.object; + depth += 1; } // disallow assignments/updates to imported bindings and namespaces - if ( disallowImportReassignments && has( this.module.imports, node.name ) && !scope.contains( node.name ) ) { - const err = new Error( `Illegal reassignment to import '${node.name}'` ); - err.file = this.module.path; - err.loc = getLocation( this.module.magicString.toString(), node.start ); - throw err; + if ( disallowImportReassignments ) { + const importSpecifier = this.module.imports[ node.name ]; + + if ( importSpecifier && !scope.contains( node.name ) ) { + const minDepth = importSpecifier.name === '*' ? + 2 : // cannot do e.g. `namespace.foo = bar` + 1; // cannot do e.g. `foo = bar`, but `foo.bar = bar` is fine + + if ( depth < minDepth ) { + const err = new Error( `Illegal reassignment to import '${node.name}'` ); + err.file = this.module.path; + err.loc = getLocation( this.module.magicString.toString(), node.start ); + throw err; + } + } } if ( node.type !== 'Identifier' ) { diff --git a/test/function/legal-import-modification/_config.js b/test/function/legal-import-modification/_config.js new file mode 100644 index 0000000..3792857 --- /dev/null +++ b/test/function/legal-import-modification/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'assigning to properties of imported bindings is permitted' +}; diff --git a/test/function/legal-import-modification/foo.js b/test/function/legal-import-modification/foo.js new file mode 100644 index 0000000..ff8b4c5 --- /dev/null +++ b/test/function/legal-import-modification/foo.js @@ -0,0 +1 @@ +export default {}; diff --git a/test/function/legal-import-modification/main.js b/test/function/legal-import-modification/main.js new file mode 100644 index 0000000..455d804 --- /dev/null +++ b/test/function/legal-import-modification/main.js @@ -0,0 +1,5 @@ +import foo from './foo'; + +foo.modified = true; + +export default foo; From b94faecfe982d09da9aa019fee48bd5586f03516 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 16:43:42 -0400 Subject: [PATCH 10/16] -> 0.6.2 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8db502..b384a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.6.2 + +* Permit assignments to properties of imported bindings + ## 0.6.1 * Support for basic transformers diff --git a/package.json b/package.json index b843223..20cad0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.6.1", + "version": "0.6.2", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "jsnext:main": "src/rollup.js", From d0e8890738da1bf569a5e4fc4700b6ad87107d07 Mon Sep 17 00:00:00 2001 From: Boris Letocha Date: Tue, 26 May 2015 23:33:36 +0200 Subject: [PATCH 11/16] Fixed endless cycle on Windows in defaultExternalResolver --- src/utils/resolvePath.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/utils/resolvePath.js b/src/utils/resolvePath.js index ac8a376..12fcca8 100644 --- a/src/utils/resolvePath.js +++ b/src/utils/resolvePath.js @@ -1,4 +1,4 @@ -import { dirname, isAbsolute, resolve } from 'path'; +import { dirname, isAbsolute, resolve, parse } from 'path'; import { readFileSync } from 'sander'; export function defaultResolver ( importee, importer, options ) { @@ -18,9 +18,10 @@ export function defaultResolver ( importee, importer, options ) { export function defaultExternalResolver ( id, importer, options ) { // for now, only node_modules is supported, and only jsnext:main - let dir = dirname( importer ); + let parsed = parse( importer ); + let dir = parsed.dir; - while ( dir !== '/' ) { + while ( dir !== parsed.root ) { const pkgPath = resolve( dir, 'node_modules', id, 'package.json' ); let pkgJson; From 23e0f695595c0a9a8ee44ed1a354d9e0359ef17e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 17:48:05 -0400 Subject: [PATCH 12/16] fix finalisers --- src/Bundle.js | 1 - src/finalisers/amd.js | 7 ++++-- src/finalisers/es6.js | 25 ++++++++++++++++--- src/finalisers/iife.js | 29 +++++++++++++++++++--- src/finalisers/umd.js | 23 +++++++++++++---- test/form/export-default/_config.js | 7 ++++++ test/form/export-default/_expected/amd.js | 7 ++++++ test/form/export-default/_expected/cjs.js | 5 ++++ test/form/export-default/_expected/es6.js | 3 +++ test/form/export-default/_expected/iife.js | 7 ++++++ test/form/export-default/_expected/umd.js | 11 ++++++++ test/form/export-default/main.js | 1 + test/test.js | 6 +++-- 13 files changed, 116 insertions(+), 16 deletions(-) create mode 100644 test/form/export-default/_config.js create mode 100644 test/form/export-default/_expected/amd.js create mode 100644 test/form/export-default/_expected/cjs.js create mode 100644 test/form/export-default/_expected/es6.js create mode 100644 test/form/export-default/_expected/iife.js create mode 100644 test/form/export-default/_expected/umd.js create mode 100644 test/form/export-default/main.js diff --git a/src/Bundle.js b/src/Bundle.js index 6583efc..c1e2ef1 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -97,7 +97,6 @@ export default class Bundle { this.statements = statements; this.deconflict(); }); - } deconflict () { diff --git a/src/finalisers/amd.js b/src/finalisers/amd.js index a81221e..0fae690 100644 --- a/src/finalisers/amd.js +++ b/src/finalisers/amd.js @@ -23,13 +23,16 @@ export default function amd ( bundle, magicString, exportMode, options ) { if ( exportMode === 'default' ) { exportBlock = `return ${bundle.entryModule.getCanonicalName('default')};`; } else { - exportBlock = '\n\n' + Object.keys( exports ).map( name => { + exportBlock = Object.keys( exports ).map( name => { return `exports.${name} = ${exports[name].localName};`; }).join( '\n' ); } + if ( exportBlock ) { + magicString.append( '\n\n' + exportBlock ); + } + return magicString - .append( exportBlock ) .trim() .indent() .append( '\n\n});' ) diff --git a/src/finalisers/es6.js b/src/finalisers/es6.js index f88ea40..b7a2620 100644 --- a/src/finalisers/es6.js +++ b/src/finalisers/es6.js @@ -1,7 +1,26 @@ +import { keys } from '../utils/object'; + export default function es6 ( bundle, magicString, exportMode, options ) { - // TODO - const introBlock = ''; - const exportBlock = ''; + const introBlock = ''; // TODO... + + const exports = bundle.entryModule.exports; + const exportBlock = keys( exports ).map( exportedName => { + const specifier = exports[ exportedName ]; + + const canonicalName = bundle.entryModule.getCanonicalName( specifier.localName ); + + if ( exportedName === 'default' ) { + return `export default ${canonicalName};`; + } + + return exportedName === canonicalName ? + `export { ${exportedName} };` : + `export { ${canonicalName} as ${exportedName} };`; + }).join( '\n' ); + + if ( exportBlock ) { + magicString.append( '\n\n' + exportBlock ); + } return magicString.trim(); } diff --git a/src/finalisers/iife.js b/src/finalisers/iife.js index 39aadc2..da006ae 100644 --- a/src/finalisers/iife.js +++ b/src/finalisers/iife.js @@ -1,10 +1,33 @@ +import { has } from '../utils/object'; +import { getName } from '../utils/map-helpers'; + export default function iife ( bundle, magicString, exportMode, options ) { + const globalNames = options.globals || {}; + + let dependencies = bundle.externalModules.map( module => { + return has( globalNames, module.id ) ? globalNames[ module.id ] : module.name; + }); + + let args = bundle.externalModules.map( getName ); + + if ( exportMode !== 'none' && !options.moduleName ) { + throw new Error( 'You must supply options.moduleName for IIFE bundles' ); + } + + if ( exportMode === 'named' ) { + dependencies.unshift( `(window.${options.moduleName} = {})` ); + args.unshift( 'exports' ); + } + + let intro = `(function (${args}) { 'use strict';\n\n`; + let outro = `\n\n})(${dependencies});`; - const intro = `(function () { 'use strict';\n\n`; - const outro = `\n\n})();`; + if ( exportMode === 'default' ) { + intro = `var ${options.moduleName} = ${intro}`; + magicString.append( `\n\nreturn ${bundle.entryModule.getCanonicalName('default')};` ); + } return magicString - .trim() .indent() .prepend( intro ) .append( outro ); diff --git a/src/finalisers/umd.js b/src/finalisers/umd.js index 53eba7e..54be34f 100644 --- a/src/finalisers/umd.js +++ b/src/finalisers/umd.js @@ -26,23 +26,36 @@ export default function umd ( bundle, magicString, exportMode, options ) { ( has( options, 'moduleId' ) ? `['${options.moduleId}'], ` : `` ) + ( amdDeps.length ? `[${amdDeps.join( ', ' )}], ` : `` ); + const defaultExport = exportMode === 'default' ? `global.${options.moduleName} = ` : ''; + const intro = `(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(${cjsDeps.join( ', ' )}) : typeof define === 'function' && define.amd ? define(${amdParams}factory) : - factory(${globalDeps}); + ${defaultExport}factory(${globalDeps}); }(this, function (${args}) { 'use strict'; `.replace( /^\t\t/gm, '' ).replace( /^\t/gm, indentStr ); const exports = bundle.entryModule.exports; - const exportBlock = '\n\n' + Object.keys( exports ).map( name => { - return `exports.${name} = ${exports[name].localName};`; - }).join( '\n' ); + let exportBlock; + + if ( exportMode === 'default' ) { + const canonicalName = bundle.entryModule.getCanonicalName( 'default' ); + exportBlock = `return ${canonicalName};`; + } else { + exportBlock = Object.keys( exports ).map( name => { + const canonicalName = bundle.entryModule.getCanonicalName( exports[ name ].localName ); + return `exports.${name} = ${canonicalName};`; + }).join( '\n' ); + } + + if ( exportBlock ) { + magicString.append( '\n\n' + exportBlock ); + } return magicString - .append( exportBlock ) .trim() .indent() .append( '\n\n}));' ) diff --git a/test/form/export-default/_config.js b/test/form/export-default/_config.js new file mode 100644 index 0000000..64c8831 --- /dev/null +++ b/test/form/export-default/_config.js @@ -0,0 +1,7 @@ +module.exports = { + description: 'single (default) exports', + options: { + moduleName: 'myBundle' + } + // solo: true +}; diff --git a/test/form/export-default/_expected/amd.js b/test/form/export-default/_expected/amd.js new file mode 100644 index 0000000..37d2571 --- /dev/null +++ b/test/form/export-default/_expected/amd.js @@ -0,0 +1,7 @@ +define(function () { 'use strict'; + + var main = 42; + + return main; + +}); diff --git a/test/form/export-default/_expected/cjs.js b/test/form/export-default/_expected/cjs.js new file mode 100644 index 0000000..5a370cd --- /dev/null +++ b/test/form/export-default/_expected/cjs.js @@ -0,0 +1,5 @@ +'use strict'; + +var main = 42; + +module.exports = main; diff --git a/test/form/export-default/_expected/es6.js b/test/form/export-default/_expected/es6.js new file mode 100644 index 0000000..d862de8 --- /dev/null +++ b/test/form/export-default/_expected/es6.js @@ -0,0 +1,3 @@ +var main = 42; + +export default main; diff --git a/test/form/export-default/_expected/iife.js b/test/form/export-default/_expected/iife.js new file mode 100644 index 0000000..b7b15ee --- /dev/null +++ b/test/form/export-default/_expected/iife.js @@ -0,0 +1,7 @@ +var myBundle = (function () { 'use strict'; + + var main = 42; + + return main; + +})(); diff --git a/test/form/export-default/_expected/umd.js b/test/form/export-default/_expected/umd.js new file mode 100644 index 0000000..667a50c --- /dev/null +++ b/test/form/export-default/_expected/umd.js @@ -0,0 +1,11 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + global.myBundle = factory(); +}(this, function () { 'use strict'; + + var main = 42; + + return main; + +})); diff --git a/test/form/export-default/main.js b/test/form/export-default/main.js new file mode 100644 index 0000000..7a4e8a7 --- /dev/null +++ b/test/form/export-default/main.js @@ -0,0 +1 @@ +export default 42; diff --git a/test/test.js b/test/test.js index 5f71f6e..bc41a61 100644 --- a/test/test.js +++ b/test/test.js @@ -155,9 +155,11 @@ describe( 'rollup', function () { PROFILES.forEach( function ( profile ) { ( config.skip ? it.skip : config.solo ? it.only : it )( 'generates ' + profile.format, function () { return bundlePromise.then( function ( bundle ) { - var actual = bundle.generate({ + var options = extend( {}, config.options, { format: profile.format - }).code.trim(); + }); + + var actual = bundle.generate( options ).code.trim(); try { var expected = sander.readFileSync( FORM, dir, '_expected', profile.format + '.js' ).toString().trim(); From c2ad42ba25c04abd11cd83e40e755788314a38c5 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 17:53:02 -0400 Subject: [PATCH 13/16] -> 0.6.3 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b384a31..68ba34e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # rollup changelog +## 0.6.3 + +* Fix exports and external module imports with some output formats +* Fix endless cycle bug on Windows ([#3](https://github.com/rollup/rollup/pull/3)) - thanks @Bobris + ## 0.6.2 * Permit assignments to properties of imported bindings diff --git a/package.json b/package.json index 20cad0d..ee51e17 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.6.2", + "version": "0.6.3", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "jsnext:main": "src/rollup.js", From 583552e9f2884e9ea71d83a899530aa89fb0fc07 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 17:57:17 -0400 Subject: [PATCH 14/16] fix CJS export --- src/finalisers/umd.js | 3 ++- test/form/export-default/_expected/umd.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/finalisers/umd.js b/src/finalisers/umd.js index 54be34f..09164f7 100644 --- a/src/finalisers/umd.js +++ b/src/finalisers/umd.js @@ -26,11 +26,12 @@ export default function umd ( bundle, magicString, exportMode, options ) { ( has( options, 'moduleId' ) ? `['${options.moduleId}'], ` : `` ) + ( amdDeps.length ? `[${amdDeps.join( ', ' )}], ` : `` ); + const cjsExport = exportMode === 'default' ? `module.exports = ` : ``; const defaultExport = exportMode === 'default' ? `global.${options.moduleName} = ` : ''; const intro = `(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(${cjsDeps.join( ', ' )}) : + typeof exports === 'object' && typeof module !== 'undefined' ? ${cjsExport}factory(${cjsDeps.join( ', ' )}) : typeof define === 'function' && define.amd ? define(${amdParams}factory) : ${defaultExport}factory(${globalDeps}); }(this, function (${args}) { 'use strict'; diff --git a/test/form/export-default/_expected/umd.js b/test/form/export-default/_expected/umd.js index 667a50c..b5aa08a 100644 --- a/test/form/export-default/_expected/umd.js +++ b/test/form/export-default/_expected/umd.js @@ -1,5 +1,5 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : global.myBundle = factory(); }(this, function () { 'use strict'; From c25e6781815496d340e988ab855658a67f0d84c7 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 May 2015 17:57:45 -0400 Subject: [PATCH 15/16] -> 0.6.4 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68ba34e..2f694ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.6.4 + +* Fix CJS bundling with default export + ## 0.6.3 * Fix exports and external module imports with some output formats diff --git a/package.json b/package.json index ee51e17..a29e1e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.6.3", + "version": "0.6.4", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "jsnext:main": "src/rollup.js", From 6dfa8f1e3fddc637b451e0ace45ee3d3430ccfea Mon Sep 17 00:00:00 2001 From: Brian Donovan Date: Tue, 26 May 2015 21:35:13 -0700 Subject: [PATCH 16/16] Remove unused instance variables. --- src/Bundle.js | 1 - src/Module.js | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Bundle.js b/src/Bundle.js index 151f2a4..17bb888 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -23,7 +23,6 @@ export default class Bundle { this.modulePromises = {}; this.statements = []; this.externalModules = []; - this.defaultExportName = null; this.internalNamespaceModules = []; } diff --git a/src/Module.js b/src/Module.js index aa3eb3a..a5e1e64 100644 --- a/src/Module.js +++ b/src/Module.js @@ -19,7 +19,6 @@ export default class Module { this.bundle = bundle; this.path = path; - this.relativePath = relative( bundle.base, path ).slice( 0, -3 ); // remove .js this.magicString = new MagicString( source, { filename: path