Browse Source

support default exports from bundle

contingency-plan
Rich Harris 10 years ago
parent
commit
281d5ca00a
  1. 66
      src/Bundle.js
  2. 2
      src/finalisers/amd.js
  3. 10
      src/finalisers/cjs.js
  4. 2
      src/finalisers/es6.js
  5. 2
      src/finalisers/umd.js
  6. 8
      test/samples/export-default-expression/_config.js
  7. 1
      test/samples/export-default-expression/main.js
  8. 10
      test/test.js

66
src/Bundle.js

@ -1,4 +1,4 @@
import { resolve } from 'path';
import { basename, extname, resolve } from 'path';
import { readFile } from 'sander';
import MagicString from 'magic-string';
import { keys, has } from './utils/object';
@ -10,9 +10,13 @@ import replaceIdentifiers from './utils/replaceIdentifiers';
import makeLegalIdentifier from './utils/makeLegalIdentifier';
import { defaultResolver } from './utils/resolvePath';
function badExports ( option, keys ) {
throw new Error( `'${option}' was specified for options.exports, but entry module has following exports: ${keys.join(', ')}` );
}
export default class Bundle {
constructor ( options ) {
this.base = options.base || process.cwd();
this.base = resolve( options.base || process.cwd() );
this.entryPath = resolve( this.base, options.entry ).replace( /\.js$/, '' ) + '.js';
this.resolvePath = options.resolvePath || defaultResolver;
@ -20,6 +24,7 @@ export default class Bundle {
this.modulePromises = {};
this.statements = [];
this.externalModules = [];
this.defaultExportName = null;
}
fetchModule ( importee, importer ) {
@ -72,11 +77,29 @@ export default class Bundle {
// Exclude imports
if ( /^Import/.test( node.type ) ) return;
if ( node.type === 'ExportNamedDeclaration' ) {
// Exclude default exports that proxy a name
// e.g. `export default foo`
if ( node.type === 'ExportDefaultDeclaration' && /Declaration$/.test( node.declaration.type ) ) return;
// Exclude specifier exports
if ( node.specifiers.length ) return;
// e.g. `export { foo }`
if ( node.type === 'ExportNamedDeclaration' && node.specifiers.length ) return;
// Include everything else...
if ( node.type === 'ExportDefaultDeclaration' ) {
// TODO generic 'get deconflicted name' mechanism
let defaultExportName = makeLegalIdentifier( basename( this.entryPath ).slice( 0, -extname( this.entryPath ).length ) );
while ( this.entryModule.ast._scope.contains( defaultExportName ) ) {
defaultExportName = `_${defaultExportName}`;
}
this.defaultExportName = defaultExportName;
node._source.overwrite( node.start, node.declaration.start, `var ${defaultExportName} = ` );
}
// Remove the `export` from everything else
if ( node.type === 'ExportNamedDeclaration' ) {
// Remove the `export`
node._source.remove( node.start, node.declaration.start );
}
@ -136,6 +159,9 @@ export default class Bundle {
generate ( options = {} ) {
let magicString = new MagicString.Bundle({ separator: '' });
// Determine export mode - 'default', 'named', 'none'
let exportMode = this.getExportMode( options.exports );
// Apply new names and add to the output bundle
this.statements.forEach( statement => {
let replacements = {};
@ -162,7 +188,7 @@ export default class Bundle {
throw new Error( `You must specify an output type - valid options are ${keys( finalisers ).join( ', ' )}` );
}
magicString = finalise( this, magicString, options );
magicString = finalise( this, magicString, exportMode, options );
return {
code: magicString.toString(),
@ -173,4 +199,32 @@ export default class Bundle {
})
};
}
getExportMode ( exportMode ) {
const exportKeys = keys( this.entryModule.exports );
if ( exportMode === 'default' ) {
if ( exportKeys.length !== 1 || exportKeys[0] !== 'default' ) {
badExports( 'default', exportKeys );
}
} else if ( exportMode === 'none' && exportKeys.length ) {
badExports( 'none', exportKeys );
}
if ( !exportMode || exportMode === 'auto' ) {
if ( exportKeys.length === 0 ) {
exportMode = 'none';
} else if ( exportKeys.length === 1 && exportKeys[0] === 'default' ) {
exportMode = 'default';
} else {
exportMode = 'named';
}
}
if ( !/(?:default|named|none)/.test( exportMode ) ) {
throw new Error( `options.exports must be 'default', 'named', 'none', 'auto', or left unspecified (defaults to 'auto')` );
}
return exportMode;
}
}

2
src/finalisers/amd.js

@ -1,3 +1,3 @@
export default function amd ( bundle, magicString, options ) {
export default function amd ( bundle, magicString, exportMode, options ) {
throw new Error( 'TODO' );
}

10
src/finalisers/cjs.js

@ -1,6 +1,6 @@
import { keys } from '../utils/object';
export default function cjs ( bundle, magicString ) {
export default function cjs ( bundle, magicString, exportMode ) {
let intro = `'use strict';\n\n`;
// TODO handle ambiguous default imports
@ -24,8 +24,11 @@ export default function cjs ( bundle, magicString ) {
magicString.prepend( intro );
// TODO handle default exports
const exportBlock = keys( bundle.entryModule.exports )
let exportBlock;
if ( exportMode === 'default' && bundle.entryModule.exports.default ) {
exportBlock = `module.exports = ${bundle.defaultExportName};`;
} else if ( exportMode === 'named' ) {
exportBlock = keys( bundle.entryModule.exports )
.map( key => {
const specifier = bundle.entryModule.exports[ key ];
const name = bundle.entryModule.getCanonicalName( specifier.localName );
@ -33,6 +36,7 @@ export default function cjs ( bundle, magicString ) {
return `exports.${key} = ${name};`;
})
.join( '\n' );
}
if ( exportBlock ) {
magicString.append( '\n\n' + exportBlock );

2
src/finalisers/es6.js

@ -1,3 +1,3 @@
export default function es6 ( bundle, magicString, options ) {
export default function es6 ( bundle, magicString, exportMode, options ) {
throw new Error( 'TODO' );
}

2
src/finalisers/umd.js

@ -1,4 +1,4 @@
export default function umd ( bundle, magicString, options ) {
export default function umd ( bundle, magicString, exportMode, options ) {
const indentStr = magicString.getIndentString();
const intro =

8
test/samples/export-default-expression/_config.js

@ -0,0 +1,8 @@
var assert = require( 'assert' );
module.exports = {
description: 'exports a default value as module.exports',
exports: function ( exports ) {
assert.equal( exports, 42 );
}
};

1
test/samples/export-default-expression/main.js

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

10
test/test.js

@ -30,12 +30,14 @@ describe( 'rollup', function () {
});
try {
var fn = new Function( 'require', 'exports', 'assert', result.code );
var exports = {};
fn( require, exports, assert );
var fn = new Function( 'require', 'module', 'exports', 'assert', result.code );
var module = {
exports: {}
};
fn( require, module, module.exports, assert );
if ( config.exports ) {
config.exports( exports, assert );
config.exports( module.exports, assert );
}
} catch ( err ) {
console.log( result.code );

Loading…
Cancel
Save