Browse Source

Merge pull request #209 from rollup/0.20.0

0.20.0
better-aggressive
Rich Harris 9 years ago
parent
commit
4c7f8ce045
  1. 2
      .travis.yml
  2. 6
      CHANGELOG.md
  3. 2
      package.json
  4. 74
      src/Bundle.js
  5. 4
      src/rollup.js
  6. 28
      src/utils/defaults.js
  7. 5
      src/utils/load.js
  8. 2
      src/utils/path.js
  9. 79
      src/utils/resolveId.js
  10. 8
      test/function/custom-external-resolver-async/_config.js
  11. 8
      test/function/custom-external-resolver-sync/_config.js
  12. 18
      test/function/custom-loaders/_config.js
  13. 24
      test/function/custom-path-resolver-async/_config.js
  14. 30
      test/function/custom-path-resolver-on-entry/_config.js
  15. 22
      test/function/custom-path-resolver-plural-b/_config.js
  16. 24
      test/function/custom-path-resolver-plural/_config.js
  17. 12
      test/function/custom-path-resolver-sync/_config.js
  18. 9
      test/function/does-not-hang-on-missing-module/_config.js
  19. 16
      test/function/export-default-anonymous-function/_config.js
  20. 4
      test/function/import-from-external-subdirectory/_config.js
  21. 5
      test/function/import-from-external-subdirectory/main.js
  22. 14
      test/function/non-js-extensions/_config.js
  23. 3
      test/function/non-js-extensions/info.json
  24. 3
      test/function/non-js-extensions/main.js
  25. 18
      test/function/transformer-multiple/_config.js
  26. 8
      test/function/transformer-single/_config.js
  27. 22
      test/function/uses-supplied-ast/_config.js
  28. 30
      test/sourcemaps/transforms/_config.js
  29. 20
      test/test.js

2
.travis.yml

@ -7,4 +7,4 @@ env:
global:
- BUILD_TIMEOUT=10000
install: npm install
script: npm run ci
# script: npm run ci

6
CHANGELOG.md

@ -1,5 +1,11 @@
# rollup changelog
## 0.20.0
* Support for [plugins](https://github.com/rollup/rollup/wiki/Plugins) ([#207](https://github.com/rollup/rollup/pulls/207))
* BREAKING – `options.transform`, `options.load`, `options.resolveId`, `options.resolveExternal` and `options.external` are no longer supported, and should be handled by plugins. [More info](https://github.com/rollup/rollup/wiki/Plugins)
* BREAKING – the .js extension is only added if it looks like there's no extension, allowing e.g. `import data from 'data.json'` (with the appropriate transformer). For safety, always include the file extension – import `./foo.js`, not `./foo`
## 0.19.2
* Fix exporting namespaces to include all of their exports ([#204](https://github.com/rollup/rollup/issues/204))

2
package.json

@ -1,6 +1,6 @@
{
"name": "rollup",
"version": "0.19.2",
"version": "0.20.0",
"description": "Next-generation ES6 module bundler",
"main": "dist/rollup.js",
"jsnext:main": "src/rollup.js",

74
src/Bundle.js

@ -6,8 +6,7 @@ import Module from './Module';
import ExternalModule from './ExternalModule';
import finalisers from './finalisers/index';
import ensureArray from './utils/ensureArray';
import { defaultResolver, defaultExternalResolver } from './utils/resolveId';
import { defaultLoader } from './utils/load';
import { load, onwarn, resolveId } from './utils/defaults';
import getExportMode from './utils/getExportMode';
import getIndentString from './utils/getIndentString';
import { unixizePath } from './utils/normalizePlatform.js';
@ -19,16 +18,25 @@ export default class Bundle {
this.entry = options.entry;
this.entryModule = null;
this.resolveId = first( ensureArray( options.resolveId ).concat( defaultResolver ) );
this.load = first( ensureArray( options.load ).concat( defaultLoader ) );
this.plugins = ensureArray( options.plugins );
this.resolveOptions = {
external: ensureArray( options.external ),
resolveExternal: first( ensureArray( options.resolveExternal ).concat( defaultExternalResolver ) )
};
this.resolveId = first(
this.plugins
.map( plugin => plugin.resolveId )
.filter( Boolean )
.concat( resolveId )
);
this.loadOptions = {};
this.transformers = ensureArray( options.transform );
this.load = first(
this.plugins
.map( plugin => plugin.load )
.filter( Boolean )
.concat( load )
);
this.transformers = this.plugins
.map( plugin => plugin.transform )
.filter( Boolean );
this.pending = blank();
this.moduleById = blank();
@ -39,13 +47,16 @@ export default class Bundle {
this.assumedGlobals = blank();
this.external = options.external || [];
this.onwarn = options.onwarn || onwarn;
// TODO strictly speaking, this only applies with non-ES6, non-default-only bundles
[ 'module', 'exports' ].forEach( global => this.assumedGlobals[ global ] = true );
}
build () {
return Promise.resolve( this.resolveId( this.entry, undefined, this.resolveOptions ) )
.then( id => this.fetchModule( id ) )
return Promise.resolve( this.resolveId( this.entry, undefined ) )
.then( id => this.fetchModule( id, undefined ) )
.then( entryModule => {
this.entryModule = entryModule;
@ -107,12 +118,19 @@ export default class Bundle {
});
}
fetchModule ( id ) {
fetchModule ( id, importer ) {
// short-circuit cycles
if ( this.pending[ id ] ) return null;
this.pending[ id ] = true;
return Promise.resolve( this.load( id, this.loadOptions ) )
return Promise.resolve( this.load( id ) )
.catch( err => {
let msg = `Could not load ${id}`;
if ( importer ) msg += ` (imported by ${importer})`;
msg += `: ${err.message}`;
throw new Error( msg );
})
.then( source => transform( source, id, this.transformers ) )
.then( source => {
const { code, originalCode, ast, sourceMapChain } = source;
@ -128,12 +146,12 @@ export default class Bundle {
fetchAllDependencies ( module ) {
const promises = module.dependencies.map( source => {
return Promise.resolve( this.resolveId( source, module.id, this.resolveOptions ) )
return Promise.resolve( this.resolveId( source, module.id ) )
.then( resolvedId => {
module.resolvedIds[ source ] = resolvedId || source;
// external module
if ( !resolvedId ) {
if ( !~this.external.indexOf( source ) ) this.onwarn( `Treating '${source}' as external dependency` );
module.resolvedIds[ source ] = source;
if ( !this.moduleById[ source ] ) {
const module = new ExternalModule( source );
this.externalModules.push( module );
@ -141,12 +159,13 @@ export default class Bundle {
}
}
else if ( resolvedId === module.id ) {
throw new Error( `A module cannot import itself (${resolvedId})` );
}
else {
return this.fetchModule( resolvedId );
if ( resolvedId === module.id ) {
throw new Error( `A module cannot import itself (${resolvedId})` );
}
module.resolvedIds[ source ] = resolvedId;
return this.fetchModule( resolvedId, module.id );
}
});
});
@ -171,7 +190,14 @@ export default class Bundle {
}
});
if ( options.intro ) magicString.prepend( options.intro + '\n' );
const 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 );
const indentString = getIndentString( magicString, options );

4
src/rollup.js

@ -11,6 +11,10 @@ export function rollup ( options ) {
throw new Error( 'You must supply options.entry to rollup' );
}
if ( options.transform || options.load || options.resolveId || options.resolveExternal ) {
throw new Error( 'The `transform`, `load`, `resolveId` and `resolveExternal` options are deprecated in favour of a unified plugin API. See https://github.com/rollup/rollup/wiki/Plugins for details' );
}
const bundle = new Bundle( options );
return bundle.build().then( () => {

28
src/utils/defaults.js

@ -0,0 +1,28 @@
import { readFileSync } from './fs';
import { dirname, extname, isAbsolute, resolve } from './path';
export function load ( id ) {
return readFileSync( id, 'utf-8' );
}
function addExt ( id ) {
if ( !extname( id ) ) id += '.js';
return id;
}
export function resolveId ( importee, importer ) {
// absolute paths are left untouched
if ( isAbsolute( importee ) ) return addExt( importee );
// if this is the entry point, resolve against cwd
if ( importer === undefined ) return resolve( process.cwd(), addExt( importee ) );
// external modules are skipped at this stage
if ( importee[0] !== '.' ) return null;
return resolve( dirname( importer ), addExt( importee ) );
}
export function onwarn ( msg ) {
console.error( msg );
}

5
src/utils/load.js

@ -1,5 +0,0 @@
import { readFileSync } from './fs';
export function defaultLoader ( id ) {
return readFileSync( id, 'utf-8' );
}

2
src/utils/path.js

@ -21,7 +21,7 @@ export function dirname ( path ) {
}
export function extname ( path ) {
const match = /\.[^\.]+$/.exec( path );
const match = /\.[^\.]+$/.exec( basename( path ) );
if ( !match ) return '';
return match[0];
}

79
src/utils/resolveId.js

@ -1,79 +0,0 @@
import { absolutePath, dirname, isAbsolute, resolve } from './path';
import { readdirSync, readFileSync } from './fs';
function dirExists ( dir ) {
try {
readdirSync( dir );
return true;
} catch ( err ) {
return false;
}
}
export function defaultResolver ( importee, importer, options ) {
// absolute paths are left untouched
if ( isAbsolute( importee ) ) return importee;
// if this is the entry point, resolve against cwd
if ( importer === undefined ) return resolve( process.cwd(), importee );
// we try to resolve external modules
if ( importee[0] !== '.' ) {
const [ id ] = importee.split( /[\/\\]/ );
// unless we want to keep it external, that is
if ( ~options.external.indexOf( id ) ) return null;
return options.resolveExternal( importee, importer, options );
}
return resolve( dirname( importer ), importee ).replace( /\.js$/, '' ) + '.js';
}
export function defaultExternalResolver ( id, importer ) {
// for now, only node_modules is supported, and only jsnext:main
const root = absolutePath.exec( importer )[0];
let dir = dirname( importer );
// `foo` should use jsnext:main, but `foo/src/bar` shouldn't
const parts = id.split( /[\/\\]/ );
// npm scoped packages – @user/package
if ( parts[0][0] === '@' && parts[1] ) {
var user = parts.shift();
parts[0] = user + '/' + parts[0];
}
while ( dir !== root && dir !== '.' ) {
const modulePath = resolve( dir, 'node_modules', parts[0] );
if ( dirExists( modulePath ) ) {
// `foo/src/bar`
if ( parts.length > 1 ) {
return resolve( modulePath, ...parts.slice( 1 ) ).replace( /\.js$/, '' ) + '.js';
}
// `foo`
const pkgPath = resolve( modulePath, 'package.json' );
let pkg;
try {
pkg = JSON.parse( readFileSync( pkgPath, 'utf-8' ) );
} catch ( err ) {
throw new Error( `Missing or malformed package.json: ${modulePath}` );
}
const main = pkg[ 'jsnext:main' ];
if ( !main ) {
throw new Error( `Package ${id} (imported by ${importer}) 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})` );
}

8
test/function/custom-external-resolver-async/_config.js

@ -5,9 +5,11 @@ 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' ) );
}
plugins: [{
resolveId: function ( id, importer ) {
if ( importer && id[0] !== '.' ) return Promise.resolve( path.resolve( __dirname, 'js_modules', id + '.js' ) );
}
}]
},
exports: function ( exports ) {
assert.ok( exports.success );

8
test/function/custom-external-resolver-sync/_config.js

@ -4,9 +4,11 @@ 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' );
}
plugins: [{
resolveId: function ( id, importer ) {
if ( importer && id[0] !== '.' ) return path.resolve( __dirname, 'js_modules', id + '.js' );
}
}]
},
exports: function ( exports ) {
assert.ok( exports.success );

18
test/function/custom-loaders/_config.js

@ -3,15 +3,19 @@ var fs = require( 'fs' );
module.exports = {
description: 'uses custom loaders, falling back to default',
options: {
load: [
function ( id ) {
if ( /foo\.js/.test( id ) ) {
return fs.readFileSync( id, 'utf-8' ).replace( '@', 1 );
plugins: [
{
load: function ( id ) {
if ( /foo\.js/.test( id ) ) {
return fs.readFileSync( id, 'utf-8' ).replace( '@', 1 );
}
}
},
function ( id ) {
if ( /bar\.js/.test( id ) ) {
return fs.readFileSync( id, 'utf-8' ).replace( '@', 2 );
{
load: function ( id ) {
if ( /bar\.js/.test( id ) ) {
return fs.readFileSync( id, 'utf-8' ).replace( '@', 2 );
}
}
}
]

24
test/function/custom-path-resolver-async/_config.js

@ -4,20 +4,22 @@ var assert = require( 'assert' );
module.exports = {
description: 'uses a custom path resolver (asynchronous)',
options: {
resolveId: function ( importee, importer ) {
var Promise = require( 'sander' ).Promise;
var resolved;
plugins: [{
resolveId: function ( importee, importer ) {
var Promise = require( 'sander' ).Promise;
var resolved;
if ( path.normalize(importee) === path.resolve( __dirname, 'main.js' ) ) return importee;
if ( path.normalize(importee) === path.resolve( __dirname, 'main.js' ) ) return importee;
if ( importee === 'foo' ) {
resolved = path.resolve( __dirname, 'bar.js' );
} else {
resolved = false;
}
if ( importee === 'foo' ) {
resolved = path.resolve( __dirname, 'bar.js' );
} else {
resolved = false;
}
return Promise.resolve( resolved );
}
return Promise.resolve( resolved );
}
}]
},
exports: function ( exports ) {
assert.strictEqual( exports.path, require( 'path' ) );

30
test/function/custom-path-resolver-on-entry/_config.js

@ -10,22 +10,24 @@ module.exports = {
description: 'applies custom resolver to entry point',
//solo: true,
options: {
resolveId: function ( importee, importer ) {
if ( importer === undefined ) {
return '@' + path.relative( __dirname, importee );
}
plugins: [{
resolveId: function ( importee, importer ) {
if ( importer === undefined ) {
return '@' + path.relative( __dirname, importee );
}
if ( importer[0] === '@' ) {
return path.resolve( __dirname, importee ) + '.js';
}
},
load: function ( moduleId ) {
if ( moduleId[0] === '@' ) {
return cachedModules[ moduleId ];
}
if ( importer[0] === '@' ) {
return path.resolve( __dirname, importee ) + '.js';
}
},
load: function ( moduleId ) {
if ( moduleId[0] === '@' ) {
return cachedModules[ moduleId ];
}
return fs.readFileSync( moduleId, 'utf-8' );
}
return fs.readFileSync( moduleId, 'utf-8' );
}
}]
},
exports: function ( exports ) {
assert.equal( exports, 42 );

22
test/function/custom-path-resolver-plural-b/_config.js

@ -3,17 +3,21 @@ var assert = require( 'assert' );
module.exports = {
description: 'resolver error is not caught',
options: {
resolveId: [
function () {
throw new Error( 'nope' );
plugins: [
{
resolveId: function () {
throw new Error( 'nope' );
},
load: function ( id ) {
if ( id === 'main' ) return 'assert.ok( false );'
}
},
function ( importee, importer ) {
return 'main';
{
resolveId: function ( importee, importer ) {
return 'main';
}
}
],
load: function ( id ) {
if ( id === 'main' ) return 'assert.ok( false );'
}
]
},
error: function ( err ) {
assert.equal( err.message, 'nope' );

24
test/function/custom-path-resolver-plural/_config.js

@ -4,18 +4,22 @@ var assert = require( 'assert' );
module.exports = {
description: 'uses custom path resolvers (plural)',
options: {
resolveId: [
function ( importee ) {
if ( importee[0] === '@' )
return path.resolve( __dirname, 'globals-' + importee.slice( 1 ).toLowerCase() + '.js' );
plugins: [
{
resolveId: function ( importee ) {
if ( importee[0] === '@' )
return path.resolve( __dirname, 'globals-' + importee.slice( 1 ).toLowerCase() + '.js' );
},
load: function ( id ) {
if ( id === '<empty>' ) return '';
}
},
function ( importee ) {
if ( importee[0] === '!' ) return '<empty>';
{
resolveId: function ( importee ) {
if ( importee[0] === '!' ) return '<empty>';
}
}
],
load: function ( id ) {
if ( id === '<empty>' ) return '';
}
]
},
exports: function ( exports ) {
assert.strictEqual( exports.res, 0 );

12
test/function/custom-path-resolver-sync/_config.js

@ -4,12 +4,14 @@ var assert = require( 'assert' );
module.exports = {
description: 'uses a custom path resolver (synchronous)',
options: {
resolveId: function ( importee, importer ) {
if ( path.normalize(importee) === path.resolve( __dirname, 'main.js' ) ) return importee;
if ( importee === 'foo' ) return path.resolve( __dirname, 'bar.js' );
plugins: [{
resolveId: function ( importee, importer ) {
if ( path.normalize(importee) === path.resolve( __dirname, 'main.js' ) ) return importee;
if ( importee === 'foo' ) return path.resolve( __dirname, 'bar.js' );
return false;
}
return false;
}
}]
},
exports: function ( exports ) {
assert.strictEqual( exports.path, require( 'path' ) );

9
test/function/does-not-hang-on-missing-module/_config.js

@ -2,7 +2,12 @@ var assert = require( 'assert' );
module.exports = {
description: 'does not hang on missing module (#53)',
error: function ( error ) {
assert.ok( /Could not find package unlessYouCreatedThisFileForSomeReason/.test( error.message ) );
options: {
onwarn: function ( msg ) {
assert.equal( "Treating 'unlessYouCreatedThisFileForSomeReason' as external dependency", msg );
}
},
runtimeError: function ( error ) {
assert.equal( "Cannot find module 'unlessYouCreatedThisFileForSomeReason'", error.message );
}
};

16
test/function/export-default-anonymous-function/_config.js

@ -4,12 +4,14 @@ var path = require( 'path' );
module.exports = {
description: 'exports an anonymous function with custom ID resolver', // yeah, this is a real edge case
options: {
resolveId: function ( importee, importer ) {
return path.basename( importee ).replace( /\..+/, '' );
},
load: function ( id ) {
console.log( 'id', id )
return fs.readFileSync( path.join( __dirname, id + '.js' ), 'utf-8' );
}
plugins: [{
resolveId: function ( importee, importer ) {
return path.basename( importee ).replace( /\..+/, '' );
},
load: function ( id ) {
console.log( 'id', id )
return fs.readFileSync( path.join( __dirname, id + '.js' ), 'utf-8' );
}
}]
}
};

4
test/function/import-from-external-subdirectory/_config.js

@ -1,4 +0,0 @@
module.exports = {
description: 'default resolver imports from a subdirectory of an external module',
babel: true
};

5
test/function/import-from-external-subdirectory/main.js

@ -1,5 +0,0 @@
// this test is brittle, it relies on this dependency continuing
// to be structured in a certain way
import btoa from 'magic-string/src/utils/btoa';
assert.equal( btoa( 'it works' ), new Buffer( 'it works' ).toString( 'base64' ) );

14
test/function/non-js-extensions/_config.js

@ -0,0 +1,14 @@
var path = require( 'path' );
module.exports = {
description: 'non .js extensions are preserved',
options: {
plugins: [{
transform: function ( code, id ) {
if ( path.extname( id ) === '.json' ) {
return 'export default ' + code;
}
}
}]
}
};

3
test/function/non-js-extensions/info.json

@ -0,0 +1,3 @@
{
"answer": 42
}

3
test/function/non-js-extensions/main.js

@ -0,0 +1,3 @@
import info from './info.json';
assert.equal( info.answer, 42 );

18
test/function/transformer-multiple/_config.js

@ -3,15 +3,19 @@ var assert = require( 'assert' );
module.exports = {
description: 'accepts multiple transformer functions',
options: {
transform: [
function ( code, path ) {
return code.replace( /MAGIC_NUMBER/g, 3 );
plugins: [
{
transform: function ( code, path ) {
return code.replace( /MAGIC_NUMBER/g, 3 );
}
},
function ( code, path ) {
return code.replace( /\d+/g, function ( match ) {
return 2 * +match;
});
{
transform: function ( code, path ) {
return code.replace( /\d+/g, function ( match ) {
return 2 * +match;
});
}
}
]
},

8
test/function/transformer-single/_config.js

@ -3,9 +3,11 @@ var assert = require( 'assert' );
module.exports = {
description: 'accepts a single transformer function',
options: {
transform: function ( code, path ) {
return code.replace( /MAGIC_NUMBER/g, 3 );
}
plugins: [{
transform: function ( code, path ) {
return code.replace( /MAGIC_NUMBER/g, 3 );
}
}]
},
exports: function ( exports ) {
assert.equal( exports.magicNumber, 3 );

22
test/function/uses-supplied-ast/_config.js

@ -19,16 +19,18 @@ var modules = {
module.exports = {
description: 'uses supplied AST',
options: {
resolveId: function ( importee, importer ) {
if ( !importer ) return 'main';
return importee;
},
load: function ( id ) {
if ( id === 'bar' ) {
throw new Error( 'loaded incorrect module' );
}
plugins: [{
resolveId: function ( importee, importer ) {
if ( !importer ) return 'main';
return importee;
},
load: function ( id ) {
if ( id === 'bar' ) {
throw new Error( 'loaded incorrect module' );
}
return modules[ id ];
}
return modules[ id ];
}
}]
}
};

30
test/sourcemaps/transforms/_config.js

@ -7,22 +7,26 @@ var SourceMapConsumer = require( 'source-map' ).SourceMapConsumer;
module.exports = {
description: 'preserves sourcemap chains when transforming',
options: {
transform: [
function ( source, id ) {
return babel.transform( source, {
blacklist: [ 'es6.modules' ],
sourceMap: true
});
plugins: [
{
transform: function ( source, id ) {
return babel.transform( source, {
blacklist: [ 'es6.modules' ],
sourceMap: true
});
}
},
function ( source, id ) {
var s = new MagicString( source );
s.append( '\nassert.equal( 1 + 1, 2 );\nassert.equal( 2 + 2, 4 );' );
{
transform: function ( source, id ) {
var s = new MagicString( source );
s.append( '\nassert.equal( 1 + 1, 2 );\nassert.equal( 2 + 2, 4 );' );
return {
code: s.toString(),
map: s.generateMap({ hires: true })
};
return {
code: s.toString(),
map: s.generateMap({ hires: true })
};
}
}
]
},

20
test/test.js

@ -61,10 +61,12 @@ describe( 'rollup', function () {
it( 'fails without options or options.dest', function () {
return rollup.rollup({
entry: 'x',
resolveId: function () { return 'test'; },
load: function () {
return '// empty';
}
plugins: [{
resolveId: function () { return 'test'; },
load: function () {
return '// empty';
}
}]
}).then( function ( bundle ) {
assert.throws( function () {
bundle.write();
@ -79,10 +81,12 @@ describe( 'rollup', function () {
it( 'expects options.moduleName for IIFE and UMD bundles', function () {
return rollup.rollup({
entry: 'x',
resolveId: function () { return 'test'; },
load: function () {
return 'export var foo = 42;';
}
plugins: [{
resolveId: function () { return 'test'; },
load: function () {
return 'export var foo = 42;';
}
}]
}).then( function ( bundle ) {
assert.throws( function () {
bundle.generate({

Loading…
Cancel
Save