From 688d66698aaa4bb6cbe9e975ceab5266a02d9ce7 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 26 Jul 2015 13:54:57 -0400 Subject: [PATCH 01/15] more robust interop --- src/finalisers/shared/getInteropBlock.js | 10 ++++++++-- .../external-imports-custom-names/_expected/amd.js | 2 ++ .../external-imports-custom-names/_expected/iife.js | 2 ++ .../external-imports-custom-names/_expected/umd.js | 2 ++ test/form/external-imports/_expected/amd.js | 1 + test/form/external-imports/_expected/iife.js | 1 + test/form/external-imports/_expected/umd.js | 1 + 7 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/finalisers/shared/getInteropBlock.js b/src/finalisers/shared/getInteropBlock.js index 2fadc3a..8060e00 100644 --- a/src/finalisers/shared/getInteropBlock.js +++ b/src/finalisers/shared/getInteropBlock.js @@ -1,6 +1,12 @@ export default function getInteropBlock ( bundle ) { return bundle.externalModules - .filter( module => module.needsDefault && module.needsNamed ) - .map( module => `var ${module.name}__default = 'default' in ${module.name} ? ${module.name}['default'] : ${module.name};` ) + .map( module => { + return module.needsDefault ? + ( module.needsNamed ? + `var ${module.name}__default = 'default' in ${module.name} ? ${module.name}['default'] : ${module.name};` : + `${module.name} = 'default' in ${module.name} ? ${module.name}['default'] : ${module.name};` ) : + null; + }) + .filter( Boolean ) .join( '\n' ); } diff --git a/test/form/external-imports-custom-names/_expected/amd.js b/test/form/external-imports-custom-names/_expected/amd.js index 9cb5e1a..936a550 100644 --- a/test/form/external-imports-custom-names/_expected/amd.js +++ b/test/form/external-imports-custom-names/_expected/amd.js @@ -1,5 +1,7 @@ define(['jquery'], function ($) { 'use strict'; + $ = 'default' in $ ? $['default'] : $; + $( function () { $( 'body' ).html( '

hello world!

' ); }); diff --git a/test/form/external-imports-custom-names/_expected/iife.js b/test/form/external-imports-custom-names/_expected/iife.js index 1ff0551..4b34e9b 100644 --- a/test/form/external-imports-custom-names/_expected/iife.js +++ b/test/form/external-imports-custom-names/_expected/iife.js @@ -1,5 +1,7 @@ (function ($) { 'use strict'; + $ = 'default' in $ ? $['default'] : $; + $( function () { $( 'body' ).html( '

hello world!

' ); }); diff --git a/test/form/external-imports-custom-names/_expected/umd.js b/test/form/external-imports-custom-names/_expected/umd.js index e8013aa..8912390 100644 --- a/test/form/external-imports-custom-names/_expected/umd.js +++ b/test/form/external-imports-custom-names/_expected/umd.js @@ -4,6 +4,8 @@ factory(global.jQuery); }(this, function ($) { 'use strict'; + $ = 'default' in $ ? $['default'] : $; + $( function () { $( 'body' ).html( '

hello world!

' ); }); diff --git a/test/form/external-imports/_expected/amd.js b/test/form/external-imports/_expected/amd.js index 8e696cb..a259937 100644 --- a/test/form/external-imports/_expected/amd.js +++ b/test/form/external-imports/_expected/amd.js @@ -1,5 +1,6 @@ define(['factory', 'baz', 'shipping-port', 'alphabet'], function (factory, baz, containers, alphabet) { 'use strict'; + factory = 'default' in factory ? factory['default'] : factory; var alphabet__default = 'default' in alphabet ? alphabet['default'] : alphabet; factory( null ); diff --git a/test/form/external-imports/_expected/iife.js b/test/form/external-imports/_expected/iife.js index 897ee39..e3857fb 100644 --- a/test/form/external-imports/_expected/iife.js +++ b/test/form/external-imports/_expected/iife.js @@ -1,5 +1,6 @@ (function (factory,baz,containers,alphabet) { 'use strict'; + factory = 'default' in factory ? factory['default'] : factory; var alphabet__default = 'default' in alphabet ? alphabet['default'] : alphabet; factory( null ); diff --git a/test/form/external-imports/_expected/umd.js b/test/form/external-imports/_expected/umd.js index f6064c8..196c394 100644 --- a/test/form/external-imports/_expected/umd.js +++ b/test/form/external-imports/_expected/umd.js @@ -4,6 +4,7 @@ factory(global.factory,global.baz,global.containers,global.alphabet); }(this, function (factory,baz,containers,alphabet) { 'use strict'; + factory = 'default' in factory ? factory['default'] : factory; var alphabet__default = 'default' in alphabet ? alphabet['default'] : alphabet; factory( null ); From 62241d523dc1b60f2320563b864b58100dace721 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 26 Jul 2015 13:55:12 -0400 Subject: [PATCH 02/15] include imports/exports in bundle object --- src/rollup.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rollup.js b/src/rollup.js index 0b8a652..89f5a07 100644 --- a/src/rollup.js +++ b/src/rollup.js @@ -1,5 +1,6 @@ import { basename } from './utils/path'; import { writeFile } from 'sander'; +import { keys } from './utils/object'; import Bundle from './Bundle'; let SOURCEMAPPING_URL = 'sourceMa'; @@ -14,6 +15,9 @@ export function rollup ( options ) { return bundle.build().then( () => { return { + imports: bundle.externalModules.map( module => module.id ), + exports: keys( bundle.entryModule.exports ), + generate: options => bundle.generate( options ), write: options => { if ( !options || !options.dest ) { From 7030e5478572fbc7c53d17455b72ee12d8fb336f Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 26 Jul 2015 14:56:08 -0400 Subject: [PATCH 03/15] refactor and robustify export blocks --- src/finalisers/amd.js | 14 ++------------ src/finalisers/cjs.js | 21 ++++----------------- src/finalisers/iife.js | 13 +++++++------ src/finalisers/shared/getExportBlock.js | 12 ++++++++++++ src/finalisers/umd.js | 20 +++----------------- 5 files changed, 28 insertions(+), 52 deletions(-) create mode 100644 src/finalisers/shared/getExportBlock.js diff --git a/src/finalisers/amd.js b/src/finalisers/amd.js index 02a947b..ab8d2db 100644 --- a/src/finalisers/amd.js +++ b/src/finalisers/amd.js @@ -1,5 +1,6 @@ import { getName, quoteId } from '../utils/map-helpers'; import getInteropBlock from './shared/getInteropBlock'; +import getExportBlock from './shared/getExportBlock'; export default function amd ( bundle, magicString, { exportMode, indentString }, options ) { let deps = bundle.externalModules.map( quoteId ); @@ -20,18 +21,7 @@ export default function amd ( bundle, magicString, { exportMode, indentString }, const interopBlock = getInteropBlock( bundle ); if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' ); - const exports = bundle.entryModule.exports; - - let exportBlock; - - if ( exportMode === 'default' ) { - exportBlock = `return ${bundle.entryModule.getCanonicalName('default')};`; - } else { - exportBlock = bundle.toExport.map( name => { - return `exports.${name} = ${exports[name].localName};`; - }).join( '\n' ); - } - + const exportBlock = getExportBlock( bundle, exportMode ); if ( exportBlock ) magicString.append( '\n\n' + exportBlock ); return magicString diff --git a/src/finalisers/cjs.js b/src/finalisers/cjs.js index 1f2f92a..4ae39b1 100644 --- a/src/finalisers/cjs.js +++ b/src/finalisers/cjs.js @@ -1,3 +1,5 @@ +import getExportBlock from './shared/getExportBlock'; + export default function cjs ( bundle, magicString, { exportMode }) { let intro = `'use strict';\n\n`; @@ -21,23 +23,8 @@ export default function cjs ( bundle, magicString, { exportMode }) { magicString.prepend( intro ); - let exportBlock; - if ( exportMode === 'default' && bundle.entryModule.exports.default ) { - exportBlock = `module.exports = ${bundle.entryModule.getCanonicalName('default')};`; - } else if ( exportMode === 'named' ) { - exportBlock = bundle.toExport - .map( key => { - const specifier = bundle.entryModule.exports[ key ]; - const name = bundle.entryModule.getCanonicalName( specifier.localName ); - - return `exports.${key} = ${name};`; - }) - .join( '\n' ); - } - - if ( exportBlock ) { - magicString.append( '\n\n' + exportBlock ); - } + const exportBlock = getExportBlock( bundle, exportMode, 'module.exports =' ); + if ( exportBlock ) magicString.append( '\n\n' + exportBlock ); return magicString; } diff --git a/src/finalisers/iife.js b/src/finalisers/iife.js index dd6b6d0..2d8c88d 100644 --- a/src/finalisers/iife.js +++ b/src/finalisers/iife.js @@ -1,6 +1,7 @@ import { blank } from '../utils/object'; import { getName } from '../utils/map-helpers'; import getInteropBlock from './shared/getInteropBlock'; +import getExportBlock from './shared/getExportBlock'; export default function iife ( bundle, magicString, { exportMode, indentString }, options ) { const globalNames = options.globals || blank(); @@ -23,16 +24,16 @@ export default function iife ( bundle, magicString, { exportMode, indentString } let intro = `(function (${args}) { 'use strict';\n\n`; let outro = `\n\n})(${dependencies});`; - // var foo__default = 'default' in foo ? foo['default'] : foo; - const interopBlock = getInteropBlock( bundle ); - if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' ); - if ( exportMode === 'default' ) { intro = `var ${options.moduleName} = ${intro}`; - magicString.append( `\n\nreturn ${bundle.entryModule.getCanonicalName('default')};` ); } - // TODO named exports + // var foo__default = 'default' in foo ? foo['default'] : foo; + const interopBlock = getInteropBlock( bundle ); + if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' ); + + const exportBlock = getExportBlock( bundle, exportMode ); + if ( exportBlock ) magicString.append( '\n\n' + exportBlock ); return magicString .indent( indentString ) diff --git a/src/finalisers/shared/getExportBlock.js b/src/finalisers/shared/getExportBlock.js new file mode 100644 index 0000000..c02c727 --- /dev/null +++ b/src/finalisers/shared/getExportBlock.js @@ -0,0 +1,12 @@ +export default function getExportBlock ( bundle, exportMode, mechanism = 'return' ) { + if ( exportMode === 'default' ) { + return `${mechanism} ${bundle.entryModule.getCanonicalName('default')};`; + } + + return bundle.toExport + .map( name => { + const prop = name === 'default' ? `['default']` : `.${name}`; + return `exports${prop} = ${bundle.entryModule.getCanonicalName(name)};`; + }) + .join( '\n' ); +} diff --git a/src/finalisers/umd.js b/src/finalisers/umd.js index 8dc20ff..7415d8f 100644 --- a/src/finalisers/umd.js +++ b/src/finalisers/umd.js @@ -1,6 +1,7 @@ import { blank } from '../utils/object'; import { getName, quoteId, req } from '../utils/map-helpers'; import getInteropBlock from './shared/getInteropBlock'; +import getExportBlock from './shared/getExportBlock'; export default function umd ( bundle, magicString, { exportMode, indentString }, options ) { if ( exportMode !== 'none' && !options.moduleName ) { @@ -45,23 +46,8 @@ export default function umd ( bundle, magicString, { exportMode, indentString }, const interopBlock = getInteropBlock( bundle ); if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' ); - const exports = bundle.entryModule.exports; - - let exportBlock; - - if ( exportMode === 'default' ) { - const canonicalName = bundle.entryModule.getCanonicalName( 'default' ); - exportBlock = `return ${canonicalName};`; - } else { - exportBlock = bundle.toExport.map( name => { - const canonicalName = bundle.entryModule.getCanonicalName( exports[ name ].localName ); - return `exports.${name} = ${canonicalName};`; - }).join( '\n' ); - } - - if ( exportBlock ) { - magicString.append( '\n\n' + exportBlock ); - } + const exportBlock = getExportBlock( bundle, exportMode ); + if ( exportBlock ) magicString.append( '\n\n' + exportBlock ); return magicString .trim() From 32659d12e0b44e9f165984d9c86a85b60421dbe3 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 26 Jul 2015 18:15:14 -0400 Subject: [PATCH 04/15] prevent module importing itself --- src/Bundle.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Bundle.js b/src/Bundle.js index 4ed55a0..5541e49 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -194,6 +194,10 @@ export default class Bundle { return this.modulePromises[ importee ]; } + if ( id === importer ) { + throw new Error( `A module cannot import itself (${id})` ); + } + if ( !this.modulePromises[ id ] ) { this.modulePromises[ id ] = Promise.resolve( this.load( id, this.loadOptions ) ) .then( source => { From 3260134d1ff100c8ecf5e9e86afd449b851cba89 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 26 Jul 2015 18:33:42 -0400 Subject: [PATCH 05/15] fix amd module IDs --- src/finalisers/amd.js | 2 +- src/finalisers/umd.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/finalisers/amd.js b/src/finalisers/amd.js index ab8d2db..42115ce 100644 --- a/src/finalisers/amd.js +++ b/src/finalisers/amd.js @@ -12,7 +12,7 @@ export default function amd ( bundle, magicString, { exportMode, indentString }, } const params = - ( options.moduleId ? `['${options.moduleId}'], ` : `` ) + + ( options.moduleId ? `'${options.moduleId}', ` : `` ) + ( deps.length ? `[${deps.join( ', ' )}], ` : `` ); const intro = `define(${params}function (${args.join( ', ' )}) { 'use strict';\n\n`; diff --git a/src/finalisers/umd.js b/src/finalisers/umd.js index 7415d8f..cf62777 100644 --- a/src/finalisers/umd.js +++ b/src/finalisers/umd.js @@ -27,7 +27,7 @@ export default function umd ( bundle, magicString, { exportMode, indentString }, } const amdParams = - ( options.moduleId ? `['${options.moduleId}'], ` : `` ) + + ( options.moduleId ? `'${options.moduleId}', ` : `` ) + ( amdDeps.length ? `[${amdDeps.join( ', ' )}], ` : `` ); const cjsExport = exportMode === 'default' ? `module.exports = ` : ``; From 1fea163ccc48bcb76ae4e124df3e0d7603319706 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Mon, 27 Jul 2015 08:14:47 -0400 Subject: [PATCH 06/15] make use strict optional --- src/finalisers/amd.js | 3 ++- src/finalisers/cjs.js | 4 ++-- src/finalisers/iife.js | 3 ++- src/finalisers/umd.js | 4 +++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/finalisers/amd.js b/src/finalisers/amd.js index 42115ce..831de3a 100644 --- a/src/finalisers/amd.js +++ b/src/finalisers/amd.js @@ -15,7 +15,8 @@ export default function amd ( bundle, magicString, { exportMode, indentString }, ( options.moduleId ? `'${options.moduleId}', ` : `` ) + ( deps.length ? `[${deps.join( ', ' )}], ` : `` ); - const intro = `define(${params}function (${args.join( ', ' )}) { 'use strict';\n\n`; + const useStrict = options.useStrict !== false ? ` 'use strict';` : ``; + const intro = `define(${params}function (${args.join( ', ' )}) {${useStrict}\n\n`; // var foo__default = 'default' in foo ? foo['default'] : foo; const interopBlock = getInteropBlock( bundle ); diff --git a/src/finalisers/cjs.js b/src/finalisers/cjs.js index 4ae39b1..034273d 100644 --- a/src/finalisers/cjs.js +++ b/src/finalisers/cjs.js @@ -1,7 +1,7 @@ import getExportBlock from './shared/getExportBlock'; -export default function cjs ( bundle, magicString, { exportMode }) { - let intro = `'use strict';\n\n`; +export default function cjs ( bundle, magicString, { exportMode }, options ) { + let intro = options.useStrict === false ? `` : `'use strict';\n\n`; // TODO handle empty imports, once they're supported const importBlock = bundle.externalModules diff --git a/src/finalisers/iife.js b/src/finalisers/iife.js index 2d8c88d..fef3978 100644 --- a/src/finalisers/iife.js +++ b/src/finalisers/iife.js @@ -21,7 +21,8 @@ export default function iife ( bundle, magicString, { exportMode, indentString } args.unshift( 'exports' ); } - let intro = `(function (${args}) { 'use strict';\n\n`; + const useStrict = options.useStrict !== false ? ` 'use strict';` : ``; + let intro = `(function (${args}) {${useStrict}\n\n`; let outro = `\n\n})(${dependencies});`; if ( exportMode === 'default' ) { diff --git a/src/finalisers/umd.js b/src/finalisers/umd.js index cf62777..d2f01db 100644 --- a/src/finalisers/umd.js +++ b/src/finalisers/umd.js @@ -33,12 +33,14 @@ export default function umd ( bundle, magicString, { exportMode, indentString }, const cjsExport = exportMode === 'default' ? `module.exports = ` : ``; const defaultExport = exportMode === 'default' ? `global.${options.moduleName} = ` : ''; + const useStrict = options.useStrict !== false ? ` 'use strict';` : ``; + const intro = `(function (global, factory) { 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'; + }(this, function (${args}) {${useStrict} `.replace( /^\t\t/gm, '' ).replace( /^\t/gm, magicString.getIndentString() ); From 41cd743ae6e72ef838b7ac283dd7d0820b01afb0 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 2 Aug 2015 12:09:55 -0400 Subject: [PATCH 07/15] support banner/footer option --- src/Bundle.js | 3 +++ test/form/banner-and-footer/_config.js | 7 +++++++ test/form/banner-and-footer/_expected/amd.js | 7 +++++++ test/form/banner-and-footer/_expected/cjs.js | 5 +++++ test/form/banner-and-footer/_expected/es6.js | 3 +++ test/form/banner-and-footer/_expected/iife.js | 7 +++++++ test/form/banner-and-footer/_expected/umd.js | 11 +++++++++++ test/form/banner-and-footer/main.js | 1 + 8 files changed, 44 insertions(+) create mode 100644 test/form/banner-and-footer/_config.js create mode 100644 test/form/banner-and-footer/_expected/amd.js create mode 100644 test/form/banner-and-footer/_expected/cjs.js create mode 100644 test/form/banner-and-footer/_expected/es6.js create mode 100644 test/form/banner-and-footer/_expected/iife.js create mode 100644 test/form/banner-and-footer/_expected/umd.js create mode 100644 test/form/banner-and-footer/main.js diff --git a/src/Bundle.js b/src/Bundle.js index a609448..2f4e7f2 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -337,6 +337,9 @@ export default class Bundle { indentString: getIndentString( magicString, options ) }, options ); + if ( options.banner ) magicString.prepend( options.banner + '\n' ); + if ( options.footer ) magicString.append( '\n' + options.footer ); + const code = magicString.toString(); let map = null; diff --git a/test/form/banner-and-footer/_config.js b/test/form/banner-and-footer/_config.js new file mode 100644 index 0000000..cc113e0 --- /dev/null +++ b/test/form/banner-and-footer/_config.js @@ -0,0 +1,7 @@ +module.exports = { + description: 'adds a banner/footer', + options: { + banner: '/* this is a banner */', + footer: '/* this is a footer */' + } +}; diff --git a/test/form/banner-and-footer/_expected/amd.js b/test/form/banner-and-footer/_expected/amd.js new file mode 100644 index 0000000..86e9835 --- /dev/null +++ b/test/form/banner-and-footer/_expected/amd.js @@ -0,0 +1,7 @@ +/* this is a banner */ +define(function () { 'use strict'; + + console.log( 'hello world' ); + +}); +/* this is a footer */ diff --git a/test/form/banner-and-footer/_expected/cjs.js b/test/form/banner-and-footer/_expected/cjs.js new file mode 100644 index 0000000..90c4119 --- /dev/null +++ b/test/form/banner-and-footer/_expected/cjs.js @@ -0,0 +1,5 @@ +/* this is a banner */ +'use strict'; + +console.log( 'hello world' ); +/* this is a footer */ diff --git a/test/form/banner-and-footer/_expected/es6.js b/test/form/banner-and-footer/_expected/es6.js new file mode 100644 index 0000000..93e3dfb --- /dev/null +++ b/test/form/banner-and-footer/_expected/es6.js @@ -0,0 +1,3 @@ +/* this is a banner */ +console.log( 'hello world' ); +/* this is a footer */ diff --git a/test/form/banner-and-footer/_expected/iife.js b/test/form/banner-and-footer/_expected/iife.js new file mode 100644 index 0000000..8135f24 --- /dev/null +++ b/test/form/banner-and-footer/_expected/iife.js @@ -0,0 +1,7 @@ +/* this is a banner */ +(function () { 'use strict'; + + console.log( 'hello world' ); + +})(); +/* this is a footer */ diff --git a/test/form/banner-and-footer/_expected/umd.js b/test/form/banner-and-footer/_expected/umd.js new file mode 100644 index 0000000..74dd673 --- /dev/null +++ b/test/form/banner-and-footer/_expected/umd.js @@ -0,0 +1,11 @@ +/* this is a banner */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + factory(); +}(this, function () { 'use strict'; + + console.log( 'hello world' ); + +})); +/* this is a footer */ diff --git a/test/form/banner-and-footer/main.js b/test/form/banner-and-footer/main.js new file mode 100644 index 0000000..3b5a604 --- /dev/null +++ b/test/form/banner-and-footer/main.js @@ -0,0 +1 @@ +console.log( 'hello world' ); From 9164f80487ab17165371ddc7b6224250530f82f8 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 2 Aug 2015 12:46:42 -0400 Subject: [PATCH 08/15] remove pre-existing sourcemap comments --- src/Module.js | 7 +++++++ .../removes-existing-sourcemap-comments/_config.js | 3 +++ .../_expected/amd.js | 9 +++++++++ .../_expected/cjs.js | 7 +++++++ .../_expected/es6.js | 5 +++++ .../_expected/iife.js | 9 +++++++++ .../_expected/umd.js | 13 +++++++++++++ .../form/removes-existing-sourcemap-comments/foo.js | 5 +++++ .../removes-existing-sourcemap-comments/main.js | 5 +++++ 9 files changed, 63 insertions(+) create mode 100644 test/form/removes-existing-sourcemap-comments/_config.js create mode 100644 test/form/removes-existing-sourcemap-comments/_expected/amd.js create mode 100644 test/form/removes-existing-sourcemap-comments/_expected/cjs.js create mode 100644 test/form/removes-existing-sourcemap-comments/_expected/es6.js create mode 100644 test/form/removes-existing-sourcemap-comments/_expected/iife.js create mode 100644 test/form/removes-existing-sourcemap-comments/_expected/umd.js create mode 100644 test/form/removes-existing-sourcemap-comments/foo.js create mode 100644 test/form/removes-existing-sourcemap-comments/main.js diff --git a/src/Module.js b/src/Module.js index 3cf9b8a..70c2ccb 100644 --- a/src/Module.js +++ b/src/Module.js @@ -41,6 +41,13 @@ export default class Module { filename: id }); + // remove existing sourceMappingURL comments + const pattern = /\/\/#\s+sourceMappingURL=.+\n?/g; + let match; + while ( match = pattern.exec( source ) ) { + this.magicString.remove( match.index, match.index + match[0].length ); + } + this.suggestedNames = blank(); this.comments = []; diff --git a/test/form/removes-existing-sourcemap-comments/_config.js b/test/form/removes-existing-sourcemap-comments/_config.js new file mode 100644 index 0000000..e1b917a --- /dev/null +++ b/test/form/removes-existing-sourcemap-comments/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'removes existing sourcemap comments' +}; diff --git a/test/form/removes-existing-sourcemap-comments/_expected/amd.js b/test/form/removes-existing-sourcemap-comments/_expected/amd.js new file mode 100644 index 0000000..e614344 --- /dev/null +++ b/test/form/removes-existing-sourcemap-comments/_expected/amd.js @@ -0,0 +1,9 @@ +define(function () { 'use strict'; + + function foo () { + return 42; + } + + console.log( foo() ); + +}); diff --git a/test/form/removes-existing-sourcemap-comments/_expected/cjs.js b/test/form/removes-existing-sourcemap-comments/_expected/cjs.js new file mode 100644 index 0000000..d7a8df2 --- /dev/null +++ b/test/form/removes-existing-sourcemap-comments/_expected/cjs.js @@ -0,0 +1,7 @@ +'use strict'; + +function foo () { + return 42; +} + +console.log( foo() ); diff --git a/test/form/removes-existing-sourcemap-comments/_expected/es6.js b/test/form/removes-existing-sourcemap-comments/_expected/es6.js new file mode 100644 index 0000000..c458828 --- /dev/null +++ b/test/form/removes-existing-sourcemap-comments/_expected/es6.js @@ -0,0 +1,5 @@ +function foo () { + return 42; +} + +console.log( foo() ); diff --git a/test/form/removes-existing-sourcemap-comments/_expected/iife.js b/test/form/removes-existing-sourcemap-comments/_expected/iife.js new file mode 100644 index 0000000..07a64a4 --- /dev/null +++ b/test/form/removes-existing-sourcemap-comments/_expected/iife.js @@ -0,0 +1,9 @@ +(function () { 'use strict'; + + function foo () { + return 42; + } + + console.log( foo() ); + +})(); diff --git a/test/form/removes-existing-sourcemap-comments/_expected/umd.js b/test/form/removes-existing-sourcemap-comments/_expected/umd.js new file mode 100644 index 0000000..79f644c --- /dev/null +++ b/test/form/removes-existing-sourcemap-comments/_expected/umd.js @@ -0,0 +1,13 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + factory(); +}(this, function () { 'use strict'; + + function foo () { + return 42; + } + + console.log( foo() ); + +})); diff --git a/test/form/removes-existing-sourcemap-comments/foo.js b/test/form/removes-existing-sourcemap-comments/foo.js new file mode 100644 index 0000000..74fda08 --- /dev/null +++ b/test/form/removes-existing-sourcemap-comments/foo.js @@ -0,0 +1,5 @@ +export default function () { + return 42; +} + +//# sourceMappingURL=foo.js.map diff --git a/test/form/removes-existing-sourcemap-comments/main.js b/test/form/removes-existing-sourcemap-comments/main.js new file mode 100644 index 0000000..3c48af8 --- /dev/null +++ b/test/form/removes-existing-sourcemap-comments/main.js @@ -0,0 +1,5 @@ +import foo from './foo'; + +console.log( foo() ); + +//# sourceMappingURL=main.js.map From f35d453c72b69760d20a258146d63fb91d1f3d8c Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 2 Aug 2015 12:57:13 -0400 Subject: [PATCH 09/15] deconflict external imports --- src/Bundle.js | 8 +++----- .../deconflicts-external-imports/_config.js | 13 +++++++++++++ test/function/deconflicts-external-imports/a.js | 5 +++++ test/function/deconflicts-external-imports/b.js | 5 +++++ test/function/deconflicts-external-imports/main.js | 5 +++++ 5 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 test/function/deconflicts-external-imports/_config.js create mode 100644 test/function/deconflicts-external-imports/a.js create mode 100644 test/function/deconflicts-external-imports/b.js create mode 100644 test/function/deconflicts-external-imports/main.js diff --git a/src/Bundle.js b/src/Bundle.js index 2f4e7f2..dcc50c7 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -91,16 +91,14 @@ export default class Bundle { // Assign names to external modules this.externalModules.forEach( module => { - // TODO is this right? let name = makeLegalIdentifier( module.suggestedNames['*'] || module.suggestedNames.default || module.id ); - if ( definers[ name ] ) { + while ( definers[ name ] ) { conflicts[ name ] = true; - } else { - definers[ name ] = []; + name = `_${name}`; } - definers[ name ].push( module ); + definers[ name ] = [ module ]; module.name = name; this.assumedGlobals[ name ] = true; }); diff --git a/test/function/deconflicts-external-imports/_config.js b/test/function/deconflicts-external-imports/_config.js new file mode 100644 index 0000000..5d981c0 --- /dev/null +++ b/test/function/deconflicts-external-imports/_config.js @@ -0,0 +1,13 @@ +module.exports = { + description: 'deconflicts external imports', + context: { + require: function ( id ) { + return function () { + return id; + }; + } + }, + options: { + external: [ 'foo', 'bar' ] + } +}; diff --git a/test/function/deconflicts-external-imports/a.js b/test/function/deconflicts-external-imports/a.js new file mode 100644 index 0000000..b5f4daa --- /dev/null +++ b/test/function/deconflicts-external-imports/a.js @@ -0,0 +1,5 @@ +import foo from 'foo'; + +export default function () { + assert.equal( foo(), 'foo' ); +} diff --git a/test/function/deconflicts-external-imports/b.js b/test/function/deconflicts-external-imports/b.js new file mode 100644 index 0000000..8316a4a --- /dev/null +++ b/test/function/deconflicts-external-imports/b.js @@ -0,0 +1,5 @@ +import foo from 'bar'; + +export default function () { + assert.equal( foo(), 'bar' ); +} diff --git a/test/function/deconflicts-external-imports/main.js b/test/function/deconflicts-external-imports/main.js new file mode 100644 index 0000000..6527271 --- /dev/null +++ b/test/function/deconflicts-external-imports/main.js @@ -0,0 +1,5 @@ +import a from './a'; +import b from './b'; + +a(); +b(); From 39057cddf001b8de799ce7c50cd661974c565dcd Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 2 Aug 2015 13:49:36 -0400 Subject: [PATCH 10/15] use provided AST instead of parsing again --- src/Bundle.js | 8 +++++ src/Module.js | 35 +++++++++++----------- test/function/uses-supplied-ast/_config.js | 35 ++++++++++++++++++++++ 3 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 test/function/uses-supplied-ast/_config.js diff --git a/src/Bundle.js b/src/Bundle.js index dcc50c7..d174b57 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -191,9 +191,17 @@ export default class Bundle { if ( !this.modulePromises[ id ] ) { this.modulePromises[ id ] = Promise.resolve( this.load( id, this.loadOptions ) ) .then( source => { + let ast; + + if ( typeof source === 'object' ) { + ast = source.ast; + source = source.code; + } + const module = new Module({ id, source, + ast, bundle: this }); diff --git a/src/Module.js b/src/Module.js index 70c2ccb..85bd7a0 100644 --- a/src/Module.js +++ b/src/Module.js @@ -29,7 +29,7 @@ function isEmptyExportedVarDeclaration ( node, module, allBundleExports, es6 ) { } export default class Module { - constructor ({ id, source, bundle }) { + constructor ({ id, source, ast, bundle }) { this.source = source; this.bundle = bundle; @@ -51,7 +51,7 @@ export default class Module { this.suggestedNames = blank(); this.comments = []; - this.statements = this._parse(); + this.statements = this._parse( ast ); // imports and exports, indexed by ID this.imports = blank(); @@ -517,21 +517,22 @@ export default class Module { } // TODO rename this to parse, once https://github.com/rollup/rollup/issues/42 is fixed - _parse () { - // Try to extract a list of top-level statements/declarations. If - // the parse fails, attach file info and abort - let ast; - - try { - ast = parse( this.source, { - ecmaVersion: 6, - sourceType: 'module', - onComment: ( block, text, start, end ) => this.comments.push({ block, text, start, end }) - }); - } catch ( err ) { - err.code = 'PARSE_ERROR'; - err.file = this.id; // see above - not necessarily true, but true enough - throw err; + _parse ( ast ) { + // The ast can be supplied programmatically (but usually won't be) + if ( !ast ) { + // Try to extract a list of top-level statements/declarations. If + // the parse fails, attach file info and abort + try { + ast = parse( this.source, { + ecmaVersion: 6, + sourceType: 'module', + onComment: ( block, text, start, end ) => this.comments.push({ block, text, start, end }) + }); + } catch ( err ) { + err.code = 'PARSE_ERROR'; + err.file = this.id; // see above - not necessarily true, but true enough + throw err; + } } walk( ast, { diff --git a/test/function/uses-supplied-ast/_config.js b/test/function/uses-supplied-ast/_config.js new file mode 100644 index 0000000..e7c9b00 --- /dev/null +++ b/test/function/uses-supplied-ast/_config.js @@ -0,0 +1,35 @@ +var acorn = require( 'acorn' ); + +var modules = { + 'main': 'import foo from \'foo\';\nfoo();', + + // the code points to './bar' but the AST points to './baz', so we + // can check the AST is being used + 'foo': { + code: 'import bar from \'bar\';\nexport default function foo () {\n\tconsole.log( bar );\n}', + ast: acorn.parse( 'import bar from \'baz\';\nexport default function foo () {\n\tconsole.log( bar );\n}', { + ecmaVersion: 6, + sourceType: 'module' + }) + }, + + 'baz': 'export default 42;' +}; + +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' ); + } + + return modules[ id ]; + } + }, + solo: true +}; From 418ce08a444106ab66b53053307291b47f645800 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 2 Aug 2015 13:58:56 -0400 Subject: [PATCH 11/15] rename internal namespace exports as appropriate --- src/Bundle.js | 5 ++++- .../import-namespace-from-internal-module-renamed/_config.js | 3 +++ .../import-namespace-from-internal-module-renamed/foo.js | 3 +++ .../import-namespace-from-internal-module-renamed/main.js | 3 +++ test/function/uses-supplied-ast/_config.js | 5 ++--- 5 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 test/function/import-namespace-from-internal-module-renamed/_config.js create mode 100644 test/function/import-namespace-from-internal-module-renamed/foo.js create mode 100644 test/function/import-namespace-from-internal-module-renamed/main.js diff --git a/src/Bundle.js b/src/Bundle.js index d174b57..4379619 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -323,7 +323,10 @@ export default class Bundle { const exportKeys = keys( module.exports ); return `var ${module.getCanonicalName('*', format === 'es6')} = {\n` + - exportKeys.map( key => `${indentString}get ${key} () { return ${module.getCanonicalName(key, format === 'es6')}; }` ).join( ',\n' ) + + exportKeys.map( key => { + const localName = module.exports[ key ].localName; + return `${indentString}get ${key} () { return ${module.getCanonicalName(localName, format === 'es6')}; }`; + }).join( ',\n' ) + `\n};\n\n`; }).join( '' ); diff --git a/test/function/import-namespace-from-internal-module-renamed/_config.js b/test/function/import-namespace-from-internal-module-renamed/_config.js new file mode 100644 index 0000000..77f908a --- /dev/null +++ b/test/function/import-namespace-from-internal-module-renamed/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'correctly exports x as y inside a bundle' +}; diff --git a/test/function/import-namespace-from-internal-module-renamed/foo.js b/test/function/import-namespace-from-internal-module-renamed/foo.js new file mode 100644 index 0000000..6a9cd7c --- /dev/null +++ b/test/function/import-namespace-from-internal-module-renamed/foo.js @@ -0,0 +1,3 @@ +var x = 42; + +export { x as y }; diff --git a/test/function/import-namespace-from-internal-module-renamed/main.js b/test/function/import-namespace-from-internal-module-renamed/main.js new file mode 100644 index 0000000..12aa3ee --- /dev/null +++ b/test/function/import-namespace-from-internal-module-renamed/main.js @@ -0,0 +1,3 @@ +import * as foo from './foo'; + +assert.equal( foo.y, 42 ); diff --git a/test/function/uses-supplied-ast/_config.js b/test/function/uses-supplied-ast/_config.js index e7c9b00..fff6bd6 100644 --- a/test/function/uses-supplied-ast/_config.js +++ b/test/function/uses-supplied-ast/_config.js @@ -12,7 +12,7 @@ var modules = { sourceType: 'module' }) }, - + 'baz': 'export default 42;' }; @@ -30,6 +30,5 @@ module.exports = { return modules[ id ]; } - }, - solo: true + } }; From 0b5dcbced20cf7458d0e8b079824ca20cc337d37 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 2 Aug 2015 14:18:04 -0400 Subject: [PATCH 12/15] make _scope configurable - avoids some test headaches --- src/Statement.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Statement.js b/src/Statement.js index e559b99..ebfbb8a 100644 --- a/src/Statement.js +++ b/src/Statement.js @@ -90,7 +90,11 @@ export default class Statement { } if ( newScope ) { - Object.defineProperty( node, '_scope', { value: newScope }); + Object.defineProperty( node, '_scope', { + value: newScope, + configurable: true + }); + scope = newScope; } }, From c2ebdcbe4c0afe04a30ca7b92c38a4f185a522b1 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 2 Aug 2015 16:00:35 -0400 Subject: [PATCH 13/15] dont rewrite var foo as var exports.foo --- src/Module.js | 6 +++++- .../_config.js | 11 +++++++++++ .../export-and-import-reference-share-var/foo.js | 2 ++ .../export-and-import-reference-share-var/main.js | 13 +++++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 test/function/export-and-import-reference-share-var/_config.js create mode 100644 test/function/export-and-import-reference-share-var/foo.js create mode 100644 test/function/export-and-import-reference-share-var/main.js diff --git a/src/Module.js b/src/Module.js index 85bd7a0..9790351 100644 --- a/src/Module.js +++ b/src/Module.js @@ -641,7 +641,11 @@ export default class Module { // split up/remove var declarations as necessary if ( statement.node.isSynthetic ) { - magicString.insert( statement.start, `${statement.node.kind} ` ); + // insert `var/let/const` if necessary + if ( !allBundleExports[ statement.node.declarations[0].id.name ] ) { + magicString.insert( statement.start, `${statement.node.kind} ` ); + } + magicString.overwrite( statement.end, statement.next, ';\n' ); // TODO account for trailing newlines } diff --git a/test/function/export-and-import-reference-share-var/_config.js b/test/function/export-and-import-reference-share-var/_config.js new file mode 100644 index 0000000..f46bf2c --- /dev/null +++ b/test/function/export-and-import-reference-share-var/_config.js @@ -0,0 +1,11 @@ +var assert = require( 'assert' ); + +module.exports = { + description: 'allows export and import reference to share name', + exports: function ( exports ) { + assert.equal( exports.b, 9 ); + }, + solo: true +}; + +// adapted from es6-module-transpiler diff --git a/test/function/export-and-import-reference-share-var/foo.js b/test/function/export-and-import-reference-share-var/foo.js new file mode 100644 index 0000000..e284e3a --- /dev/null +++ b/test/function/export-and-import-reference-share-var/foo.js @@ -0,0 +1,2 @@ +export var a = 1; +assert.equal(a, 1); diff --git a/test/function/export-and-import-reference-share-var/main.js b/test/function/export-and-import-reference-share-var/main.js new file mode 100644 index 0000000..f37ca04 --- /dev/null +++ b/test/function/export-and-import-reference-share-var/main.js @@ -0,0 +1,13 @@ +import { a } from './foo'; + +// This variable declaration is going to be altered because `b` needs to be +// re-written. We need to make sure that the `a` re-writing and the unaffected +// `c` declarator are not being clobbered by that alteration. +var a_ = a, b = 9, c = 'c'; + +assert.equal(a, 1); +assert.equal(a_, 1); +assert.equal(b, 9); +assert.equal(c, 'c'); + +export { b }; From 09eaef614305a049ca5aac6a949bf35e75797cd7 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 2 Aug 2015 16:15:35 -0400 Subject: [PATCH 14/15] rename variables named `exports` --- src/Bundle.js | 2 ++ test/function/deconflicts-exports/_config.js | 12 ++++++++++++ test/function/deconflicts-exports/main.js | 8 ++++++++ .../export-and-import-reference-share-var/_config.js | 3 +-- 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 test/function/deconflicts-exports/_config.js create mode 100644 test/function/deconflicts-exports/main.js diff --git a/src/Bundle.js b/src/Bundle.js index 4379619..7e9116e 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -39,7 +39,9 @@ export default class Bundle { this.statements = null; this.externalModules = []; this.internalNamespaceModules = []; + this.assumedGlobals = blank(); + this.assumedGlobals.exports = true; // TODO strictly speaking, this only applies with non-ES6, non-default-only bundles } build () { diff --git a/test/function/deconflicts-exports/_config.js b/test/function/deconflicts-exports/_config.js new file mode 100644 index 0000000..4bd81c0 --- /dev/null +++ b/test/function/deconflicts-exports/_config.js @@ -0,0 +1,12 @@ +var assert = require( 'assert' ); + +module.exports = { + description: 'renames variables named `exports` if necessary', + exports: function ( exports ) { + assert.deepEqual( Object.keys( exports ), [ 'a', 'b' ] ); + assert.equal( exports.a, 'A' ); + assert.equal( exports.b, 42 ); + }, + solo: true, + show: true +}; diff --git a/test/function/deconflicts-exports/main.js b/test/function/deconflicts-exports/main.js new file mode 100644 index 0000000..ea04690 --- /dev/null +++ b/test/function/deconflicts-exports/main.js @@ -0,0 +1,8 @@ +var exports = { + number: 21 +}; + +export var a = 'A'; +export var b = exports.number * 2; + +assert.deepEqual( Object.keys( exports ), [ 'number' ]); diff --git a/test/function/export-and-import-reference-share-var/_config.js b/test/function/export-and-import-reference-share-var/_config.js index f46bf2c..321e785 100644 --- a/test/function/export-and-import-reference-share-var/_config.js +++ b/test/function/export-and-import-reference-share-var/_config.js @@ -4,8 +4,7 @@ module.exports = { description: 'allows export and import reference to share name', exports: function ( exports ) { assert.equal( exports.b, 9 ); - }, - solo: true + } }; // adapted from es6-module-transpiler From b6aa24c23d09056c4d171bdbfd6ca1f2fbf57aa4 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 2 Aug 2015 16:33:53 -0400 Subject: [PATCH 15/15] update test config --- test/function/deconflicts-exports/_config.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/function/deconflicts-exports/_config.js b/test/function/deconflicts-exports/_config.js index 4bd81c0..8724967 100644 --- a/test/function/deconflicts-exports/_config.js +++ b/test/function/deconflicts-exports/_config.js @@ -6,7 +6,5 @@ module.exports = { assert.deepEqual( Object.keys( exports ), [ 'a', 'b' ] ); assert.equal( exports.a, 'A' ); assert.equal( exports.b, 42 ); - }, - solo: true, - show: true + } };