From 7dc633ce890f1b611ea2fc3a0f724222601e8892 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Mon, 18 May 2015 22:48:19 -0400 Subject: [PATCH] support custom resolvePath functions --- README.md | 8 ++- src/Bundle.js | 58 ++++++++++--------- src/rollup.js | 2 +- .../custom-path-resolver-async/_config.js | 20 +++++++ .../samples/custom-path-resolver-async/bar.js | 1 + .../custom-path-resolver-async/main.js | 6 ++ .../custom-path-resolver-sync/_config.js | 15 +++++ test/samples/custom-path-resolver-sync/bar.js | 1 + .../samples/custom-path-resolver-sync/main.js | 6 ++ test/test.js | 16 ++++- 10 files changed, 100 insertions(+), 33 deletions(-) create mode 100644 test/samples/custom-path-resolver-async/_config.js create mode 100644 test/samples/custom-path-resolver-async/bar.js create mode 100644 test/samples/custom-path-resolver-async/main.js create mode 100644 test/samples/custom-path-resolver-sync/_config.js create mode 100644 test/samples/custom-path-resolver-sync/bar.js create mode 100644 test/samples/custom-path-resolver-sync/main.js diff --git a/README.md b/README.md index 8eca59b..857e1e6 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,13 @@ The example below is aspirational. It isn't yet implemented - it exists in the n ```js rollup.rollup( 'app.js', { - /* options */ + // Override the default path resolution + resolvePath: function ( importee, importer ) { + // 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 + } }).then( function ( bundle ) { // generate code and a sourcemap const { code, map } = bundle.generate({ diff --git a/src/Bundle.js b/src/Bundle.js index 24510e7..fe15de8 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -1,5 +1,5 @@ -import { resolve } from 'path'; -import { readFile } from 'sander'; +import { dirname, resolve } from 'path'; +import { readFile, Promise } from 'sander'; import MagicString from 'magic-string'; import { keys, has } from './utils/object'; import { sequence } from './utils/promise'; @@ -12,8 +12,9 @@ import { defaultResolver } from './utils/resolvePath'; export default class Bundle { constructor ( options ) { - this.base = options.base || process.cwd(); - this.entryPath = resolve( this.base, options.entry ).replace( /\.js$/, '' ) + '.js'; + this.entryPath = resolve( options.entry ).replace( /\.js$/, '' ) + '.js'; + this.base = dirname( this.entryPath ); + this.resolvePath = options.resolvePath || defaultResolver; this.entryModule = null; @@ -23,38 +24,39 @@ export default class Bundle { } fetchModule ( importee, importer ) { - const path = this.resolvePath( importee, importer ); - - if ( !path ) { - // external module - if ( !has( this.modulePromises, importee ) ) { - const module = new ExternalModule( importee ); - this.externalModules.push( module ); - this.modulePromises[ importee ] = Promise.resolve( module ); - } + return Promise.resolve( importer === null ? importee : this.resolvePath( importee, importer ) ) + .then( path => { + if ( !path ) { + // external module + if ( !has( this.modulePromises, importee ) ) { + const module = new ExternalModule( importee ); + this.externalModules.push( module ); + this.modulePromises[ importee ] = Promise.resolve( module ); + } - return this.modulePromises[ importee ]; - } + return this.modulePromises[ importee ]; + } - if ( !has( this.modulePromises, path ) ) { - this.modulePromises[ path ] = readFile( path, { encoding: 'utf-8' }) - .then( code => { - const module = new Module({ - path, - code, - bundle: this - }); + if ( !has( this.modulePromises, path ) ) { + this.modulePromises[ path ] = readFile( path, { encoding: 'utf-8' }) + .then( code => { + const module = new Module({ + path, + code, + bundle: this + }); - return module; - }); - } + return module; + }); + } - return this.modulePromises[ path ]; + return this.modulePromises[ path ]; + }); } build () { // bring in top-level AST nodes from the entry module - return this.fetchModule( this.entryPath ) + return this.fetchModule( this.entryPath, null ) .then( entryModule => { this.entryModule = entryModule; diff --git a/src/rollup.js b/src/rollup.js index 5d29eef..899d36b 100644 --- a/src/rollup.js +++ b/src/rollup.js @@ -8,7 +8,7 @@ SOURCEMAPPING_URL += 'ppingURL'; export function rollup ( entry, options = {} ) { const bundle = new Bundle({ entry, - base: options.base || process.cwd() + resolvePath: options.resolvePath }); return bundle.build().then( () => { diff --git a/test/samples/custom-path-resolver-async/_config.js b/test/samples/custom-path-resolver-async/_config.js new file mode 100644 index 0000000..056c7c3 --- /dev/null +++ b/test/samples/custom-path-resolver-async/_config.js @@ -0,0 +1,20 @@ +module.exports = { + description: 'uses a custom path resolver (synchronous)', + options: { + resolvePath: function ( importee, importer ) { + var Promise = require( 'sander' ).Promise; + var resolved; + + if ( importee === 'foo' ) { + resolved = require( 'path' ).resolve( __dirname, 'bar.js' ); + } else { + resolved = false; + } + + return Promise.resolve( resolved ); + } + }, + exports: function ( exports, assert ) { + assert.strictEqual( exports.path, require( 'path' ) ); + } +}; \ No newline at end of file diff --git a/test/samples/custom-path-resolver-async/bar.js b/test/samples/custom-path-resolver-async/bar.js new file mode 100644 index 0000000..d9778e3 --- /dev/null +++ b/test/samples/custom-path-resolver-async/bar.js @@ -0,0 +1 @@ +export default 'bar'; \ No newline at end of file diff --git a/test/samples/custom-path-resolver-async/main.js b/test/samples/custom-path-resolver-async/main.js new file mode 100644 index 0000000..6a26b18 --- /dev/null +++ b/test/samples/custom-path-resolver-async/main.js @@ -0,0 +1,6 @@ +import foo from 'foo'; +import path from 'path'; + +assert.equal( foo, 'bar' ); + +export { path }; \ No newline at end of file diff --git a/test/samples/custom-path-resolver-sync/_config.js b/test/samples/custom-path-resolver-sync/_config.js new file mode 100644 index 0000000..fab04d0 --- /dev/null +++ b/test/samples/custom-path-resolver-sync/_config.js @@ -0,0 +1,15 @@ +module.exports = { + description: 'uses a custom path resolver (synchronous)', + options: { + resolvePath: function ( importee, importer ) { + if ( importee === 'foo' ) { + return require( 'path' ).resolve( __dirname, 'bar.js' ); + } + + return false; + } + }, + exports: function ( exports, assert ) { + assert.strictEqual( exports.path, require( 'path' ) ); + } +}; \ No newline at end of file diff --git a/test/samples/custom-path-resolver-sync/bar.js b/test/samples/custom-path-resolver-sync/bar.js new file mode 100644 index 0000000..d9778e3 --- /dev/null +++ b/test/samples/custom-path-resolver-sync/bar.js @@ -0,0 +1 @@ +export default 'bar'; \ No newline at end of file diff --git a/test/samples/custom-path-resolver-sync/main.js b/test/samples/custom-path-resolver-sync/main.js new file mode 100644 index 0000000..6a26b18 --- /dev/null +++ b/test/samples/custom-path-resolver-sync/main.js @@ -0,0 +1,6 @@ +import foo from 'foo'; +import path from 'path'; + +assert.equal( foo, 'bar' ); + +export { path }; \ No newline at end of file diff --git a/test/test.js b/test/test.js index 3cd685b..58732b2 100644 --- a/test/test.js +++ b/test/test.js @@ -8,6 +8,16 @@ var rollup = require( '../dist/rollup' ); var SAMPLES = path.resolve( __dirname, 'samples' ); +function extend ( target ) { + [].slice.call( arguments, 1 ).forEach( function ( source ) { + source && Object.keys( source ).forEach( function ( key ) { + target[ key ] = source[ key ]; + }); + }); + + return target; +} + describe( 'rollup', function () { it( 'exists', function () { assert.ok( !!rollup ); @@ -23,11 +33,11 @@ describe( 'rollup', function () { var config = require( SAMPLES + '/' + dir + '/_config' ); ( config.solo ? it.only : it )( config.description, function () { - return rollup.rollup( SAMPLES + '/' + dir + '/main.js' ) + return rollup.rollup( SAMPLES + '/' + dir + '/main.js', extend( {}, config.options ) ) .then( function ( bundle ) { - var result = bundle.generate({ + var result = bundle.generate( extend( {}, config.bundleOptions, { format: 'cjs' - }); + })); try { var fn = new Function( 'require', 'exports', 'assert', result.code );