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