From bcd5dc789c25cec25fa1f2941206781785e63f19 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 28 Apr 2016 10:17:13 -0400 Subject: [PATCH 01/99] ensure import paths are case-sensitive (#590) --- src/utils/defaults.js | 11 ++++++----- test/function/paths-are-case-sensitive/Foo.js | 3 +++ test/function/paths-are-case-sensitive/_config.js | 8 ++++++++ test/function/paths-are-case-sensitive/main.js | 3 +++ 4 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 test/function/paths-are-case-sensitive/Foo.js create mode 100644 test/function/paths-are-case-sensitive/_config.js create mode 100644 test/function/paths-are-case-sensitive/main.js diff --git a/src/utils/defaults.js b/src/utils/defaults.js index 70c36fe..449e320 100644 --- a/src/utils/defaults.js +++ b/src/utils/defaults.js @@ -1,5 +1,5 @@ -import { isFile, readFileSync } from './fs.js'; -import { dirname, isAbsolute, resolve } from './path.js'; +import { isFile, readdirSync, readFileSync } from './fs.js'; +import { basename, dirname, isAbsolute, resolve } from './path.js'; import { blank } from './object.js'; export function load ( id ) { @@ -7,10 +7,11 @@ export function load ( id ) { } function addJsExtensionIfNecessary ( file ) { - if ( isFile( file ) ) return file; + const name = basename( file ); + const files = readdirSync( dirname( file ) ); - file += '.js'; - if ( isFile( file ) ) return file; + if ( ~files.indexOf( name ) && isFile( file ) ) return file; + if ( ~files.indexOf( `${name}.js` ) && isFile( `${file}.js` ) ) return `${file}.js`; return null; } diff --git a/test/function/paths-are-case-sensitive/Foo.js b/test/function/paths-are-case-sensitive/Foo.js new file mode 100644 index 0000000..81baecb --- /dev/null +++ b/test/function/paths-are-case-sensitive/Foo.js @@ -0,0 +1,3 @@ +export default function () { + assert.ok( false ); +} diff --git a/test/function/paths-are-case-sensitive/_config.js b/test/function/paths-are-case-sensitive/_config.js new file mode 100644 index 0000000..3f81b80 --- /dev/null +++ b/test/function/paths-are-case-sensitive/_config.js @@ -0,0 +1,8 @@ +var assert = require( 'assert' ); + +module.exports = { + description: 'insists on correct casing for imports', + error: function ( err ) { + assert.ok( /Could not resolve/.test( err.message ) ); + } +}; diff --git a/test/function/paths-are-case-sensitive/main.js b/test/function/paths-are-case-sensitive/main.js new file mode 100644 index 0000000..e1fb5c4 --- /dev/null +++ b/test/function/paths-are-case-sensitive/main.js @@ -0,0 +1,3 @@ +import foo from './foo.js'; + +foo(); From 4ccb7e744f90f0c798efb8c4540872a08a5826a3 Mon Sep 17 00:00:00 2001 From: Nelson Date: Thu, 25 Aug 2016 17:51:36 +0100 Subject: [PATCH 02/99] Update link to codecov image. fixes https://github.com/rollup/rollup/issues/878 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e02e51..c5de896 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ alt="dependency status"> - Coverage via Codecov + Coverage via Codecov Date: Mon, 29 Aug 2016 16:03:24 -0400 Subject: [PATCH 03/99] ensure intro appears before interop block (#880) --- src/Bundle.js | 6 +++--- src/finalisers/amd.js | 8 +++++--- src/finalisers/cjs.js | 6 +++--- src/finalisers/es.js | 7 +++---- src/finalisers/iife.js | 20 +++++++++++--------- src/finalisers/umd.js | 12 ++++++++---- test/form/intro-and-outro/_config.js | 3 ++- test/form/intro-and-outro/_expected/amd.js | 7 +++++-- test/form/intro-and-outro/_expected/cjs.js | 8 +++++++- test/form/intro-and-outro/_expected/es.js | 5 ++++- test/form/intro-and-outro/_expected/iife.js | 9 ++++++--- test/form/intro-and-outro/_expected/umd.js | 13 ++++++++----- test/form/intro-and-outro/main.js | 5 ++++- 13 files changed, 69 insertions(+), 40 deletions(-) diff --git a/src/Bundle.js b/src/Bundle.js index ffffbf6..e458975 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -312,21 +312,21 @@ export default class Bundle { } }); - const intro = [ options.intro ] + let intro = [ options.intro ] .concat( this.plugins.map( plugin => plugin.intro && plugin.intro() ) ) .filter( Boolean ) .join( '\n\n' ); - if ( intro ) magicString.prepend( intro + '\n' ); + if ( intro ) intro += '\n'; const indentString = getIndentString( magicString, options ); const finalise = finalisers[ format ]; if ( !finalise ) throw new Error( `You must specify an output type - valid options are ${keys( finalisers ).join( ', ' )}` ); - magicString = finalise( this, magicString.trim(), { exportMode, indentString }, options ); + magicString = finalise( this, magicString.trim(), { exportMode, indentString, intro }, options ); const banner = [ options.banner ] .concat( this.plugins.map( plugin => plugin.banner ) ) diff --git a/src/finalisers/amd.js b/src/finalisers/amd.js index c04c88b..456dd86 100644 --- a/src/finalisers/amd.js +++ b/src/finalisers/amd.js @@ -3,7 +3,7 @@ import getInteropBlock from './shared/getInteropBlock.js'; import getExportBlock from './shared/getExportBlock.js'; import esModuleExport from './shared/esModuleExport.js'; -export default function amd ( bundle, magicString, { exportMode, indentString }, options ) { +export default function amd ( bundle, magicString, { exportMode, indentString, intro }, options ) { const deps = bundle.externalModules.map( quotePath ); const args = bundle.externalModules.map( getName ); @@ -17,12 +17,14 @@ export default function amd ( bundle, magicString, { exportMode, indentString }, ( deps.length ? `[${deps.join( ', ' )}], ` : `` ); const useStrict = options.useStrict !== false ? ` 'use strict';` : ``; - const intro = `define(${params}function (${args.join( ', ' )}) {${useStrict}\n\n`; + const wrapperStart = `define(${params}function (${args.join( ', ' )}) {${useStrict}\n\n`; // var foo__default = 'default' in foo ? foo['default'] : foo; const interopBlock = getInteropBlock( bundle ); if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' ); + if ( intro ) magicString.prepend( intro ); + const exportBlock = getExportBlock( bundle.entryModule, exportMode ); if ( exportBlock ) magicString.append( '\n\n' + exportBlock ); if ( exportMode === 'named' ) magicString.append( `\n\n${esModuleExport}` ); @@ -31,5 +33,5 @@ export default function amd ( bundle, magicString, { exportMode, indentString }, return magicString .indent( indentString ) .append( '\n\n});' ) - .prepend( intro ); + .prepend( wrapperStart ); } diff --git a/src/finalisers/cjs.js b/src/finalisers/cjs.js index b12d621..0ffbce8 100644 --- a/src/finalisers/cjs.js +++ b/src/finalisers/cjs.js @@ -1,9 +1,9 @@ import getExportBlock from './shared/getExportBlock.js'; import esModuleExport from './shared/esModuleExport.js'; -export default function cjs ( bundle, magicString, { exportMode }, options ) { - let intro = ( options.useStrict === false ? `` : `'use strict';\n\n` ) + - ( exportMode === 'named' ? `${esModuleExport}\n\n` : '' ); +export default function cjs ( bundle, magicString, { exportMode, intro }, options ) { + intro = ( options.useStrict === false ? intro : `'use strict';\n\n${intro}` ) + + ( exportMode === 'named' ? `${esModuleExport}\n\n` : '' ); let needsInterop = false; diff --git a/src/finalisers/es.js b/src/finalisers/es.js index a788083..4e08ce3 100644 --- a/src/finalisers/es.js +++ b/src/finalisers/es.js @@ -4,7 +4,7 @@ function notDefault ( name ) { return name !== 'default'; } -export default function es ( bundle, magicString, config, options ) { +export default function es ( bundle, magicString, { intro }, options ) { const importBlock = bundle.externalModules .map( module => { const specifiers = []; @@ -49,9 +49,8 @@ export default function es ( bundle, magicString, config, options ) { }) .join( '\n' ); - if ( importBlock ) { - magicString.prepend( importBlock + '\n\n' ); - } + if ( importBlock ) intro += importBlock + '\n\n'; + if ( intro ) magicString.prepend( intro ); const module = bundle.entryModule; diff --git a/src/finalisers/iife.js b/src/finalisers/iife.js index 319bd90..612f70f 100644 --- a/src/finalisers/iife.js +++ b/src/finalisers/iife.js @@ -16,7 +16,7 @@ function setupNamespace ( keypath ) { .join( '\n' ) + '\n'; } -export default function iife ( bundle, magicString, { exportMode, indentString }, options ) { +export default function iife ( bundle, magicString, { exportMode, indentString, intro }, options ) { const globalNameMaker = getGlobalNameMaker( options.globals || blank(), bundle.onwarn ); const name = options.moduleName; @@ -35,29 +35,31 @@ export default function iife ( bundle, magicString, { exportMode, indentString } args.unshift( 'exports' ); } - const useStrict = options.useStrict !== false ? `'use strict';` : ``; + const useStrict = options.useStrict !== false ? `${indentString}'use strict';\n\n` : ``; - let intro = `(function (${args}) {\n`; - const outro = `\n\n}(${dependencies}));`; + let wrapperIntro = `(function (${args}) {\n${useStrict}`; + const wrapperOutro = `\n\n}(${dependencies}));`; if ( exportMode === 'default' ) { - intro = ( isNamespaced ? `this.` : `${bundle.varOrConst} ` ) + `${name} = ${intro}`; + wrapperIntro = ( isNamespaced ? `this.` : `${bundle.varOrConst} ` ) + `${name} = ${wrapperIntro}`; } if ( isNamespaced ) { - intro = setupNamespace( name ) + intro; + wrapperIntro = setupNamespace( name ) + wrapperIntro; } // var foo__default = 'default' in foo ? foo['default'] : foo; const interopBlock = getInteropBlock( bundle ); if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' ); - if ( useStrict ) magicString.prepend( useStrict + '\n\n' ); + + if ( intro ) magicString.prepend( intro ); + const exportBlock = getExportBlock( bundle.entryModule, exportMode ); if ( exportBlock ) magicString.append( '\n\n' + exportBlock ); if ( options.outro ) magicString.append( `\n${options.outro}` ); return magicString .indent( indentString ) - .prepend( intro ) - .append( outro ); + .prepend( wrapperIntro ) + .append( wrapperOutro ); } diff --git a/src/finalisers/umd.js b/src/finalisers/umd.js index 11631f5..a36db02 100644 --- a/src/finalisers/umd.js +++ b/src/finalisers/umd.js @@ -16,7 +16,9 @@ function setupNamespace ( name ) { .join( ', ' ); } -export default function umd ( bundle, magicString, { exportMode, indentString }, options ) { +const wrapperOutro = '\n\n})));'; + +export default function umd ( bundle, magicString, { exportMode, indentString, intro }, options ) { if ( exportMode !== 'none' && !options.moduleName ) { throw new Error( 'You must supply options.moduleName for UMD bundles' ); } @@ -54,7 +56,7 @@ export default function umd ( bundle, magicString, { exportMode, indentString }, exports.noConflict = function() { global.${options.moduleName} = current; return exports; }; })()` : `(${defaultExport}factory(${globalDeps}))`; - const intro = + const wrapperIntro = `(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? ${cjsExport}factory(${cjsDeps.join( ', ' )}) : typeof define === 'function' && define.amd ? define(${amdParams}factory) : @@ -67,6 +69,8 @@ export default function umd ( bundle, magicString, { exportMode, indentString }, const interopBlock = getInteropBlock( bundle ); if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' ); + if ( intro ) magicString.prepend( intro ); + const exportBlock = getExportBlock( bundle.entryModule, exportMode ); if ( exportBlock ) magicString.append( '\n\n' + exportBlock ); if ( exportMode === 'named' ) magicString.append( `\n\n${esModuleExport}` ); @@ -75,6 +79,6 @@ export default function umd ( bundle, magicString, { exportMode, indentString }, return magicString .trim() .indent( indentString ) - .append( '\n\n})));' ) - .prepend( intro ); + .append( wrapperOutro ) + .prepend( wrapperIntro ); } diff --git a/test/form/intro-and-outro/_config.js b/test/form/intro-and-outro/_config.js index 843e554..410d586 100644 --- a/test/form/intro-and-outro/_config.js +++ b/test/form/intro-and-outro/_config.js @@ -3,6 +3,7 @@ module.exports = { options: { intro: '/* this is an intro */', outro: '/* this is an outro */', - moduleName: 'foo' + moduleName: 'foo', + external: [ 'external' ] } }; diff --git a/test/form/intro-and-outro/_expected/amd.js b/test/form/intro-and-outro/_expected/amd.js index 3c16c97..ca940e4 100644 --- a/test/form/intro-and-outro/_expected/amd.js +++ b/test/form/intro-and-outro/_expected/amd.js @@ -1,7 +1,10 @@ -define(function () { 'use strict'; +define(['external'], function (a) { 'use strict'; /* this is an intro */ - console.log( 'hello world' ); + var a__default = 'default' in a ? a['default'] : a; + + console.log( a__default ); + console.log( a.b ); var main = 42; diff --git a/test/form/intro-and-outro/_expected/cjs.js b/test/form/intro-and-outro/_expected/cjs.js index dde8f6b..6c68e67 100644 --- a/test/form/intro-and-outro/_expected/cjs.js +++ b/test/form/intro-and-outro/_expected/cjs.js @@ -1,7 +1,13 @@ 'use strict'; /* this is an intro */ -console.log( 'hello world' ); +function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } + +var a = require('external'); +var a__default = _interopDefault(a); + +console.log( a__default ); +console.log( a.b ); var main = 42; diff --git a/test/form/intro-and-outro/_expected/es.js b/test/form/intro-and-outro/_expected/es.js index 7459f70..2da2a74 100644 --- a/test/form/intro-and-outro/_expected/es.js +++ b/test/form/intro-and-outro/_expected/es.js @@ -1,5 +1,8 @@ /* this is an intro */ -console.log( 'hello world' ); +import a, { b } from 'external'; + +console.log( a ); +console.log( b ); var main = 42; diff --git a/test/form/intro-and-outro/_expected/iife.js b/test/form/intro-and-outro/_expected/iife.js index ef591fc..2c716d3 100644 --- a/test/form/intro-and-outro/_expected/iife.js +++ b/test/form/intro-and-outro/_expected/iife.js @@ -1,12 +1,15 @@ -var foo = (function () { +var foo = (function (a) { 'use strict'; /* this is an intro */ - console.log( 'hello world' ); + var a__default = 'default' in a ? a['default'] : a; + + console.log( a__default ); + console.log( a.b ); var main = 42; return main; /* this is an outro */ -}()); +}(a)); diff --git a/test/form/intro-and-outro/_expected/umd.js b/test/form/intro-and-outro/_expected/umd.js index 97b8e3e..232db4e 100644 --- a/test/form/intro-and-outro/_expected/umd.js +++ b/test/form/intro-and-outro/_expected/umd.js @@ -1,11 +1,14 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global.foo = factory()); -}(this, (function () { 'use strict'; + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('external')) : + typeof define === 'function' && define.amd ? define(['external'], factory) : + (global.foo = factory(global.a)); +}(this, (function (a) { 'use strict'; /* this is an intro */ - console.log( 'hello world' ); + var a__default = 'default' in a ? a['default'] : a; + + console.log( a__default ); + console.log( a.b ); var main = 42; diff --git a/test/form/intro-and-outro/main.js b/test/form/intro-and-outro/main.js index 6054017..663ee99 100644 --- a/test/form/intro-and-outro/main.js +++ b/test/form/intro-and-outro/main.js @@ -1,3 +1,6 @@ -console.log( 'hello world' ); +import a, { b } from 'external' + +console.log( a ); +console.log( b ); export default 42; From 1d7c853ce6fabec207401a53d8210ea25f08d72e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 29 Aug 2016 16:20:40 -0400 Subject: [PATCH 04/99] prevent leaky state, always clone magic-string --- src/Module.js | 2 +- test/test.js | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Module.js b/src/Module.js index 11abeca..d015182 100644 --- a/src/Module.js +++ b/src/Module.js @@ -448,7 +448,7 @@ export default class Module { } render ( es ) { - const magicString = this.magicString; + const magicString = this.magicString.clone(); this.statements.forEach( statement => { if ( !statement.isIncluded ) { diff --git a/test/test.js b/test/test.js index 4eb73a6..92a88f0 100644 --- a/test/test.js +++ b/test/test.js @@ -290,10 +290,12 @@ describe( 'rollup', function () { } }, config.options ); - ( config.skip ? describe.skip : config.solo ? describe.only : describe)( dir, () => { + ( config.skip ? describe.skip : config.solo ? describe.only : describe )( dir, () => { + const promise = rollup.rollup( options ); + PROFILES.forEach( profile => { it( 'generates ' + profile.format, () => { - return rollup.rollup( options ).then( bundle => { + return promise.then( bundle => { const options = extend( {}, config.options, { dest: FORM + '/' + dir + '/_actual/' + profile.format + '.js', format: profile.format From 3817b74002b66f901bdf3cce64eff9bafc7dc1c7 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Mon, 29 Aug 2016 20:01:39 -0400 Subject: [PATCH 05/99] replace require kludge in runRollup.js (#706 - comment) --- bin/src/runRollup.js | 7 +++---- rollup.config.cli.js | 10 +++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/bin/src/runRollup.js b/bin/src/runRollup.js index 270654f..70e028e 100644 --- a/bin/src/runRollup.js +++ b/bin/src/runRollup.js @@ -1,10 +1,9 @@ import { realpathSync } from 'fs'; +import { rollup } from 'rollup'; import relative from 'require-relative'; import handleError from './handleError'; import SOURCEMAPPING_URL from './sourceMappingUrl.js'; -const rollup = require( '../dist/rollup.js' ); // TODO make this an import, somehow - import { install as installSourcemapSupport } from 'source-map-support'; installSourcemapSupport(); @@ -58,7 +57,7 @@ export default function runRollup ( command ) { config = realpathSync( config ); } - rollup.rollup({ + rollup({ entry: config, onwarn: message => { if ( /Treating .+ as external dependency/.test( message ) ) return; @@ -217,7 +216,7 @@ function bundle ( options ) { handleError({ code: 'MISSING_INPUT_OPTION' }); } - return rollup.rollup( options ).then( bundle => { + return rollup( options ).then( bundle => { if ( options.dest ) { return bundle.write( options ); } diff --git a/rollup.config.cli.js b/rollup.config.cli.js index 95e8588..982f6b0 100644 --- a/rollup.config.cli.js +++ b/rollup.config.cli.js @@ -15,7 +15,7 @@ export default { buble(), commonjs({ include: 'node_modules/**', - namedExports: { 'chalk': [ 'red', 'cyan', 'grey' ] } + namedExports: { chalk: [ 'red', 'cyan', 'grey' ] } }), nodeResolve({ main: true @@ -25,6 +25,10 @@ export default { 'fs', 'path', 'module', - 'source-map-support' - ] + 'source-map-support', + 'rollup' + ], + paths: { + rollup: '../dist/rollup.js' + } }; From 32aa67583c543a474d1ccf4e0c98dbb9ae7a9ac2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 30 Aug 2016 17:41:36 -0400 Subject: [PATCH 06/99] -> v0.34.11 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acd43df..b63e3c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # rollup changelog +## 0.34.11 + +* Prevent leaky state when `bundle` is reused ([#875](https://github.com/rollup/rollup/issues/875)) +* Ensure `intro` appears before interop block ([#880](https://github.com/rollup/rollup/issues/880)) + ## 0.34.10 * Allow custom `options.context` to replace top-level `this` ([#851](https://github.com/rollup/rollup/issues/851)) diff --git a/package.json b/package.json index 25191db..b63da00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.34.10", + "version": "0.34.11", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From dab40ce0b98cb5fc5eb99e6c98b981373cd15e4f Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Tue, 30 Aug 2016 20:09:35 -0400 Subject: [PATCH 07/99] handle non-existent dir of import --- src/utils/defaults.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/utils/defaults.js b/src/utils/defaults.js index 08e16f2..c746a7f 100644 --- a/src/utils/defaults.js +++ b/src/utils/defaults.js @@ -7,11 +7,15 @@ export function load ( id ) { } function addJsExtensionIfNecessary ( file ) { - const name = basename( file ); - const files = readdirSync( dirname( file ) ); - - if ( ~files.indexOf( name ) && isFile( file ) ) return file; - if ( ~files.indexOf( `${name}.js` ) && isFile( `${file}.js` ) ) return `${file}.js`; + try { + const name = basename( file ); + const files = readdirSync( dirname( file ) ); + + if ( ~files.indexOf( name ) && isFile( file ) ) return file; + if ( ~files.indexOf( `${name}.js` ) && isFile( `${file}.js` ) ) return `${file}.js`; + } catch ( err ) { + // noop + } return null; } From 3de477137ac14e3cac5ae5350d2e4e3728dc4d37 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 31 Aug 2016 23:11:55 -0400 Subject: [PATCH 08/99] fix rollup-watch (#887) --- bin/src/runRollup.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/src/runRollup.js b/bin/src/runRollup.js index 70e028e..53a6231 100644 --- a/bin/src/runRollup.js +++ b/bin/src/runRollup.js @@ -1,5 +1,5 @@ import { realpathSync } from 'fs'; -import { rollup } from 'rollup'; +import * as rollup from 'rollup'; import relative from 'require-relative'; import handleError from './handleError'; import SOURCEMAPPING_URL from './sourceMappingUrl.js'; @@ -57,7 +57,7 @@ export default function runRollup ( command ) { config = realpathSync( config ); } - rollup({ + rollup.rollup({ entry: config, onwarn: message => { if ( /Treating .+ as external dependency/.test( message ) ) return; @@ -216,7 +216,7 @@ function bundle ( options ) { handleError({ code: 'MISSING_INPUT_OPTION' }); } - return rollup( options ).then( bundle => { + return rollup.rollup( options ).then( bundle => { if ( options.dest ) { return bundle.write( options ); } From 7ad9f6914d3a39fac01906fa1c2344c09f034c37 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 31 Aug 2016 23:12:25 -0400 Subject: [PATCH 09/99] -> v0.34.12 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b63e3c4..78270b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.34.12 + +* Fix `rollup --watch` ([#887](https://github.com/rollup/rollup/issues/887)) + ## 0.34.11 * Prevent leaky state when `bundle` is reused ([#875](https://github.com/rollup/rollup/issues/875)) diff --git a/package.json b/package.json index b63da00..fdd52f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.34.11", + "version": "0.34.12", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From a27ba30aa6ab400d0dde858e0f086cc554c5cbff Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Wed, 31 Aug 2016 23:24:19 -0700 Subject: [PATCH 10/99] Add options as second arg to .transformBundle Currently only piping through `format`, could certainly pass others if necessary. Maybe all should be brought through? --- src/Bundle.js | 2 +- src/utils/transformBundle.js | 4 ++-- test/form/transform-bundle-plugin-options/_config.js | 12 ++++++++++++ .../transform-bundle-plugin-options/_expected/amd.js | 1 + .../transform-bundle-plugin-options/_expected/cjs.js | 1 + .../transform-bundle-plugin-options/_expected/es.js | 1 + .../_expected/iife.js | 1 + .../transform-bundle-plugin-options/_expected/umd.js | 1 + test/form/transform-bundle-plugin-options/main.js | 1 + 9 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 test/form/transform-bundle-plugin-options/_config.js create mode 100644 test/form/transform-bundle-plugin-options/_expected/amd.js create mode 100644 test/form/transform-bundle-plugin-options/_expected/cjs.js create mode 100644 test/form/transform-bundle-plugin-options/_expected/es.js create mode 100644 test/form/transform-bundle-plugin-options/_expected/iife.js create mode 100644 test/form/transform-bundle-plugin-options/_expected/umd.js create mode 100644 test/form/transform-bundle-plugin-options/main.js diff --git a/src/Bundle.js b/src/Bundle.js index e458975..36d1675 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -347,7 +347,7 @@ export default class Bundle { let map = null; const bundleSourcemapChain = []; - code = transformBundle( code, this.plugins, bundleSourcemapChain ) + code = transformBundle( code, this.plugins, bundleSourcemapChain, options ) .replace( new RegExp( `\\/\\/#\\s+${SOURCEMAPPING_URL}=.+\\n?`, 'g' ), '' ); if ( options.sourceMap ) { diff --git a/src/utils/transformBundle.js b/src/utils/transformBundle.js index 027c7f0..8569ad5 100644 --- a/src/utils/transformBundle.js +++ b/src/utils/transformBundle.js @@ -1,13 +1,13 @@ import { decode } from 'sourcemap-codec'; -export default function transformBundle ( code, plugins, sourceMapChain ) { +export default function transformBundle ( code, plugins, sourceMapChain, options ) { return plugins.reduce( ( code, plugin ) => { if ( !plugin.transformBundle ) return code; let result; try { - result = plugin.transformBundle( code ); + result = plugin.transformBundle( code, { format : options.format } ); } catch ( err ) { err.plugin = plugin.name; err.message = `Error transforming bundle${plugin.name ? ` with '${plugin.name}' plugin` : ''}: ${err.message}`; diff --git a/test/form/transform-bundle-plugin-options/_config.js b/test/form/transform-bundle-plugin-options/_config.js new file mode 100644 index 0000000..417e38f --- /dev/null +++ b/test/form/transform-bundle-plugin-options/_config.js @@ -0,0 +1,12 @@ +module.exports = { + description: 'plugin .transformBundle gets passed options', + options: { + plugins: [ + { + transformBundle: function (code, options) { + return JSON.stringify(options); + } + } + ] + } +}; diff --git a/test/form/transform-bundle-plugin-options/_expected/amd.js b/test/form/transform-bundle-plugin-options/_expected/amd.js new file mode 100644 index 0000000..69904ec --- /dev/null +++ b/test/form/transform-bundle-plugin-options/_expected/amd.js @@ -0,0 +1 @@ +{"format":"amd"} diff --git a/test/form/transform-bundle-plugin-options/_expected/cjs.js b/test/form/transform-bundle-plugin-options/_expected/cjs.js new file mode 100644 index 0000000..72dd9eb --- /dev/null +++ b/test/form/transform-bundle-plugin-options/_expected/cjs.js @@ -0,0 +1 @@ +{"format":"cjs"} diff --git a/test/form/transform-bundle-plugin-options/_expected/es.js b/test/form/transform-bundle-plugin-options/_expected/es.js new file mode 100644 index 0000000..3a0913b --- /dev/null +++ b/test/form/transform-bundle-plugin-options/_expected/es.js @@ -0,0 +1 @@ +{"format":"es"} diff --git a/test/form/transform-bundle-plugin-options/_expected/iife.js b/test/form/transform-bundle-plugin-options/_expected/iife.js new file mode 100644 index 0000000..9bdbe7b --- /dev/null +++ b/test/form/transform-bundle-plugin-options/_expected/iife.js @@ -0,0 +1 @@ +{"format":"iife"} diff --git a/test/form/transform-bundle-plugin-options/_expected/umd.js b/test/form/transform-bundle-plugin-options/_expected/umd.js new file mode 100644 index 0000000..c7dad30 --- /dev/null +++ b/test/form/transform-bundle-plugin-options/_expected/umd.js @@ -0,0 +1 @@ +{"format":"umd"} diff --git a/test/form/transform-bundle-plugin-options/main.js b/test/form/transform-bundle-plugin-options/main.js new file mode 100644 index 0000000..934dee7 --- /dev/null +++ b/test/form/transform-bundle-plugin-options/main.js @@ -0,0 +1 @@ +console.log( 1 + 1 ); From 72e4637c3f26251d2f96a6fcbfd32d42926391d3 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 1 Sep 2016 08:36:21 -0400 Subject: [PATCH 11/99] missing changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78270b5..d50425a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 0.34.12 * Fix `rollup --watch` ([#887](https://github.com/rollup/rollup/issues/887)) +* Case-sensitive paths ([#862](https://github.com/rollup/rollup/issues/862)) ## 0.34.11 From 7fb7ee494d70cd9cab3555474b66c4b307a6dcb2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 1 Sep 2016 09:45:26 -0400 Subject: [PATCH 12/99] -> v0.34.13 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d50425a..3739a73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.34.13 + +* Pass `{ format }` through to `transformBundle` ([#867](https://github.com/rollup/rollup/issues/867)) + ## 0.34.12 * Fix `rollup --watch` ([#887](https://github.com/rollup/rollup/issues/887)) diff --git a/package.json b/package.json index fdd52f6..7845b4c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.34.12", + "version": "0.34.13", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From 62503515f3d94d89670472c36891d77019f20b26 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Mon, 5 Sep 2016 00:01:54 +0300 Subject: [PATCH 13/99] Fix --help format description --- bin/src/help.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/src/help.md b/bin/src/help.md index cf2ef8b..58482d5 100644 --- a/bin/src/help.md +++ b/bin/src/help.md @@ -12,7 +12,7 @@ Basic options: -w, --watch Watch files in bundle and rebuild on changes -i, --input Input (alternative to ) -o, --output Output (if absent, prints to stdout) --f, --format [es6] Type of output (amd, cjs, es6, iife, umd) +-f, --format [es] Type of output (amd, cjs, es, iife, umd) -e, --external Comma-separate list of module IDs to exclude -g, --globals Comma-separate list of `module ID:Global` pairs Any module IDs defined here are added to external From 173cfc0df75e84ed4303887741d18831ed0076c8 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Tue, 6 Sep 2016 08:33:37 -0400 Subject: [PATCH 14/99] rewrite --- .eslintrc | 6 +- package.json | 4 +- rollup.config.js | 5 +- src/Bundle.js | 77 ++- src/Declaration.js | 242 ++------- src/Module.js | 494 +++--------------- src/Reference.js | 30 -- src/Statement.js | 160 ------ src/ast/Node.js | 92 ++++ src/ast/Scope.js | 52 -- src/ast/attachScopes.js | 78 --- src/ast/conditions.js | 38 -- src/ast/create.js | 7 - src/ast/enhance.js | 63 +++ src/ast/isFunctionDeclaration.js | 6 - src/ast/keys.js | 3 + src/ast/modifierNodes.js | 19 - src/ast/nodes/ArrayExpression.js | 8 + src/ast/nodes/ArrowFunctionExpression.js | 35 ++ src/ast/nodes/AssignmentExpression.js | 45 ++ src/ast/nodes/BinaryExpression.js | 38 ++ src/ast/nodes/BlockStatement.js | 71 +++ src/ast/nodes/CallExpression.js | 40 ++ src/ast/nodes/ClassDeclaration.js | 40 ++ src/ast/nodes/ClassExpression.js | 26 + src/ast/nodes/ConditionalExpression.js | 65 +++ src/ast/nodes/ExportAllDeclaration.js | 11 + src/ast/nodes/ExportDefaultDeclaration.js | 124 +++++ src/ast/nodes/ExportNamedDeclaration.js | 25 + src/ast/nodes/ExpressionStatement.js | 16 + src/ast/nodes/FunctionDeclaration.js | 53 ++ src/ast/nodes/FunctionExpression.js | 21 + src/ast/nodes/Identifier.js | 35 ++ src/ast/nodes/IfStatement.js | 73 +++ src/ast/nodes/ImportDeclaration.js | 16 + src/ast/nodes/Literal.js | 17 + src/ast/nodes/MemberExpression.js | 74 +++ src/ast/nodes/NewExpression.js | 8 + src/ast/nodes/ObjectExpression.js | 8 + src/ast/nodes/ParenthesizedExpression.js | 11 + src/ast/nodes/ReturnStatement.js | 7 + src/ast/nodes/TemplateLiteral.js | 7 + src/ast/nodes/ThisExpression.js | 20 + src/ast/nodes/UnaryExpression.js | 34 ++ src/ast/nodes/UpdateExpression.js | 35 ++ src/ast/nodes/VariableDeclaration.js | 86 +++ src/ast/nodes/VariableDeclarator.js | 89 ++++ src/ast/nodes/index.js | 63 +++ src/ast/nodes/shared/callHasEffects.js | 65 +++ .../shared/disallowIllegalReassignment.js | 28 + src/ast/nodes/shared/isUsedByBundle.js | 48 ++ .../nodes/shared}/pureFunctions.js | 0 src/ast/scopes/BundleScope.js | 40 ++ src/ast/scopes/ModuleScope.js | 47 ++ src/ast/scopes/Scope.js | 91 ++++ src/ast/{ => utils}/extractNames.js | 0 src/ast/{ => utils}/flatten.js | 0 src/ast/{ => utils}/isReference.js | 0 src/ast/values.js | 8 + src/finalisers/es.js | 8 +- src/finalisers/shared/getExportBlock.js | 6 +- src/utils/run.js | 119 ----- .../_config.js | 2 +- test/form/external-imports/_expected/es.js | 4 +- .../_expected/es.js | 5 +- .../namespace-optimization/_expected/amd.js | 2 +- .../namespace-optimization/_expected/cjs.js | 2 +- .../namespace-optimization/_expected/es.js | 2 +- .../namespace-optimization/_expected/iife.js | 2 +- .../namespace-optimization/_expected/umd.js | 4 +- test/form/namespace-optimization/main.js | 2 +- test/form/no-treeshake/_expected/es.js | 3 +- .../_expected/amd.js | 2 +- .../_expected/cjs.js | 2 +- .../_expected/es.js | 2 +- .../_expected/iife.js | 2 +- .../_expected/umd.js | 4 +- test/form/side-effect-k/_expected/amd.js | 5 +- test/form/side-effect-k/_expected/cjs.js | 5 +- test/form/side-effect-k/_expected/es.js | 5 +- test/form/side-effect-k/_expected/iife.js | 5 +- test/form/side-effect-k/_expected/umd.js | 5 +- test/form/skips-dead-branches-b/_config.js | 3 + .../skips-dead-branches-b/_expected/amd.js | 9 + .../skips-dead-branches-b/_expected/cjs.js | 7 + .../skips-dead-branches-b/_expected/es.js | 5 + .../skips-dead-branches-b/_expected/iife.js | 10 + .../skips-dead-branches-b/_expected/umd.js | 13 + .../skips-dead-branches-b/main.js | 0 test/form/skips-dead-branches-c/_config.js | 3 + .../skips-dead-branches-c/_expected/amd.js | 9 + .../skips-dead-branches-c/_expected/cjs.js | 7 + .../skips-dead-branches-c/_expected/es.js | 5 + .../skips-dead-branches-c/_expected/iife.js | 10 + .../skips-dead-branches-c/_expected/umd.js | 13 + .../skips-dead-branches-c/main.js | 0 test/form/skips-dead-branches-d/_config.js | 3 + .../skips-dead-branches-d/_expected/amd.js | 9 + .../skips-dead-branches-d/_expected/cjs.js | 7 + .../skips-dead-branches-d/_expected/es.js | 5 + .../skips-dead-branches-d/_expected/iife.js | 10 + .../skips-dead-branches-d/_expected/umd.js | 13 + .../skips-dead-branches-d/main.js | 0 test/form/skips-dead-branches-e/_config.js | 3 + .../skips-dead-branches-e/_expected/amd.js | 9 + .../skips-dead-branches-e/_expected/cjs.js | 7 + .../skips-dead-branches-e/_expected/es.js | 5 + .../skips-dead-branches-e/_expected/iife.js | 10 + .../skips-dead-branches-e/_expected/umd.js | 13 + .../skips-dead-branches-e/main.js | 0 test/form/skips-dead-branches-f/_config.js | 3 + .../skips-dead-branches-f/_expected/amd.js | 9 + .../skips-dead-branches-f/_expected/cjs.js | 7 + .../skips-dead-branches-f/_expected/es.js | 5 + .../skips-dead-branches-f/_expected/iife.js | 10 + .../skips-dead-branches-f/_expected/umd.js | 13 + .../skips-dead-branches-f/main.js | 0 test/form/skips-dead-branches-g/_config.js | 3 + .../skips-dead-branches-g/_expected/amd.js | 10 + .../skips-dead-branches-g/_expected/cjs.js | 8 + .../skips-dead-branches-g/_expected/es.js | 6 + .../skips-dead-branches-g/_expected/iife.js | 11 + .../skips-dead-branches-g/_expected/umd.js | 14 + test/form/skips-dead-branches-g/main.js | 8 + test/form/skips-dead-branches/_config.js | 3 + .../form/skips-dead-branches/_expected/amd.js | 9 + .../form/skips-dead-branches/_expected/cjs.js | 7 + test/form/skips-dead-branches/_expected/es.js | 5 + .../skips-dead-branches/_expected/iife.js | 10 + .../form/skips-dead-branches/_expected/umd.js | 13 + .../skips-dead-branches/main.js | 0 .../string-indentation-b/_expected/amd.js | 3 +- .../string-indentation-b/_expected/cjs.js | 3 +- .../form/string-indentation-b/_expected/es.js | 3 +- .../string-indentation-b/_expected/iife.js | 3 +- .../string-indentation-b/_expected/umd.js | 3 +- .../function/consistent-renaming-b/_config.js | 2 +- .../consistent-renaming-b/altdir/two.js | 3 +- .../consistent-renaming-b/subdir/one.js | 2 +- .../consistent-renaming-b/subdir/two.js | 3 +- test/function/cycles-pathological/_config.js | 13 +- .../iife-strong-dependencies/_config.js | 13 +- .../_config.js | 2 +- test/function/no-imports/_config.js | 2 +- .../function/reassign-import-fails/_config.js | 2 +- .../function/skips-dead-branches-b/_config.js | 8 - .../function/skips-dead-branches-c/_config.js | 8 - .../function/skips-dead-branches-d/_config.js | 8 - .../function/skips-dead-branches-e/_config.js | 8 - .../function/skips-dead-branches-f/_config.js | 8 - .../function/skips-dead-branches-g/_config.js | 9 - test/function/skips-dead-branches-g/main.js | 6 - test/function/skips-dead-branches/_config.js | 8 - test/function/tracks-alias-mutations/bar.js | 6 +- test/test.js | 13 +- 155 files changed, 2285 insertions(+), 1298 deletions(-) delete mode 100644 src/Reference.js delete mode 100644 src/Statement.js create mode 100644 src/ast/Node.js delete mode 100644 src/ast/Scope.js delete mode 100644 src/ast/attachScopes.js delete mode 100644 src/ast/conditions.js delete mode 100644 src/ast/create.js create mode 100644 src/ast/enhance.js delete mode 100644 src/ast/isFunctionDeclaration.js create mode 100644 src/ast/keys.js delete mode 100644 src/ast/modifierNodes.js create mode 100644 src/ast/nodes/ArrayExpression.js create mode 100644 src/ast/nodes/ArrowFunctionExpression.js create mode 100644 src/ast/nodes/AssignmentExpression.js create mode 100644 src/ast/nodes/BinaryExpression.js create mode 100644 src/ast/nodes/BlockStatement.js create mode 100644 src/ast/nodes/CallExpression.js create mode 100644 src/ast/nodes/ClassDeclaration.js create mode 100644 src/ast/nodes/ClassExpression.js create mode 100644 src/ast/nodes/ConditionalExpression.js create mode 100644 src/ast/nodes/ExportAllDeclaration.js create mode 100644 src/ast/nodes/ExportDefaultDeclaration.js create mode 100644 src/ast/nodes/ExportNamedDeclaration.js create mode 100644 src/ast/nodes/ExpressionStatement.js create mode 100644 src/ast/nodes/FunctionDeclaration.js create mode 100644 src/ast/nodes/FunctionExpression.js create mode 100644 src/ast/nodes/Identifier.js create mode 100644 src/ast/nodes/IfStatement.js create mode 100644 src/ast/nodes/ImportDeclaration.js create mode 100644 src/ast/nodes/Literal.js create mode 100644 src/ast/nodes/MemberExpression.js create mode 100644 src/ast/nodes/NewExpression.js create mode 100644 src/ast/nodes/ObjectExpression.js create mode 100644 src/ast/nodes/ParenthesizedExpression.js create mode 100644 src/ast/nodes/ReturnStatement.js create mode 100644 src/ast/nodes/TemplateLiteral.js create mode 100644 src/ast/nodes/ThisExpression.js create mode 100644 src/ast/nodes/UnaryExpression.js create mode 100644 src/ast/nodes/UpdateExpression.js create mode 100644 src/ast/nodes/VariableDeclaration.js create mode 100644 src/ast/nodes/VariableDeclarator.js create mode 100644 src/ast/nodes/index.js create mode 100644 src/ast/nodes/shared/callHasEffects.js create mode 100644 src/ast/nodes/shared/disallowIllegalReassignment.js create mode 100644 src/ast/nodes/shared/isUsedByBundle.js rename src/{utils => ast/nodes/shared}/pureFunctions.js (100%) create mode 100644 src/ast/scopes/BundleScope.js create mode 100644 src/ast/scopes/ModuleScope.js create mode 100644 src/ast/scopes/Scope.js rename src/ast/{ => utils}/extractNames.js (100%) rename src/ast/{ => utils}/flatten.js (100%) rename src/ast/{ => utils}/isReference.js (100%) create mode 100644 src/ast/values.js delete mode 100644 src/utils/run.js create mode 100644 test/form/skips-dead-branches-b/_config.js create mode 100644 test/form/skips-dead-branches-b/_expected/amd.js create mode 100644 test/form/skips-dead-branches-b/_expected/cjs.js create mode 100644 test/form/skips-dead-branches-b/_expected/es.js create mode 100644 test/form/skips-dead-branches-b/_expected/iife.js create mode 100644 test/form/skips-dead-branches-b/_expected/umd.js rename test/{function => form}/skips-dead-branches-b/main.js (100%) create mode 100644 test/form/skips-dead-branches-c/_config.js create mode 100644 test/form/skips-dead-branches-c/_expected/amd.js create mode 100644 test/form/skips-dead-branches-c/_expected/cjs.js create mode 100644 test/form/skips-dead-branches-c/_expected/es.js create mode 100644 test/form/skips-dead-branches-c/_expected/iife.js create mode 100644 test/form/skips-dead-branches-c/_expected/umd.js rename test/{function => form}/skips-dead-branches-c/main.js (100%) create mode 100644 test/form/skips-dead-branches-d/_config.js create mode 100644 test/form/skips-dead-branches-d/_expected/amd.js create mode 100644 test/form/skips-dead-branches-d/_expected/cjs.js create mode 100644 test/form/skips-dead-branches-d/_expected/es.js create mode 100644 test/form/skips-dead-branches-d/_expected/iife.js create mode 100644 test/form/skips-dead-branches-d/_expected/umd.js rename test/{function => form}/skips-dead-branches-d/main.js (100%) create mode 100644 test/form/skips-dead-branches-e/_config.js create mode 100644 test/form/skips-dead-branches-e/_expected/amd.js create mode 100644 test/form/skips-dead-branches-e/_expected/cjs.js create mode 100644 test/form/skips-dead-branches-e/_expected/es.js create mode 100644 test/form/skips-dead-branches-e/_expected/iife.js create mode 100644 test/form/skips-dead-branches-e/_expected/umd.js rename test/{function => form}/skips-dead-branches-e/main.js (100%) create mode 100644 test/form/skips-dead-branches-f/_config.js create mode 100644 test/form/skips-dead-branches-f/_expected/amd.js create mode 100644 test/form/skips-dead-branches-f/_expected/cjs.js create mode 100644 test/form/skips-dead-branches-f/_expected/es.js create mode 100644 test/form/skips-dead-branches-f/_expected/iife.js create mode 100644 test/form/skips-dead-branches-f/_expected/umd.js rename test/{function => form}/skips-dead-branches-f/main.js (100%) create mode 100644 test/form/skips-dead-branches-g/_config.js create mode 100644 test/form/skips-dead-branches-g/_expected/amd.js create mode 100644 test/form/skips-dead-branches-g/_expected/cjs.js create mode 100644 test/form/skips-dead-branches-g/_expected/es.js create mode 100644 test/form/skips-dead-branches-g/_expected/iife.js create mode 100644 test/form/skips-dead-branches-g/_expected/umd.js create mode 100644 test/form/skips-dead-branches-g/main.js create mode 100644 test/form/skips-dead-branches/_config.js create mode 100644 test/form/skips-dead-branches/_expected/amd.js create mode 100644 test/form/skips-dead-branches/_expected/cjs.js create mode 100644 test/form/skips-dead-branches/_expected/es.js create mode 100644 test/form/skips-dead-branches/_expected/iife.js create mode 100644 test/form/skips-dead-branches/_expected/umd.js rename test/{function => form}/skips-dead-branches/main.js (100%) delete mode 100644 test/function/skips-dead-branches-b/_config.js delete mode 100644 test/function/skips-dead-branches-c/_config.js delete mode 100644 test/function/skips-dead-branches-d/_config.js delete mode 100644 test/function/skips-dead-branches-e/_config.js delete mode 100644 test/function/skips-dead-branches-f/_config.js delete mode 100644 test/function/skips-dead-branches-g/_config.js delete mode 100644 test/function/skips-dead-branches-g/main.js delete mode 100644 test/function/skips-dead-branches/_config.js diff --git a/.eslintrc b/.eslintrc index cab1314..9fded74 100644 --- a/.eslintrc +++ b/.eslintrc @@ -27,7 +27,11 @@ "browser": true, "node": true }, - "extends": "eslint:recommended", + "extends": [ + "eslint:recommended", + "plugin:import/errors", + "plugin:import/warnings" + ], "parserOptions": { "ecmaVersion": 6, "sourceType": "module" diff --git a/package.json b/package.json index 7845b4c..0eabb8c 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "scripts": { "pretest": "npm run build && npm run build:cli", "test": "mocha", + "test:quick": "rollup -c && mocha", "pretest-coverage": "npm run build", "test-coverage": "rm -rf coverage/* && istanbul cover --report json node_modules/.bin/_mocha -- -u exports -R spec test/test.js", "posttest-coverage": "remap-istanbul -i coverage/coverage-final.json -o coverage/coverage-remapped.json -b dist && remap-istanbul -i coverage/coverage-final.json -o coverage/coverage-remapped.lcov -t lcovonly -b dist && remap-istanbul -i coverage/coverage-final.json -o coverage/coverage-remapped -t html -b dist", @@ -48,8 +49,9 @@ "buble": "^0.12.5", "chalk": "^1.1.3", "codecov.io": "^0.1.6", - "console-group": "^0.2.1", + "console-group": "^0.3.1", "eslint": "^2.13.0", + "eslint-plugin-import": "^1.14.0", "estree-walker": "^0.2.1", "istanbul": "^0.4.3", "magic-string": "^0.15.2", diff --git a/rollup.config.js b/rollup.config.js index 7847ff7..a8ac2c8 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -22,7 +22,10 @@ export default { entry: 'src/rollup.js', plugins: [ buble({ - include: [ 'src/**', 'node_modules/acorn/**' ] + include: [ 'src/**', 'node_modules/acorn/**' ], + target: { + node: 4 + } }), nodeResolve({ diff --git a/src/Bundle.js b/src/Bundle.js index 36d1675..9e5f010 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -17,6 +17,7 @@ import collapseSourcemaps from './utils/collapseSourcemaps.js'; import SOURCEMAPPING_URL from './utils/sourceMappingURL.js'; import callIfFunction from './utils/callIfFunction.js'; import { dirname, isRelative, isAbsolute, normalize, relative, resolve } from './utils/path.js'; +import BundleScope from './ast/scopes/BundleScope.js'; export default class Bundle { constructor ( options ) { @@ -59,14 +60,17 @@ export default class Bundle { ( id => options.paths.hasOwnProperty( id ) ? options.paths[ id ] : this.getPathRelativeToEntryDirname( id ) ) : id => this.getPathRelativeToEntryDirname( id ); + this.scope = new BundleScope(); + // TODO strictly speaking, this only applies with non-ES6, non-default-only bundles + [ 'module', 'exports', '_interopDefault' ].forEach( name => { + this.scope.findDeclaration( name ); // creates global declaration as side-effect + }); + this.moduleById = new Map(); this.modules = []; - this.externalModules = []; - this.internalNamespaces = []; this.context = String( options.context ); - this.assumedGlobals = blank(); if ( typeof options.external === 'function' ) { this.isExternal = options.external; @@ -77,11 +81,10 @@ export default class Bundle { this.onwarn = options.onwarn || makeOnwarn(); - // TODO strictly speaking, this only applies with non-ES6, non-default-only bundles - [ 'module', 'exports', '_interopDefault' ].forEach( global => this.assumedGlobals[ global ] = true ); - this.varOrConst = options.preferConst ? 'const' : 'var'; this.acornOptions = options.acorn || {}; + + this.dependentExpressions = []; } build () { @@ -100,7 +103,6 @@ export default class Bundle { // Phase 2 – binding. We link references to their declarations // to generate a complete picture of the bundle this.modules.forEach( module => module.bindImportSpecifiers() ); - this.modules.forEach( module => module.bindAliases() ); this.modules.forEach( module => module.bindReferences() ); // Phase 3 – marking. We 'run' each statement to see which ones @@ -109,21 +111,47 @@ export default class Bundle { // mark all export statements entryModule.getExports().forEach( name => { const declaration = entryModule.traceExport( name ); + declaration.exportName = name; + declaration.activate(); - declaration.use(); + if ( declaration.isNamespace ) { + declaration.needsNamespaceBlock = true; + } }); // mark statements that should appear in the bundle - let settled = false; - while ( !settled ) { - settled = true; - + if ( this.treeshake ) { this.modules.forEach( module => { - if ( module.run( this.treeshake ) ) settled = false; + module.run(); }); + + let settled = false; + while ( !settled ) { + settled = true; + + for ( const expression of this.dependentExpressions ) { + if ( expression.isUsedByBundle() ) { + const statement = expression.findParent( /ExpressionStatement/ ); + + if ( statement && !statement.ran ) { + settled = false; + statement.run( statement.findScope() ); + } + } + } + } } + // let settled = false; + // while ( !settled ) { + // settled = true; + // + // this.modules.forEach( module => { + // if ( module.run( this.treeshake ) ) settled = false; + // }); + // } + // Phase 4 – final preparation. We order the modules with an // enhanced topological sort that accounts for cycles, then // ensure that names are deconflicted throughout the bundle @@ -136,7 +164,7 @@ export default class Bundle { const used = blank(); // ensure no conflicts with globals - keys( this.assumedGlobals ).forEach( name => used[ name ] = 1 ); + keys( this.scope.declarations ).forEach( name => used[ name ] = 1 ); function getSafeName ( name ) { while ( used[ name ] ) { @@ -147,27 +175,33 @@ export default class Bundle { return name; } + const toDeshadow = new Map(); + this.externalModules.forEach( module => { - module.name = getSafeName( module.name ); + const safeName = getSafeName( module.name ); + toDeshadow.set( safeName, true ); + module.name = safeName; // ensure we don't shadow named external imports, if // we're creating an ES6 bundle forOwn( module.declarations, ( declaration, name ) => { - declaration.setSafeName( getSafeName( name ) ); + const safeName = getSafeName( name ); + toDeshadow.set( safeName, true ); + declaration.setSafeName( safeName ); }); }); this.modules.forEach( module => { - forOwn( module.declarations, ( declaration, originalName ) => { - if ( declaration.isGlobal ) return; - - if ( originalName === 'default' ) { - if ( declaration.original && !declaration.original.isReassigned ) return; + forOwn( module.scope.declarations, ( declaration ) => { + if ( declaration.isDefault && declaration.declaration.id ) { + return; } declaration.name = getSafeName( declaration.name ); }); }); + + this.scope.deshadow( toDeshadow ); } fetchModule ( id, importer ) { @@ -306,6 +340,7 @@ export default class Bundle { this.orderedModules.forEach( module => { const source = module.render( format === 'es' ); + if ( source.toString().length ) { magicString.addSource( source ); usedModules.push( module ); diff --git a/src/Declaration.js b/src/Declaration.js index c383081..8d0934e 100644 --- a/src/Declaration.js +++ b/src/Declaration.js @@ -1,35 +1,24 @@ import { blank, forOwn, keys } from './utils/object.js'; import makeLegalIdentifier from './utils/makeLegalIdentifier.js'; -import run from './utils/run.js'; -import { SyntheticReference } from './Reference.js'; - -const use = alias => alias.use(); +import { UNKNOWN } from './ast/values.js'; export default class Declaration { - constructor ( node, isParam, statement ) { - if ( node ) { - if ( node.type === 'FunctionDeclaration' ) { - this.isFunctionDeclaration = true; - this.functionNode = node; - } else if ( node.type === 'VariableDeclarator' && node.init && /FunctionExpression/.test( node.init.type ) ) { - this.isFunctionDeclaration = true; - this.functionNode = node.init; - } - } + constructor ( node, isParam ) { + this.node = node; - this.statement = statement; this.name = node.id ? node.id.name : node.name; this.exportName = null; this.isParam = isParam; this.isReassigned = false; - this.aliases = []; - - this.isUsed = false; } - addAlias ( declaration ) { - this.aliases.push( declaration ); + activate () { + if ( this.activated ) return; + this.activated = true; + + if ( this.isParam ) return; + this.node.activate(); } addReference ( reference ) { @@ -48,142 +37,15 @@ export default class Declaration { return `exports.${this.exportName}`; } - - run ( strongDependencies ) { - if ( this.tested ) return this.hasSideEffects; - - - if ( !this.functionNode ) { - this.hasSideEffects = true; // err on the side of caution. TODO handle unambiguous `var x; x = y => z` cases - } else { - if ( this.running ) return true; // short-circuit infinite loop - this.running = true; - - this.hasSideEffects = run( this.functionNode.body, this.functionNode._scope, this.statement, strongDependencies, false ); - - this.running = false; - } - - this.tested = true; - return this.hasSideEffects; - } - - use () { - if ( this.isUsed ) return; - - this.isUsed = true; - if ( this.statement ) this.statement.mark(); - - this.aliases.forEach( use ); - } -} - -export class SyntheticDefaultDeclaration { - constructor ( node, statement, name ) { - this.node = node; - this.statement = statement; - this.name = name; - - this.original = null; - this.exportName = null; - this.aliases = []; - } - - addAlias ( declaration ) { - this.aliases.push( declaration ); - } - - addReference ( reference ) { - // Bind the reference to `this` declaration. - reference.declaration = this; - - // Don't change the name to `default`; it's not a valid identifier name. - if ( reference.name === 'default' ) return; - - this.name = reference.name; - } - - bind ( declaration ) { - this.original = declaration; - } - - render () { - return !this.original || this.original.isReassigned ? - this.name : - this.original.render(); - } - - run ( strongDependencies ) { - if ( this.original ) { - return this.original.run( strongDependencies ); - } - - let declaration = this.node.declaration; - while ( declaration.type === 'ParenthesizedExpression' ) declaration = declaration.expression; - - if ( /FunctionExpression/.test( declaration.type ) ) { - return run( declaration.body, this.statement.scope, this.statement, strongDependencies, false ); - } - - // otherwise assume the worst - return true; - } - - use () { - this.isUsed = true; - this.statement.mark(); - - if ( this.original ) this.original.use(); - - this.aliases.forEach( use ); - } -} - -export class SyntheticGlobalDeclaration { - constructor ( name ) { - this.name = name; - this.isExternal = true; - this.isGlobal = true; - this.isReassigned = false; - - this.aliases = []; - - this.isUsed = false; - } - - addAlias ( declaration ) { - this.aliases.push( declaration ); - } - - addReference ( reference ) { - reference.declaration = this; - if ( reference.isReassignment ) this.isReassigned = true; - } - - render () { - return this.name; - } - - run () { - return true; - } - - use () { - if ( this.isUsed ) return; - this.isUsed = true; - - this.aliases.forEach( use ); - } } export class SyntheticNamespaceDeclaration { constructor ( module ) { this.isNamespace = true; this.module = module; - this.name = null; + this.name = module.basename(); this.needsNamespaceBlock = false; - this.aliases = []; this.originals = blank(); module.getExports().forEach( name => { @@ -191,70 +53,40 @@ export class SyntheticNamespaceDeclaration { }); } - addAlias ( declaration ) { - this.aliases.push( declaration ); - } + activate () { + this.needsNamespaceBlock = true; - addReference ( reference ) { - // if we have e.g. `foo.bar`, we can optimise - // the reference by pointing directly to `bar` - if ( reference.parts.length ) { - const ref = reference.parts.shift(); - reference.name = ref.name; - reference.end = ref.end; - - const original = this.originals[ reference.name ]; - - // throw with an informative error message if the reference doesn't exist. - if ( !original ) { - this.module.bundle.onwarn( `Export '${reference.name}' is not defined by '${this.module.id}'` ); - reference.isUndefined = true; - return; - } + // add synthetic references, in case of chained + // namespace imports + forOwn( this.originals, original => { + original.activate(); + }); + } - original.addReference( reference ); - return; - } + addReference ( node ) { + this.name = node.name; + } - // otherwise we're accessing the namespace directly, - // which means we need to mark all of this module's - // exports and render a namespace block in the bundle - if ( !this.needsNamespaceBlock ) { - this.needsNamespaceBlock = true; - this.module.bundle.internalNamespaces.push( this ); - - // add synthetic references, in case of chained - // namespace imports - forOwn( this.originals, ( original, name ) => { - original.addReference( new SyntheticReference( name ) ); - }); - } + gatherPossibleValues ( values ) { + values.add( UNKNOWN ); + } - reference.declaration = this; - this.name = reference.name; + getName () { + return this.name; } - renderBlock ( indentString ) { + renderBlock ( es, indentString ) { const members = keys( this.originals ).map( name => { const original = this.originals[ name ]; if ( original.isReassigned ) { - return `${indentString}get ${name} () { return ${original.render()}; }`; + return `${indentString}get ${name} () { return ${original.getName( es )}; }`; } - return `${indentString}${name}: ${original.render()}`; + return `${indentString}${name}: ${original.getName( es )}`; }); - return `${this.module.bundle.varOrConst} ${this.render()} = Object.freeze({\n${members.join( ',\n' )}\n});\n\n`; - } - - render () { - return this.name; - } - - use () { - forOwn( this.originals, use ); - this.aliases.forEach( use ); + return `${this.module.bundle.varOrConst} ${this.getName( es )} = Object.freeze({\n${members.join( ',\n' )}\n});\n\n`; } } @@ -265,10 +97,12 @@ export class ExternalDeclaration { this.safeName = null; this.isExternal = true; + this.activated = true; + this.isNamespace = name === '*'; } - addAlias () { + activate () { // noop } @@ -280,7 +114,7 @@ export class ExternalDeclaration { } } - render ( es ) { + getName ( es ) { if ( this.name === '*' ) { return this.module.name; } @@ -294,15 +128,7 @@ export class ExternalDeclaration { return es ? this.safeName : `${this.module.name}.${this.name}`; } - run () { - return true; - } - setSafeName ( name ) { this.safeName = name; } - - use () { - // noop? - } } diff --git a/src/Module.js b/src/Module.js index d015182..d6a418f 100644 --- a/src/Module.js +++ b/src/Module.js @@ -1,20 +1,30 @@ import { parse } from 'acorn/src/index.js'; import MagicString from 'magic-string'; -import { walk } from 'estree-walker'; -import Statement from './Statement.js'; import { assign, blank, keys } from './utils/object.js'; import { basename, extname } from './utils/path.js'; import getLocation from './utils/getLocation.js'; import makeLegalIdentifier from './utils/makeLegalIdentifier.js'; import SOURCEMAPPING_URL from './utils/sourceMappingURL.js'; -import { - SyntheticDefaultDeclaration, - SyntheticGlobalDeclaration, - SyntheticNamespaceDeclaration -} from './Declaration.js'; -import { isFalsy, isTruthy } from './ast/conditions.js'; -import { emptyBlockStatement } from './ast/create.js'; -import extractNames from './ast/extractNames.js'; +import { SyntheticNamespaceDeclaration } from './Declaration.js'; +import extractNames from './ast/utils/extractNames.js'; +import enhance from './ast/enhance.js'; +import ModuleScope from './ast/scopes/ModuleScope.js'; + +function tryParse ( code, comments, acornOptions, id ) { + try { + return parse( code, assign({ + ecmaVersion: 6, + sourceType: 'module', + onComment: ( block, text, start, end ) => comments.push({ block, text, start, end }), + preserveParens: true + }, acornOptions )); + } catch ( err ) { + err.code = 'PARSE_ERROR'; + err.file = id; // see above - not necessarily true, but true enough + err.message += ` in ${id}`; + throw err; + } +} export default class Module { constructor ({ id, code, originalCode, originalSourceMap, ast, sourceMapChain, resolvedIds, bundle }) { @@ -23,6 +33,9 @@ export default class Module { this.originalSourceMap = originalSourceMap; this.sourceMapChain = sourceMapChain; + this.comments = []; + this.ast = ast || tryParse( code, this.comments, bundle.acornOptions, id ); // TODO what happens to comments if AST is provided? + this.bundle = bundle; this.id = id; this.excludeFromSourcemap = /\0/.test( id ); @@ -55,18 +68,15 @@ export default class Module { this.magicString.remove( match.index, match.index + match[0].length ); } - this.comments = []; - this.ast = ast; - this.statements = this.parse(); - this.declarations = blank(); + this.type === 'Module'; // TODO only necessary so that Scope knows this should be treated as a function scope... messy + this.scope = new ModuleScope( this ); this.analyse(); this.strongDependencies = []; } - addExport ( statement ) { - const node = statement.node; + addExport ( node ) { const source = node.source && node.source.value; // export { name } from './other.js' @@ -114,7 +124,7 @@ export default class Module { }; // create a synthetic declaration - this.declarations.default = new SyntheticDefaultDeclaration( node, statement, identifier || this.basename() ); + //this.declarations.default = new SyntheticDefaultDeclaration( node, identifier || this.basename() ); } // export var { foo, bar } = ... @@ -156,8 +166,7 @@ export default class Module { } } - addImport ( statement ) { - const node = statement.node; + addImport ( node ) { const source = node.source.value; if ( !~this.sources.indexOf( source ) ) this.sources.push( source ); @@ -181,17 +190,21 @@ export default class Module { } analyse () { + enhance( this.ast, this, this.comments ); + // discover this module's imports and exports - this.statements.forEach( statement => { - if ( statement.isImportDeclaration ) this.addImport( statement ); - else if ( statement.isExportDeclaration ) this.addExport( statement ); + let lastNode; - statement.firstPass(); + for ( const node of this.ast.body ) { + if ( node.isImportDeclaration ) { + this.addImport( node ); + } else if ( node.isExportDeclaration ) { + this.addExport( node ); + } - statement.scope.eachDeclaration( ( name, declaration ) => { - this.declarations[ name ] = declaration; - }); - }); + if ( lastNode ) lastNode.next = node.leadingCommentStart || node.start; + lastNode = node; + } } basename () { @@ -201,27 +214,6 @@ export default class Module { return makeLegalIdentifier( ext ? base.slice( 0, -ext.length ) : base ); } - bindAliases () { - keys( this.declarations ).forEach( name => { - if ( name === '*' ) return; - - const declaration = this.declarations[ name ]; - const statement = declaration.statement; - - if ( !statement || statement.node.type !== 'VariableDeclaration' ) return; - - const init = statement.node.declarations[0].init; - if ( !init || init.type === 'FunctionExpression' ) return; - - statement.references.forEach( reference => { - if ( reference.name === name ) return; - - const otherDeclaration = this.trace( reference.name ); - if ( otherDeclaration ) otherDeclaration.addAlias( declaration ); - }); - }); - } - bindImportSpecifiers () { [ this.imports, this.reexports ].forEach( specifiers => { keys( specifiers ).forEach( name => { @@ -246,32 +238,25 @@ export default class Module { } bindReferences () { - if ( this.declarations.default ) { - if ( this.exports.default.identifier ) { - const declaration = this.trace( this.exports.default.identifier ); - if ( declaration ) this.declarations.default.bind( declaration ); - } + for ( const node of this.ast.body ) { + node.bind( this.scope ); } - this.statements.forEach( statement => { - // skip `export { foo, bar, baz }`... - if ( statement.node.type === 'ExportNamedDeclaration' && statement.node.specifiers.length ) { - // ...unless this is the entry module - if ( this !== this.bundle.entryModule ) return; - } + // if ( this.declarations.default ) { + // if ( this.exports.default.identifier ) { + // const declaration = this.trace( this.exports.default.identifier ); + // if ( declaration ) this.declarations.default.bind( declaration ); + // } + // } + } - statement.references.forEach( reference => { - const declaration = reference.scope.findDeclaration( reference.name ) || - this.trace( reference.name ); + findParent () { + // TODO what does it mean if we're here? + return null; + } - if ( declaration ) { - declaration.addReference( reference ); - } else { - // TODO handle globals - this.bundle.assumedGlobals[ reference.name ] = true; - } - }); - }); + findScope () { + return this.scope; } getExports () { @@ -286,6 +271,8 @@ export default class Module { }); this.exportAllModules.forEach( module => { + if ( module.isExternal ) return; // TODO + module.getExports().forEach( name => { if ( name !== 'default' ) exports[ name ] = true; }); @@ -302,359 +289,26 @@ export default class Module { return this.declarations['*']; } - parse () { - // The ast can be supplied programmatically (but usually won't be) - if ( !this.ast ) { - // Try to extract a list of top-level statements/declarations. If - // the parse fails, attach file info and abort - try { - this.ast = parse( this.code, assign({ - ecmaVersion: 6, - sourceType: 'module', - onComment: ( block, text, start, end ) => this.comments.push({ block, text, start, end }), - preserveParens: true - }, this.bundle.acornOptions )); - } catch ( err ) { - err.code = 'PARSE_ERROR'; - err.file = this.id; // see above - not necessarily true, but true enough - err.message += ` in ${this.id}`; - throw err; - } - } - - walk( this.ast, { - enter: node => { - // eliminate dead branches early - if ( node.type === 'IfStatement' ) { - if ( isFalsy( node.test ) ) { - this.magicString.overwrite( node.consequent.start, node.consequent.end, '{}' ); - node.consequent = emptyBlockStatement( node.consequent.start, node.consequent.end ); - } else if ( node.alternate && isTruthy( node.test ) ) { - this.magicString.overwrite( node.alternate.start, node.alternate.end, '{}' ); - node.alternate = emptyBlockStatement( node.alternate.start, node.alternate.end ); - } - } - - this.magicString.addSourcemapLocation( node.start ); - this.magicString.addSourcemapLocation( node.end ); - }, - - leave: ( node, parent, prop ) => { - // eliminate dead branches early - if ( node.type === 'ConditionalExpression' ) { - if ( isFalsy( node.test ) ) { - this.magicString.remove( node.start, node.alternate.start ); - parent[prop] = node.alternate; - } else if ( isTruthy( node.test ) ) { - this.magicString.remove( node.start, node.consequent.start ); - this.magicString.remove( node.consequent.end, node.end ); - parent[prop] = node.consequent; - } - } - } - }); - - const statements = []; - let lastChar = 0; - let commentIndex = 0; - - this.ast.body.forEach( node => { - if ( node.type === 'EmptyStatement' ) return; - - if ( - node.type === 'ExportNamedDeclaration' && - node.declaration && - node.declaration.type === 'VariableDeclaration' && - node.declaration.declarations && - node.declaration.declarations.length > 1 - ) { - // push a synthetic export declaration - const syntheticNode = { - type: 'ExportNamedDeclaration', - specifiers: node.declaration.declarations.map( declarator => { - const id = { name: declarator.id.name }; - return { - local: id, - exported: id - }; - }), - isSynthetic: true - }; - - const statement = new Statement( syntheticNode, this, node.start, node.start ); - statements.push( statement ); - - this.magicString.remove( node.start, node.declaration.start ); - node = node.declaration; - } - - // special case - top-level var declarations with multiple declarators - // should be split up. Otherwise, we may end up including code we - // don't need, just because an unwanted declarator is included - if ( node.type === 'VariableDeclaration' && node.declarations.length > 1 ) { - // remove the leading var/let/const... UNLESS the previous node - // was also a synthetic node, in which case it'll get removed anyway - const lastStatement = statements[ statements.length - 1 ]; - if ( !lastStatement || !lastStatement.node.isSynthetic ) { - this.magicString.remove( node.start, node.declarations[0].start ); - } - - node.declarations.forEach( declarator => { - const { start, end } = declarator; - - const syntheticNode = { - type: 'VariableDeclaration', - kind: node.kind, - start, - end, - declarations: [ declarator ], - isSynthetic: true - }; - - const statement = new Statement( syntheticNode, this, start, end ); - statements.push( statement ); - }); - - lastChar = node.end; // TODO account for trailing line comment - } - - else { - let comment; - do { - comment = this.comments[ commentIndex ]; - if ( !comment ) break; - if ( comment.start > node.start ) break; - commentIndex += 1; - } while ( comment.end < lastChar ); - - const start = comment ? Math.min( comment.start, node.start ) : node.start; - const end = node.end; // TODO account for trailing line comment - - const statement = new Statement( node, this, start, end ); - statements.push( statement ); - - lastChar = end; - } - }); - - let i = statements.length; - let next = this.code.length; - while ( i-- ) { - statements[i].next = next; - if ( !statements[i].isSynthetic ) next = statements[i].start; - } - - return statements; - } - render ( es ) { const magicString = this.magicString.clone(); - this.statements.forEach( statement => { - if ( !statement.isIncluded ) { - if ( statement.node.type === 'ImportDeclaration' ) { - magicString.remove( statement.node.start, statement.next ); - return; - } - - magicString.remove( statement.start, statement.next ); - return; - } - - statement.stringLiteralRanges.forEach( range => magicString.indentExclusionRanges.push( range ) ); - - // skip `export { foo, bar, baz }` - if ( statement.node.type === 'ExportNamedDeclaration' ) { - if ( statement.node.isSynthetic ) return; - - // skip `export { foo, bar, baz }` - if ( statement.node.declaration === null ) { - magicString.remove( statement.start, statement.next ); - return; - } - } - - // split up/remove var declarations as necessary - if ( statement.node.type === 'VariableDeclaration' ) { - const declarator = statement.node.declarations[0]; - - if ( declarator.id.type === 'Identifier' ) { - const declaration = this.declarations[ declarator.id.name ]; - - if ( declaration.exportName && declaration.isReassigned ) { // `var foo = ...` becomes `exports.foo = ...` - magicString.remove( statement.start, declarator.init ? declarator.start : statement.next ); - if ( !declarator.init ) return; - } - } - - else { - // we handle destructuring differently, because whereas we can rewrite - // `var foo = ...` as `exports.foo = ...`, in a case like `var { a, b } = c()` - // where `a` or `b` is exported and reassigned, we have to append - // `exports.a = a;` and `exports.b = b` instead - extractNames( declarator.id ).forEach( name => { - const declaration = this.declarations[ name ]; - - if ( declaration.exportName && declaration.isReassigned ) { - magicString.insertLeft( statement.end, `;\nexports.${name} = ${declaration.render( es )}` ); - } - }); - } - - if ( statement.node.isSynthetic ) { - // insert `var/let/const` if necessary - magicString.insertRight( statement.start, `${statement.node.kind} ` ); - magicString.insertLeft( statement.end, ';' ); - magicString.overwrite( statement.end, statement.next, '\n' ); // TODO account for trailing newlines - } - } - - const toDeshadow = blank(); - - statement.references.forEach( reference => { - const { start, end } = reference; - - if ( reference.isUndefined ) { - magicString.overwrite( start, end, 'undefined', true ); - } - - const declaration = reference.declaration; - - if ( declaration ) { - const name = declaration.render( es ); - - // the second part of this check is necessary because of - // namespace optimisation – name of `foo.bar` could be `bar` - if ( reference.name === name && name.length === end - start ) return; - - reference.rewritten = true; - - // prevent local variables from shadowing renamed references - const identifier = name.match( /[^\.]+/ )[0]; - if ( reference.scope.contains( identifier ) ) { - toDeshadow[ identifier ] = `${identifier}$$`; // TODO more robust mechanism - } - - if ( reference.isShorthandProperty ) { - magicString.insertLeft( end, `: ${name}` ); - } else { - magicString.overwrite( start, end, name, true ); - } - } - }); - - if ( keys( toDeshadow ).length ) { - statement.references.forEach( reference => { - if ( !reference.rewritten && reference.name in toDeshadow ) { - const replacement = toDeshadow[ reference.name ]; - magicString.overwrite( reference.start, reference.end, reference.isShorthandProperty ? `${reference.name}: ${replacement}` : replacement, true ); - } - }); - } - - // modify exports as necessary - if ( statement.isExportDeclaration ) { - // remove `export` from `export var foo = 42` - // TODO: can we do something simpler here? - // we just want to remove `export`, right? - if ( statement.node.type === 'ExportNamedDeclaration' && statement.node.declaration.type === 'VariableDeclaration' ) { - const name = extractNames( statement.node.declaration.declarations[ 0 ].id )[ 0 ]; - const declaration = this.declarations[ name ]; - - // TODO is this even possible? - if ( !declaration ) throw new Error( `Missing declaration for ${name}!` ); - - let end; - - if ( es ) { - end = statement.node.declaration.start; - } else { - if ( declaration.exportName && declaration.isReassigned ) { - const declarator = statement.node.declaration.declarations[0]; - end = declarator.init ? declarator.start : statement.next; - } else { - end = statement.node.declaration.start; - } - } - - magicString.remove( statement.node.start, end ); - } - - else if ( statement.node.type === 'ExportAllDeclaration' ) { - // TODO: remove once `export * from 'external'` is supported. - magicString.remove( statement.start, statement.next ); - } - - // remove `export` from `export class Foo {...}` or `export default Foo` - // TODO default exports need different treatment - else if ( statement.node.declaration.id ) { - magicString.remove( statement.node.start, statement.node.declaration.start ); - } - - else if ( statement.node.type === 'ExportDefaultDeclaration' ) { - const defaultDeclaration = this.declarations.default; - - // prevent `var foo = foo` - if ( defaultDeclaration.original && !defaultDeclaration.original.isReassigned ) { - magicString.remove( statement.start, statement.next ); - return; - } - - const defaultName = defaultDeclaration.render(); - - // prevent `var undefined = sideEffectyDefault(foo)` - if ( !defaultDeclaration.exportName && !defaultDeclaration.isUsed ) { - magicString.remove( statement.start, statement.node.declaration.start ); - return; - } - - // anonymous functions should be converted into declarations - if ( statement.node.declaration.type === 'FunctionExpression' ) { - magicString.overwrite( statement.node.start, statement.node.declaration.start + 8, `function ${defaultName}` ); - } else { - magicString.overwrite( statement.node.start, statement.node.declaration.start, `${this.bundle.varOrConst} ${defaultName} = ` ); - } - } - - else { - throw new Error( 'Unhandled export' ); - } - } - }); + for ( const node of this.ast.body ) { + node.render( magicString, es ); + } - // add namespace block if necessary - const namespace = this.declarations['*']; - if ( namespace && namespace.needsNamespaceBlock ) { - magicString.append( '\n\n' + namespace.renderBlock( magicString.getIndentString() ) ); + if ( this.namespace().needsNamespaceBlock ) { + magicString.append( '\n\n' + this.namespace().renderBlock( es, '\t' ) ); // TODO use correct indentation } return magicString.trim(); } - /** - * Statically runs the module marking the top-level statements that must be - * included for the module to execute successfully. - * - * @param {boolean} treeshake - if we should tree-shake the module - * @return {boolean} marked - if any new statements were marked for inclusion - */ - run ( treeshake ) { - if ( !treeshake ) { - this.statements.forEach( statement => { - if ( statement.isImportDeclaration || ( statement.isExportDeclaration && statement.node.isSynthetic ) ) return; - - statement.mark(); - }); - return false; + run () { + for ( const node of this.ast.body ) { + if ( node.hasEffects( this.scope ) ) { + node.run( this.scope ); + } } - - let marked = false; - - this.statements.forEach( statement => { - marked = statement.run( this.strongDependencies ) || marked; - }); - - return marked; } toJSON () { @@ -662,14 +316,19 @@ export default class Module { id: this.id, code: this.code, originalCode: this.originalCode, - ast: this.ast, + // TODO reinstate AST caching (rewrite broke it, because AST is enhanced) + // ast: this.ast, sourceMapChain: this.sourceMapChain, resolvedIds: this.resolvedIds }; } trace ( name ) { - if ( name in this.declarations ) return this.declarations[ name ]; + // TODO this is slightly circular + if ( name in this.scope.declarations ) { + return this.scope.declarations[ name ]; + } + if ( name in this.imports ) { const importDeclaration = this.imports[ name ]; const otherModule = importDeclaration.module; @@ -708,10 +367,7 @@ export default class Module { const name = exportDeclaration.localName; const declaration = this.trace( name ); - if ( declaration ) return declaration; - - this.bundle.assumedGlobals[ name ] = true; - return ( this.declarations[ name ] = new SyntheticGlobalDeclaration( name ) ); + return declaration || this.bundle.scope.findDeclaration( name ); } for ( let i = 0; i < this.exportAllModules.length; i += 1 ) { diff --git a/src/Reference.js b/src/Reference.js deleted file mode 100644 index 5c5c3e9..0000000 --- a/src/Reference.js +++ /dev/null @@ -1,30 +0,0 @@ -export class Reference { - constructor ( node, scope, statement ) { - this.node = node; - this.scope = scope; - this.statement = statement; - - this.declaration = null; // bound later - - this.parts = []; - - let root = node; - while ( root.type === 'MemberExpression' ) { - this.parts.unshift( root.property ); - root = root.object; - } - - this.name = root.name; - - this.start = node.start; - this.end = node.start + this.name.length; // can be overridden in the case of namespace members - this.rewritten = false; - } -} - -export class SyntheticReference { - constructor ( name ) { - this.name = name; - this.parts = []; - } -} diff --git a/src/Statement.js b/src/Statement.js deleted file mode 100644 index 7ab8bf8..0000000 --- a/src/Statement.js +++ /dev/null @@ -1,160 +0,0 @@ -import { walk } from 'estree-walker'; -import Scope from './ast/Scope.js'; -import attachScopes from './ast/attachScopes.js'; -import modifierNodes, { isModifierNode } from './ast/modifierNodes.js'; -import isFunctionDeclaration from './ast/isFunctionDeclaration.js'; -import isReference from './ast/isReference.js'; -import getLocation from './utils/getLocation.js'; -import run from './utils/run.js'; -import { Reference } from './Reference.js'; - -export default class Statement { - constructor ( node, module, start, end ) { - this.node = node; - this.module = module; - this.start = start; - this.end = end; - this.next = null; // filled in later - - this.scope = new Scope({ statement: this }); - - this.references = []; - this.stringLiteralRanges = []; - - this.isIncluded = false; - this.ran = false; - - this.isImportDeclaration = node.type === 'ImportDeclaration'; - this.isExportDeclaration = /^Export/.test( node.type ); - this.isReexportDeclaration = this.isExportDeclaration && !!node.source; - - this.isFunctionDeclaration = isFunctionDeclaration( node ) || - this.isExportDeclaration && isFunctionDeclaration( node.declaration ); - } - - firstPass () { - if ( this.isImportDeclaration ) return; // nothing to analyse - - // attach scopes - attachScopes( this ); - - // find references - const statement = this; - let { module, references, scope, stringLiteralRanges } = this; - let contextDepth = 0; - - walk( this.node, { - enter ( node, parent, prop ) { - // warn about eval - if ( node.type === 'CallExpression' && node.callee.name === 'eval' && !scope.contains( 'eval' ) ) { - // TODO show location - module.bundle.onwarn( `Use of \`eval\` (in ${module.id}) is strongly discouraged, as it poses security risks and may cause issues with minification. See https://github.com/rollup/rollup/wiki/Troubleshooting#avoiding-eval for more details` ); - } - - // skip re-export declarations - if ( node.type === 'ExportNamedDeclaration' && node.source ) return this.skip(); - - if ( node.type === 'TemplateElement' ) stringLiteralRanges.push([ node.start, node.end ]); - if ( node.type === 'Literal' && typeof node.value === 'string' && /\n/.test( node.raw ) ) { - stringLiteralRanges.push([ node.start + 1, node.end - 1 ]); - } - - if ( node.type === 'ThisExpression' && contextDepth === 0 ) { - module.magicString.overwrite( node.start, node.end, module.bundle.context ); - if ( module.bundle.context === 'undefined' ) module.bundle.onwarn( 'The `this` keyword is equivalent to `undefined` at the top level of an ES module, and has been rewritten' ); - } - - if ( node._scope ) scope = node._scope; - if ( /^Function/.test( node.type ) ) contextDepth += 1; - - let isReassignment; - - if ( parent && isModifierNode( parent ) ) { - let subject = parent[ modifierNodes[ parent.type ] ]; - - if ( node === subject ) { - let depth = 0; - - while ( subject.type === 'MemberExpression' ) { - subject = subject.object; - depth += 1; - } - - const importDeclaration = module.imports[ subject.name ]; - - if ( !scope.contains( subject.name ) && importDeclaration ) { - const minDepth = importDeclaration.name === '*' ? - 2 : // cannot do e.g. `namespace.foo = bar` - 1; // cannot do e.g. `foo = bar`, but `foo.bar = bar` is fine - - if ( depth < minDepth ) { - const err = new Error( `Illegal reassignment to import '${subject.name}'` ); - err.file = module.id; - err.loc = getLocation( module.magicString.original, subject.start ); - throw err; - } - } - - isReassignment = !depth; - } - } - - if ( isReference( node, parent ) ) { - // function declaration IDs are a special case – they're associated - // with the parent scope - const referenceScope = parent.type === 'FunctionDeclaration' && node === parent.id ? - scope.parent : - scope; - - const isShorthandProperty = parent.type === 'Property' && parent.shorthand; - - // Since `node.key` can equal `node.value` for shorthand properties - // we must use the `prop` argument provided by `estree-walker` to determine - // if we're looking at the key or the value. - // If they are equal, we'll return to not create duplicate references. - if ( isShorthandProperty && parent.value === parent.key && prop === 'value' ) { - return; - } - - const reference = new Reference( node, referenceScope, statement ); - reference.isReassignment = isReassignment; - reference.isShorthandProperty = isShorthandProperty; - references.push( reference ); - - this.skip(); // don't descend from `foo.bar.baz` into `foo.bar` - } - }, - leave ( node ) { - if ( node._scope ) scope = scope.parent; - if ( /^Function/.test( node.type ) ) contextDepth -= 1; - } - }); - } - - mark () { - if ( this.isIncluded ) return; // prevent infinite loops - this.isIncluded = true; - - this.references.forEach( reference => { - if ( reference.declaration ) reference.declaration.use(); - }); - } - - run ( strongDependencies ) { - if ( ( this.ran && this.isIncluded ) || this.isImportDeclaration || this.isFunctionDeclaration ) return; - this.ran = true; - - if ( run( this.node, this.scope, this, strongDependencies, false ) ) { - this.mark(); - return true; - } - } - - source () { - return this.module.source.slice( this.start, this.end ); - } - - toString () { - return this.module.magicString.slice( this.start, this.end ); - } -} diff --git a/src/ast/Node.js b/src/ast/Node.js new file mode 100644 index 0000000..0343f28 --- /dev/null +++ b/src/ast/Node.js @@ -0,0 +1,92 @@ +import { UNKNOWN } from './values.js'; +import getLocation from '../utils/getLocation.js'; + +export default class Node { + bind ( scope ) { + this.eachChild( child => child.bind( scope ) ); + } + + eachChild ( callback ) { + for ( const key of this.keys ) { + if ( this.shorthand && key === 'key' ) continue; // key and value are the same + + const value = this[ key ]; + + if ( value ) { + if ( 'length' in value ) { + for ( const child of value ) { + if ( child ) callback( child ); + } + } else if ( value ) { + callback( value ); + } + } + } + } + + findParent ( selector ) { + return selector.test( this.type ) ? this : this.parent.findParent( selector ); + } + + // TODO abolish findScope. if a node needs to store scope, store it + findScope ( functionScope ) { + return this.parent.findScope( functionScope ); + } + + gatherPossibleValues ( values ) { + //this.eachChild( child => child.gatherPossibleValues( values ) ); + values.add( UNKNOWN ); + } + + getValue () { + return UNKNOWN; + } + + hasEffects ( scope ) { + for ( const key of this.keys ) { + const value = this[ key ]; + + if ( value ) { + if ( 'length' in value ) { + for ( const child of value ) { + if ( child && child.hasEffects( scope ) ) { + return true; + } + } + } else if ( value && value.hasEffects( scope ) ) { + return true; + } + } + } + } + + initialise ( scope ) { + this.eachChild( child => child.initialise( scope ) ); + } + + locate () { + // useful for debugging + const location = getLocation( this.module.code, this.start ); + location.file = this.module.id; + location.toString = () => JSON.stringify( location ); + + return location; + } + + render ( code, es ) { + this.eachChild( child => child.render( code, es ) ); + } + + run ( scope ) { + if ( this.ran ) return; + this.ran = true; + + this.eachChild( child => { + child.run( scope ); + }); + } + + toString () { + return this.module.code.slice( this.start, this.end ); + } +} diff --git a/src/ast/Scope.js b/src/ast/Scope.js deleted file mode 100644 index 0dc48ec..0000000 --- a/src/ast/Scope.js +++ /dev/null @@ -1,52 +0,0 @@ -import { blank, keys } from '../utils/object.js'; -import Declaration from '../Declaration.js'; -import extractNames from './extractNames.js'; - -export default class Scope { - constructor ( options ) { - options = options || {}; - - this.parent = options.parent; - this.statement = options.statement || this.parent.statement; - this.isBlockScope = !!options.block; - this.isTopLevel = !this.parent || ( this.parent.isTopLevel && this.isBlockScope ); - - this.declarations = blank(); - - if ( options.params ) { - options.params.forEach( param => { - extractNames( param ).forEach( name => { - this.declarations[ name ] = new Declaration( param, true, this.statement ); - }); - }); - } - } - - addDeclaration ( node, isBlockDeclaration, isVar ) { - if ( !isBlockDeclaration && this.isBlockScope ) { - // it's a `var` or function node, and this - // is a block scope, so we need to go up - this.parent.addDeclaration( node, isBlockDeclaration, isVar ); - } else { - extractNames( node.id ).forEach( name => { - this.declarations[ name ] = new Declaration( node, false, this.statement ); - }); - } - } - - contains ( name ) { - return this.declarations[ name ] || - ( this.parent ? this.parent.contains( name ) : false ); - } - - eachDeclaration ( fn ) { - keys( this.declarations ).forEach( key => { - fn( key, this.declarations[ key ] ); - }); - } - - findDeclaration ( name ) { - return this.declarations[ name ] || - ( this.parent && this.parent.findDeclaration( name ) ); - } -} diff --git a/src/ast/attachScopes.js b/src/ast/attachScopes.js deleted file mode 100644 index 83cbc85..0000000 --- a/src/ast/attachScopes.js +++ /dev/null @@ -1,78 +0,0 @@ -import { walk } from 'estree-walker'; -import Scope from './Scope.js'; - -const blockDeclarations = { - const: true, - let: true -}; - -export default function attachScopes ( statement ) { - let { node, scope } = statement; - - walk( node, { - enter ( node, parent ) { - // function foo () {...} - // class Foo {...} - if ( /(Function|Class)Declaration/.test( node.type ) ) { - scope.addDeclaration( node, false, false ); - } - - // var foo = 1, bar = 2 - if ( node.type === 'VariableDeclaration' ) { - const isBlockDeclaration = blockDeclarations[ node.kind ]; - - node.declarations.forEach( declarator => { - scope.addDeclaration( declarator, isBlockDeclaration, true ); - }); - } - - let newScope; - - // create new function scope - if ( /(Function|Class)/.test( node.type ) ) { - newScope = new Scope({ - parent: scope, - block: false, - params: node.params - }); - - // named function expressions - the name is considered - // part of the function's scope - if ( /(Function|Class)Expression/.test( node.type ) && node.id ) { - newScope.addDeclaration( node, false, false ); - } - } - - // create new block scope - if ( node.type === 'BlockStatement' && ( !parent || !/Function/.test( parent.type ) ) ) { - newScope = new Scope({ - parent: scope, - block: true - }); - } - - // catch clause has its own block scope - if ( node.type === 'CatchClause' ) { - newScope = new Scope({ - parent: scope, - params: [ node.param ], - block: true - }); - } - - if ( newScope ) { - Object.defineProperty( node, '_scope', { - value: newScope, - configurable: true - }); - - scope = newScope; - } - }, - leave ( node ) { - if ( node._scope ) { - scope = scope.parent; - } - } - }); -} diff --git a/src/ast/conditions.js b/src/ast/conditions.js deleted file mode 100644 index b21e4d4..0000000 --- a/src/ast/conditions.js +++ /dev/null @@ -1,38 +0,0 @@ -export function isTruthy ( node ) { - if ( node.type === 'Literal' ) return !!node.value; - if ( node.type === 'ParenthesizedExpression' ) return isTruthy( node.expression ); - if ( node.operator in operators ) return operators[ node.operator ]( node ); -} - -export function isFalsy ( node ) { - return not( isTruthy( node ) ); -} - -function not ( value ) { - return value === undefined ? value : !value; -} - -function equals ( a, b, strict ) { - if ( a.type !== b.type ) return undefined; - if ( a.type === 'Literal' ) return strict ? a.value === b.value : a.value == b.value; -} - -const operators = { - '==': x => { - return equals( x.left, x.right, false ); - }, - - '!=': x => not( operators['==']( x ) ), - - '===': x => { - return equals( x.left, x.right, true ); - }, - - '!==': x => not( operators['===']( x ) ), - - '!': x => isFalsy( x.argument ), - - '&&': x => isTruthy( x.left ) && isTruthy( x.right ), - - '||': x => isTruthy( x.left ) || isTruthy( x.right ) -}; diff --git a/src/ast/create.js b/src/ast/create.js deleted file mode 100644 index e767dbd..0000000 --- a/src/ast/create.js +++ /dev/null @@ -1,7 +0,0 @@ -export function emptyBlockStatement ( start, end ) { - return { - start, end, - type: 'BlockStatement', - body: [] - }; -} diff --git a/src/ast/enhance.js b/src/ast/enhance.js new file mode 100644 index 0000000..6a86dab --- /dev/null +++ b/src/ast/enhance.js @@ -0,0 +1,63 @@ +import nodes from './nodes/index.js'; +import Node from './Node.js'; +import keys from './keys.js'; + +const newline = /\n/; + +export default function enhance ( ast, module, comments ) { + enhanceNode( ast, module, module, module.magicString ); + + let comment = comments.shift(); + + for ( const node of ast.body ) { + if ( comment && ( comment.start < node.start ) ) { + node.leadingCommentStart = comment.start; + } + + while ( comment && comment.end < node.end ) comment = comments.shift(); + + // if the next comment is on the same line as the end of the node, + // treat is as a trailing comment + if ( comment && !newline.test( module.code.slice( node.end, comment.start ) ) ) { + node.trailingCommentEnd = comment.end; // TODO is node.trailingCommentEnd used anywhere? + comment = comments.shift(); + } + + node.initialise( module.scope ); + } +} + +function enhanceNode ( raw, parent, module, code ) { + if ( !raw ) return; + + if ( 'length' in raw ) { + for ( let i = 0; i < raw.length; i += 1 ) { + enhanceNode( raw[i], parent, module, code ); + } + + return; + } + + // with e.g. shorthand properties, key and value are + // the same node. We don't want to enhance an object twice + if ( raw.__enhanced ) return; + raw.__enhanced = true; + + if ( !keys[ raw.type ] ) { + keys[ raw.type ] = Object.keys( raw ).filter( key => typeof raw[ key ] === 'object' ); + } + + raw.parent = parent; + raw.module = module; + raw.keys = keys[ raw.type ]; + + code.addSourcemapLocation( raw.start ); + code.addSourcemapLocation( raw.end ); + + for ( const key of keys[ raw.type ] ) { + enhanceNode( raw[ key ], raw, module, code ); + } + + const type = nodes[ raw.type ] || Node; + raw.__proto__ = type.prototype; +} diff --git a/src/ast/isFunctionDeclaration.js b/src/ast/isFunctionDeclaration.js deleted file mode 100644 index a1573e7..0000000 --- a/src/ast/isFunctionDeclaration.js +++ /dev/null @@ -1,6 +0,0 @@ -export default function isFunctionDeclaration ( node ) { - if ( !node ) return false; - - return node.type === 'FunctionDeclaration' || - ( node.type === 'VariableDeclaration' && node.init && /FunctionExpression/.test( node.init.type ) ); -} diff --git a/src/ast/keys.js b/src/ast/keys.js new file mode 100644 index 0000000..99d14ea --- /dev/null +++ b/src/ast/keys.js @@ -0,0 +1,3 @@ +export default { + Program: [ 'body' ] +}; diff --git a/src/ast/modifierNodes.js b/src/ast/modifierNodes.js deleted file mode 100644 index 3696fd1..0000000 --- a/src/ast/modifierNodes.js +++ /dev/null @@ -1,19 +0,0 @@ -const modifierNodes = { - AssignmentExpression: 'left', - UpdateExpression: 'argument', - UnaryExpression: 'argument' -}; - -export default modifierNodes; - -export function isModifierNode ( node ) { - if ( !( node.type in modifierNodes ) ) { - return false; - } - - if ( node.type === 'UnaryExpression' ) { - return node.operator === 'delete'; - } - - return true; -} diff --git a/src/ast/nodes/ArrayExpression.js b/src/ast/nodes/ArrayExpression.js new file mode 100644 index 0000000..ff60919 --- /dev/null +++ b/src/ast/nodes/ArrayExpression.js @@ -0,0 +1,8 @@ +import Node from '../Node.js'; +import { ARRAY } from '../values.js'; + +export default class ArrayExpression extends Node { + gatherPossibleValues ( values ) { + values.add( ARRAY ); + } +} diff --git a/src/ast/nodes/ArrowFunctionExpression.js b/src/ast/nodes/ArrowFunctionExpression.js new file mode 100644 index 0000000..2071965 --- /dev/null +++ b/src/ast/nodes/ArrowFunctionExpression.js @@ -0,0 +1,35 @@ +import Node from '../Node.js'; +import Scope from '../scopes/Scope.js'; +import extractNames from '../utils/extractNames.js'; + +export default class ArrowFunctionExpression extends Node { + initialise ( scope ) { + if ( this.body.type !== 'BlockStatement' ) { + this.scope = new Scope({ + parent: scope, + isBlockScope: false, + isLexicalBoundary: false + }); + + for ( const param of this.params ) { + for ( const name of extractNames( param ) ) { + this.scope.addDeclaration( name, null, null, true ); // TODO ugh + } + } + } + + super.initialise( scope ); + } + + bind ( scope ) { + super.bind( this.scope || scope ); + } + + findScope ( functionScope ) { + return this.scope || this.parent.findScope( functionScope ); + } + + hasEffects () { + return false; + } +} diff --git a/src/ast/nodes/AssignmentExpression.js b/src/ast/nodes/AssignmentExpression.js new file mode 100644 index 0000000..dd3347d --- /dev/null +++ b/src/ast/nodes/AssignmentExpression.js @@ -0,0 +1,45 @@ +import Node from '../Node.js'; +import disallowIllegalReassignment from './shared/disallowIllegalReassignment.js'; +import isUsedByBundle from './shared/isUsedByBundle.js'; +import { NUMBER, STRING, UNKNOWN } from '../values.js'; + +export default class AssignmentExpression extends Node { + bind ( scope ) { + let subject = this.left; + while ( this.left.type === 'ParenthesizedExpression' ) subject = subject.expression; + + this.subject = subject; + disallowIllegalReassignment( scope, subject ); + + if ( subject.type === 'Identifier' ) { + const declaration = scope.findDeclaration( subject.name ); + declaration.isReassigned = true; + + if ( declaration.possibleValues ) { // TODO this feels hacky + if ( this.operator === '=' ) { + declaration.possibleValues.add( this.right ); + } else if ( this.operator === '+=' ) { + declaration.possibleValues.add( STRING ).add( NUMBER ); + } else { + declaration.possibleValues.add( NUMBER ); + } + } + } + + super.bind( scope ); + } + + hasEffects ( scope ) { + const hasEffects = this.isUsedByBundle() || this.right.hasEffects( scope ); + return hasEffects; + } + + initialise ( scope ) { + this.module.bundle.dependentExpressions.push( this ); + super.initialise( scope ); + } + + isUsedByBundle () { + return isUsedByBundle( this.findScope(), this.subject ); + } +} diff --git a/src/ast/nodes/BinaryExpression.js b/src/ast/nodes/BinaryExpression.js new file mode 100644 index 0000000..da3d4da --- /dev/null +++ b/src/ast/nodes/BinaryExpression.js @@ -0,0 +1,38 @@ +import Node from '../Node.js'; +import { UNKNOWN } from '../values.js'; + +const operators = { + '==': ( left, right ) => left == right, + '!=': ( left, right ) => left != right, + '===': ( left, right ) => left === right, + '!==': ( left, right ) => left !== right, + '<': ( left, right ) => left < right, + '<=': ( left, right ) => left <= right, + '>': ( left, right ) => left > right, + '>=': ( left, right ) => left >= right, + '<<': ( left, right ) => left << right, + '>>': ( left, right ) => left >> right, + '>>>': ( left, right ) => left >>> right, + '+': ( left, right ) => left + right, + '-': ( left, right ) => left - right, + '*': ( left, right ) => left * right, + '/': ( left, right ) => left / right, + '%': ( left, right ) => left % right, + '|': ( left, right ) => left | right, + '^': ( left, right ) => left ^ right, + '&': ( left, right ) => left & right, + in: ( left, right ) => left in right, + instanceof: ( left, right ) => left instanceof right +}; + +export default class BinaryExpression extends Node { + getValue () { + const leftValue = this.left.getValue(); + if ( leftValue === UNKNOWN ) return UNKNOWN; + + const rightValue = this.right.getValue(); + if ( rightValue === UNKNOWN ) return UNKNOWN; + + return operators[ this.operator ]( leftValue, rightValue ); + } +} diff --git a/src/ast/nodes/BlockStatement.js b/src/ast/nodes/BlockStatement.js new file mode 100644 index 0000000..ea341d5 --- /dev/null +++ b/src/ast/nodes/BlockStatement.js @@ -0,0 +1,71 @@ +import Node from '../Node.js'; +import Scope from '../scopes/Scope.js'; +import extractNames from '../utils/extractNames.js'; + +export default class BlockStatement extends Node { + bind () { + for ( const node of this.body ) { + node.bind( this.scope ); + } + } + + createScope ( parent ) { + this.parentIsFunction = /Function/.test( this.parent.type ); + this.isFunctionBlock = this.parentIsFunction || this.parent.type === 'Module'; + + this.scope = new Scope({ + isBlockScope: !this.isFunctionBlock, + isLexicalBoundary: this.isFunctionBlock && this.parent.type !== 'ArrowFunctionExpression', + parent: parent || this.parent.findScope( false ), // TODO always supply parent + owner: this // TODO is this used anywhere? + }); + + const params = this.parent.params || ( this.parent.type === 'CatchClause' && [ this.parent.param ] ); + + if ( params && params.length ) { + params.forEach( node => { + extractNames( node ).forEach( name => { + this.scope.addDeclaration( name, node, false, true ); + }); + }); + } + } + + findScope ( functionScope ) { + return functionScope && !this.isFunctionBlock ? this.parent.findScope( functionScope ) : this.scope; + } + + hasEffects () { + for ( const node of this.body ) { + if ( node.hasEffects( this.scope ) ) return true; + } + } + + initialise () { + if ( !this.scope ) this.createScope(); // scope can be created early in some cases, e.g for (let i... ) + + let lastNode; + for ( const node of this.body ) { + node.initialise( this.scope ); + + if ( lastNode ) lastNode.next = node.start; + lastNode = node; + } + } + + render ( code, es ) { + for ( const node of this.body ) { + node.render( code, es ); + } + } + + run () { + if ( this.ran ) return; + this.ran = true; + + for ( const node of this.body ) { + // TODO only include non-top-level statements if necessary + node.run( this.scope ); + } + } +} diff --git a/src/ast/nodes/CallExpression.js b/src/ast/nodes/CallExpression.js new file mode 100644 index 0000000..8fafc91 --- /dev/null +++ b/src/ast/nodes/CallExpression.js @@ -0,0 +1,40 @@ +import getLocation from '../../utils/getLocation.js'; +import error from '../../utils/error.js'; +import Node from '../Node.js'; +import callHasEffects from './shared/callHasEffects.js'; + +export default class CallExpression extends Node { + bind ( scope ) { + if ( this.callee.type === 'Identifier' ) { + const declaration = scope.findDeclaration( this.callee.name ); + + if ( declaration.isNamespace ) { + error({ + message: `Cannot call a namespace ('${this.callee.name}')`, + file: this.module.id, + pos: this.start, + loc: getLocation( this.module.code, this.start ) + }); + } + + if ( this.callee.name === 'eval' && declaration.isGlobal ) { + this.module.bundle.onwarn( `Use of \`eval\` (in ${this.module.id}) is strongly discouraged, as it poses security risks and may cause issues with minification. See https://github.com/rollup/rollup/wiki/Troubleshooting#avoiding-eval for more details` ); + } + } + + super.bind( scope ); + } + + hasEffects ( scope ) { + return callHasEffects( scope, this.callee ); + } + + initialise ( scope ) { + this.module.bundle.dependentExpressions.push( this ); + super.initialise( scope ); + } + + isUsedByBundle () { + return this.hasEffects( this.findScope() ); + } +} diff --git a/src/ast/nodes/ClassDeclaration.js b/src/ast/nodes/ClassDeclaration.js new file mode 100644 index 0000000..a004b9f --- /dev/null +++ b/src/ast/nodes/ClassDeclaration.js @@ -0,0 +1,40 @@ +import Node from '../Node.js'; + +// TODO is this basically identical to FunctionDeclaration? +export default class ClassDeclaration extends Node { + activate () { + if ( this.activated ) return; + this.activated = true; + + this.body.run(); + } + + addReference ( reference ) { + /* noop? */ + } + + gatherPossibleValues ( values ) { + values.add( this ); + } + + getName () { + return this.id.name; + } + + hasEffects () { + return false; + } + + initialise ( scope ) { + scope.addDeclaration( this.id.name, this, false, false ); + super.initialise( scope ); + } + + render ( code, es ) { + if ( this.activated ) { + super.render( code, es ); + } else { + code.remove( this.leadingCommentStart || this.start, this.next || this.end ); + } + } +} diff --git a/src/ast/nodes/ClassExpression.js b/src/ast/nodes/ClassExpression.js new file mode 100644 index 0000000..63f1399 --- /dev/null +++ b/src/ast/nodes/ClassExpression.js @@ -0,0 +1,26 @@ +import Node from '../Node.js'; +import Scope from '../scopes/Scope.js'; + +export default class ClassExpression extends Node { + bind () { + super.bind( this.scope ); + } + + findScope () { + return this.scope; + } + + initialise () { + this.scope = new Scope({ + isBlockScope: true, + parent: this.parent.findScope( false ) + }); + + if ( this.id ) { + // function expression IDs belong to the child scope... + this.scope.addDeclaration( this.id.name, this, false, true ); + } + + super.initialise( this.scope ); + } +} diff --git a/src/ast/nodes/ConditionalExpression.js b/src/ast/nodes/ConditionalExpression.js new file mode 100644 index 0000000..6a6c803 --- /dev/null +++ b/src/ast/nodes/ConditionalExpression.js @@ -0,0 +1,65 @@ +import Node from '../Node.js'; +import { UNKNOWN } from '../values.js'; + +export default class ConditionalExpression extends Node { + initialise ( scope ) { + if ( this.module.bundle.treeshake ) { + const testValue = this.test.getValue(); + + if ( testValue === UNKNOWN ) { + super.initialise( scope ); + } + + else if ( testValue ) { + this.consequent.initialise( scope ); + this.alternate = null; + } else if ( this.alternate ) { + this.alternate.initialise( scope ); + this.consequent = null; + } + } + + else { + super.initialise( scope ); + } + } + + gatherPossibleValues ( values ) { + const testValue = this.test.getValue(); + + if ( testValue === UNKNOWN ) { + values.add( this.consequent ).add( this.alternate ); + } else { + values.add( testValue ? this.consequent : this.alternate ); + } + } + + getValue () { + const testValue = this.test.getValue(); + if ( testValue === UNKNOWN ) return UNKNOWN; + + return testValue ? this.consequent.getValue() : this.alternate.getValue(); + } + + render ( code, es ) { + if ( !this.module.bundle.treeshake ) { + super.render( code, es ); + } + + else { + const testValue = this.test.getValue(); + + if ( testValue === UNKNOWN ) { + super.render( code, es ); + } + + else if ( testValue ) { + code.remove( this.start, this.consequent.start ); + code.remove( this.consequent.end, this.end ); + } else { + code.remove( this.start, this.alternate.start ); + code.remove( this.alternate.end, this.end ); + } + } + } +} diff --git a/src/ast/nodes/ExportAllDeclaration.js b/src/ast/nodes/ExportAllDeclaration.js new file mode 100644 index 0000000..fe929b6 --- /dev/null +++ b/src/ast/nodes/ExportAllDeclaration.js @@ -0,0 +1,11 @@ +import Node from '../Node.js'; + +export default class ExportAllDeclaration extends Node { + initialise ( scope ) { + this.isExportDeclaration = true; + } + + render ( code, es ) { + code.remove( this.leadingCommentStart || this.start, this.next || this.end ); + } +} diff --git a/src/ast/nodes/ExportDefaultDeclaration.js b/src/ast/nodes/ExportDefaultDeclaration.js new file mode 100644 index 0000000..d65d675 --- /dev/null +++ b/src/ast/nodes/ExportDefaultDeclaration.js @@ -0,0 +1,124 @@ +import Node from '../Node.js'; + +const functionOrClassDeclaration = /^(?:Function|Class)Declaration/; + +class SyntheticDefaultDeclaration { + constructor ( node, name ) { + this.node = node; + this.name = name; + this.isDefault = true; + } + + activate () { + if ( this.activated ) return; + this.activated = true; + + this.node.run(); + } + + addReference ( reference ) { + this.name = reference.name; + if ( this.original ) this.original.addReference( reference ); + } + + render ( es ) { + if ( this.original && !this.original.isReassigned ) { + return this.original.getName( es ); + } + + return this.name; + } +} + +export default class ExportDefaultDeclaration extends Node { + initialise ( scope ) { + this.isExportDeclaration = true; + this.isDefault = true; + + this.name = ( this.declaration.id && this.declaration.id.name ) || this.declaration.name || this.module.basename(); + scope.declarations.default = this; + + this.declaration.initialise( scope ); + } + + activate () { + if ( this.activated ) return; + this.activated = true; + + this.run(); + } + + addReference ( reference ) { + this.name = reference.name; + if ( this.original ) this.original.addReference( reference ); + } + + bind ( scope ) { + const name = ( this.declaration.id && this.declaration.id.name ) || this.declaration.name; + if ( name ) this.original = scope.findDeclaration( name ); + + this.declaration.bind( scope ); + } + + gatherPossibleValues ( values ) { + this.declaration.gatherPossibleValues( values ); + } + + getName ( es ) { + if ( this.original && !this.original.isReassigned ) { + return this.original.getName( es ); + } + + return this.name; + } + + // TODO this is total chaos, tidy it up + render ( code, es ) { + const treeshake = this.module.bundle.treeshake; + const name = this.getName( es ); + + if ( this.shouldInclude ) { + if ( this.activated ) { + if ( functionOrClassDeclaration.test( this.declaration.type ) ) { + if ( this.declaration.id ) { + code.remove( this.start, this.declaration.start ); + } else { + throw new Error( 'TODO anonymous class/function declaration' ); + } + } + + else { + if ( this.original && this.original.getName( es ) === name ) { + // prevent `var foo = foo` + code.remove( this.leadingCommentStart || this.start, this.next || this.end ); + return; // don't render children. TODO this seems like a bit of a hack + } else { + code.overwrite( this.start, this.declaration.start, `${this.module.bundle.varOrConst} ${name} = ` ); + } + } + } else { + // remove `var foo` from `var foo = bar()`, if `foo` is unused + code.remove( this.start, this.declaration.start ); + } + + super.render( code, es ); + } else { + if ( treeshake ) { + if ( functionOrClassDeclaration.test( this.declaration.type ) && !this.declaration.activated ) { + code.remove( this.leadingCommentStart || this.start, this.next || this.end ); + } else { + const hasEffects = this.declaration.hasEffects( this.module.scope ); + code.remove( this.start, hasEffects ? this.declaration.start : this.next || this.end ); + } + } else { + code.overwrite( this.start, this.declaration.start, `${this.module.bundle.varOrConst} ${name} = ` ); + } + // code.remove( this.start, this.next || this.end ); + } + } + + run ( scope ) { + this.shouldInclude = true; + super.run( scope ); + } +} diff --git a/src/ast/nodes/ExportNamedDeclaration.js b/src/ast/nodes/ExportNamedDeclaration.js new file mode 100644 index 0000000..b815dd6 --- /dev/null +++ b/src/ast/nodes/ExportNamedDeclaration.js @@ -0,0 +1,25 @@ +import Node from '../Node.js'; + +export default class ExportNamedDeclaration extends Node { + initialise ( scope ) { + this.isExportDeclaration = true; + if ( this.declaration ) { + this.declaration.initialise( scope ); + } + } + + bind ( scope ) { + if ( this.declaration ) { + this.declaration.bind( scope ); + } + } + + render ( code, es ) { + if ( this.declaration ) { + code.remove( this.start, this.declaration.start ); + this.declaration.render( code, es ); + } else { + code.remove( this.leadingCommentStart || this.start, this.next || this.end ); + } + } +} diff --git a/src/ast/nodes/ExpressionStatement.js b/src/ast/nodes/ExpressionStatement.js new file mode 100644 index 0000000..c5c3255 --- /dev/null +++ b/src/ast/nodes/ExpressionStatement.js @@ -0,0 +1,16 @@ +import Node from '../Node.js'; + +export default class ExpressionStatement extends Node { + render ( code, es ) { + if ( !this.module.bundle.treeshake || this.shouldInclude ) { + super.render( code, es ); + } else { + code.remove( this.leadingCommentStart || this.start, this.next || this.end ); + } + } + + run ( scope ) { + this.shouldInclude = true; + super.run( scope ); + } +} diff --git a/src/ast/nodes/FunctionDeclaration.js b/src/ast/nodes/FunctionDeclaration.js new file mode 100644 index 0000000..50e93bf --- /dev/null +++ b/src/ast/nodes/FunctionDeclaration.js @@ -0,0 +1,53 @@ +import Node from '../Node.js'; + +export default class FunctionDeclaration extends Node { + activate () { + if ( this.activated ) return; + this.activated = true; + + const scope = this.body.scope; + this.params.forEach( param => param.run( scope ) ); // in case of assignment patterns + this.body.run(); + } + + addReference ( reference ) { + /* noop? */ + } + + bind ( scope ) { + this.id.bind( scope ); + this.params.forEach( param => param.bind( this.body.scope ) ); + this.body.bind( scope ); + } + + gatherPossibleValues ( values ) { + values.add( this ); + } + + getName () { + return this.name; + } + + hasEffects () { + return false; + } + + initialise ( scope ) { + this.name = this.id.name; // may be overridden by bundle.deconflict + scope.addDeclaration( this.name, this, false, false ); + + this.body.createScope(); + + this.id.initialise( scope ); + this.params.forEach( param => param.initialise( this.body.scope ) ); + this.body.initialise( scope ); + } + + render ( code, es ) { + if ( !this.module.bundle.treeshake || this.activated ) { + super.render( code, es ); + } else { + code.remove( this.leadingCommentStart || this.start, this.next || this.end ); + } + } +} diff --git a/src/ast/nodes/FunctionExpression.js b/src/ast/nodes/FunctionExpression.js new file mode 100644 index 0000000..56c87ee --- /dev/null +++ b/src/ast/nodes/FunctionExpression.js @@ -0,0 +1,21 @@ +import Node from '../Node.js'; + +export default class FunctionExpression extends Node { + bind () { + if ( this.id ) this.id.bind( this.body.scope ); + this.params.forEach( param => param.bind( this.body.scope ) ); + this.body.bind(); + } + + hasEffects () { + return false; + } + + initialise () { + this.body.createScope(); // TODO we'll also need to do this for For[Of|In]Statement + + if ( this.id ) this.id.initialise( this.body.scope ); + this.params.forEach( param => param.initialise( this.body.scope ) ); + this.body.initialise(); + } +} diff --git a/src/ast/nodes/Identifier.js b/src/ast/nodes/Identifier.js new file mode 100644 index 0000000..836462b --- /dev/null +++ b/src/ast/nodes/Identifier.js @@ -0,0 +1,35 @@ +import Node from '../Node.js'; +import isReference from '../utils/isReference.js'; + +export default class Identifier extends Node { + bind ( scope ) { + if ( isReference( this, this.parent ) ) { + this.declaration = scope.findDeclaration( this.name ); + this.declaration.addReference( this ); // TODO necessary? + } + } + + gatherPossibleValues ( values ) { + if ( isReference( this, this.parent ) ) { + values.add( this ); + } + } + + render ( code, es ) { + if ( this.declaration ) { + const name = this.declaration.getName( es ); + if ( name !== this.name ) { + code.overwrite( this.start, this.end, name, true ); + + // special case + if ( this.parent.type === 'Property' && this.parent.shorthand ) { + code.insertLeft( this.start, `${this.name}: ` ); + } + } + } + } + + run () { + if ( this.declaration ) this.declaration.activate(); + } +} diff --git a/src/ast/nodes/IfStatement.js b/src/ast/nodes/IfStatement.js new file mode 100644 index 0000000..baccbd5 --- /dev/null +++ b/src/ast/nodes/IfStatement.js @@ -0,0 +1,73 @@ +import Node from '../Node.js'; +import { UNKNOWN } from '../values.js'; + +// TODO DRY this out +export default class IfStatement extends Node { + bind ( scope ) { + if ( this.module.bundle.treeshake ) { + if ( this.testValue === UNKNOWN ) { + super.bind( scope ); + } + + else if ( this.testValue ) { + this.consequent.bind( scope ); + this.alternate = null; + } else if ( this.alternate ) { + this.alternate.bind( scope ); + this.consequent = null; + } + } + + else { + super.bind( scope ); + } + } + + initialise ( scope ) { + this.testValue = this.test.getValue(); + + if ( this.module.bundle.treeshake ) { + if ( this.testValue === UNKNOWN ) { + super.initialise( scope ); + } + + else if ( this.testValue ) { + this.consequent.initialise( scope ); + } else if ( this.alternate ) { + this.alternate.initialise( scope ); + } + } + + else { + super.initialise( scope ); + } + } + + render ( code, es ) { + if ( this.module.bundle.treeshake ) { + if ( this.testValue === UNKNOWN ) { + super.render( code, es ); + } + + else { + code.overwrite( this.test.start, this.test.end, JSON.stringify( this.testValue ) ); + + // TODO if no block-scoped declarations, remove enclosing + // curlies and dedent block (if there is a block) + + if ( this.testValue ) { + code.remove( this.start, this.consequent.start ); + code.remove( this.consequent.end, this.end ); + this.consequent.render( code, es ); + } else { + code.remove( this.start, this.alternate ? this.alternate.start : this.next || this.end ); + if ( this.alternate ) this.alternate.render( code, es ); + } + } + } + + else { + super.render( code, es ); + } + } +} diff --git a/src/ast/nodes/ImportDeclaration.js b/src/ast/nodes/ImportDeclaration.js new file mode 100644 index 0000000..0edc68b --- /dev/null +++ b/src/ast/nodes/ImportDeclaration.js @@ -0,0 +1,16 @@ +import Node from '../Node.js'; + +export default class ImportDeclaration extends Node { + bind () { + // noop + // TODO do the inter-module binding setup here? + } + + initialise () { + this.isImportDeclaration = true; + } + + render ( code ) { + code.remove( this.start, this.next || this.end ); + } +} diff --git a/src/ast/nodes/Literal.js b/src/ast/nodes/Literal.js new file mode 100644 index 0000000..3b54f14 --- /dev/null +++ b/src/ast/nodes/Literal.js @@ -0,0 +1,17 @@ +import Node from '../Node.js'; + +export default class Literal extends Node { + getValue () { + return this.value; + } + + gatherPossibleValues ( values ) { + values.add( this ); + } + + render ( code ) { + if ( typeof this.value === 'string' ) { + code.indentExclusionRanges.push([ this.start + 1, this.end - 1 ]); + } + } +} diff --git a/src/ast/nodes/MemberExpression.js b/src/ast/nodes/MemberExpression.js new file mode 100644 index 0000000..ea373cd --- /dev/null +++ b/src/ast/nodes/MemberExpression.js @@ -0,0 +1,74 @@ +import isReference from '../utils/isReference.js'; +import Node from '../Node.js'; +import { UNKNOWN } from '../values.js'; + +class Keypath { + constructor ( node ) { + this.parts = []; + + while ( node.type === 'MemberExpression' ) { + this.parts.unshift( node.property ); + node = node.object; + } + + this.root = node; + } +} + +export default class MemberExpression extends Node { + bind ( scope ) { + // if this resolves to a namespaced declaration, prepare + // to replace it + // TODO this code is a bit inefficient + if ( isReference( this ) ) { // TODO optimise namespace access like `foo['bar']` as well + const keypath = new Keypath( this ); + + let declaration = scope.findDeclaration( keypath.root.name ); + + while ( declaration.isNamespace && keypath.parts.length ) { + const part = keypath.parts[0]; + declaration = declaration.module.traceExport( part.name ); + + if ( !declaration ) { + this.module.bundle.onwarn( `Export '${part.name}' is not defined by '${this.module.id}'` ); + break; + } + + keypath.parts.shift(); + } + + if ( keypath.parts.length ) { + super.bind( scope ); + return; // not a namespaced declaration + } + + this.declaration = declaration; + + if ( declaration.isExternal ) { + declaration.module.suggestName( keypath.root.name ); + } + } + + else { + super.bind( scope ); + } + } + + gatherPossibleValues ( values ) { + values.add( UNKNOWN ); // TODO + } + + render ( code, es ) { + if ( this.declaration ) { + const name = this.declaration.getName( es ); + if ( name !== this.name ) code.overwrite( this.start, this.end, name, true ); + } + + super.render( code, es ); + } + + run ( scope ) { + if ( this.declaration ) this.declaration.activate(); + super.run( scope ); + } +} diff --git a/src/ast/nodes/NewExpression.js b/src/ast/nodes/NewExpression.js new file mode 100644 index 0000000..8bfbb33 --- /dev/null +++ b/src/ast/nodes/NewExpression.js @@ -0,0 +1,8 @@ +import Node from '../Node.js'; +import callHasEffects from './shared/callHasEffects.js'; + +export default class NewExpression extends Node { + hasEffects ( scope ) { + return callHasEffects( scope, this.callee ); + } +} diff --git a/src/ast/nodes/ObjectExpression.js b/src/ast/nodes/ObjectExpression.js new file mode 100644 index 0000000..724cb00 --- /dev/null +++ b/src/ast/nodes/ObjectExpression.js @@ -0,0 +1,8 @@ +import Node from '../Node.js'; +import { OBJECT } from '../values.js'; + +export default class ObjectExpression extends Node { + gatherPossibleValues ( values ) { + values.add( OBJECT ); + } +} diff --git a/src/ast/nodes/ParenthesizedExpression.js b/src/ast/nodes/ParenthesizedExpression.js new file mode 100644 index 0000000..7037804 --- /dev/null +++ b/src/ast/nodes/ParenthesizedExpression.js @@ -0,0 +1,11 @@ +import Node from '../Node.js'; + +export default class ParenthesizedExpression extends Node { + getPossibleValues ( values ) { + return this.expression.getPossibleValues( values ); + } + + getValue () { + return this.expression.getValue(); + } +} diff --git a/src/ast/nodes/ReturnStatement.js b/src/ast/nodes/ReturnStatement.js new file mode 100644 index 0000000..bff5ae1 --- /dev/null +++ b/src/ast/nodes/ReturnStatement.js @@ -0,0 +1,7 @@ +import Node from '../Node.js'; + +export default class ReturnStatement extends Node { + // hasEffects () { + // return true; + // } +} diff --git a/src/ast/nodes/TemplateLiteral.js b/src/ast/nodes/TemplateLiteral.js new file mode 100644 index 0000000..057ce12 --- /dev/null +++ b/src/ast/nodes/TemplateLiteral.js @@ -0,0 +1,7 @@ +import Node from '../Node.js'; + +export default class TemplateLiteral extends Node { + render ( code ) { + code.indentExclusionRanges.push([ this.start, this.end ]); + } +} diff --git a/src/ast/nodes/ThisExpression.js b/src/ast/nodes/ThisExpression.js new file mode 100644 index 0000000..e614194 --- /dev/null +++ b/src/ast/nodes/ThisExpression.js @@ -0,0 +1,20 @@ +import Node from '../Node.js'; + +export default class ThisExpression extends Node { + initialise ( scope ) { + const lexicalBoundary = scope.findLexicalBoundary(); + + if ( lexicalBoundary.isModuleScope ) { + this.alias = this.module.bundle.context; + if ( this.alias === 'undefined' ) { + this.module.bundle.onwarn( 'The `this` keyword is equivalent to `undefined` at the top level of an ES module, and has been rewritten' ); + } + } + } + + render ( code ) { + if ( this.alias ) { + code.overwrite( this.start, this.end, this.alias, true ); + } + } +} diff --git a/src/ast/nodes/UnaryExpression.js b/src/ast/nodes/UnaryExpression.js new file mode 100644 index 0000000..9be1c2c --- /dev/null +++ b/src/ast/nodes/UnaryExpression.js @@ -0,0 +1,34 @@ +import Node from '../Node.js'; +import { UNKNOWN } from '../values.js'; + +const operators = { + "-": value => -value, + "+": value => +value, + "!": value => !value, + "~": value => ~value, + typeof: value => typeof value, + void: () => undefined, + delete: () => UNKNOWN +}; + +export default class UnaryExpression extends Node { + bind ( scope ) { + if ( this.value === UNKNOWN ) super.bind( scope ); + } + + getValue () { + const argumentValue = this.argument.getValue(); + if ( argumentValue === UNKNOWN ) return UNKNOWN; + + return operators[ this.operator ]( argumentValue ); + } + + hasEffects ( scope ) { + return this.operator === 'delete' || this.argument.hasEffects( scope ); + } + + initialise ( scope ) { + this.value = this.getValue(); + if ( this.value === UNKNOWN ) super.initialise( scope ); + } +} diff --git a/src/ast/nodes/UpdateExpression.js b/src/ast/nodes/UpdateExpression.js new file mode 100644 index 0000000..fc9d1ce --- /dev/null +++ b/src/ast/nodes/UpdateExpression.js @@ -0,0 +1,35 @@ +import Node from '../Node.js'; +import disallowIllegalReassignment from './shared/disallowIllegalReassignment.js'; +import isUsedByBundle from './shared/isUsedByBundle.js'; +import { NUMBER } from '../values.js'; + +export default class UpdateExpression extends Node { + bind ( scope ) { + let subject = this.argument; + while ( this.argument.type === 'ParenthesizedExpression' ) subject = subject.expression; + + this.subject = subject; + disallowIllegalReassignment( scope, this.argument ); + + if ( subject.type === 'Identifier' ) { + const declaration = scope.findDeclaration( subject.name ); + declaration.isReassigned = true; + declaration.possibleValues.add( NUMBER ); + } + + super.bind( scope ); + } + + hasEffects ( scope ) { + return isUsedByBundle( scope, this.subject ); + } + + initialise ( scope ) { + this.module.bundle.dependentExpressions.push( this ); + super.initialise( scope ); + } + + isUsedByBundle () { + return isUsedByBundle( this.findScope(), this.subject ); + } +} diff --git a/src/ast/nodes/VariableDeclaration.js b/src/ast/nodes/VariableDeclaration.js new file mode 100644 index 0000000..5ce99e9 --- /dev/null +++ b/src/ast/nodes/VariableDeclaration.js @@ -0,0 +1,86 @@ +import Node from '../Node.js'; +import extractNames from '../utils/extractNames.js'; + +function getSeparator ( code, start ) { + let c = start; + + while ( c > 0 && code[ c - 1 ] !== '\n' ) { + c -= 1; + if ( code[c] === ';' || code[c] === '{' ) return '; '; + } + + const lineStart = code.slice( c, start ).match( /^\s*/ )[0]; + + return `;\n${lineStart}`; +} + +export default class VariableDeclaration extends Node { + render ( code, es ) { + const treeshake = this.module.bundle.treeshake; + const separator = this.declarations.length ? getSeparator( this.module.code, this.start ) : ''; + + let c = this.start; + let empty = true; + + for ( let i = 0; i < this.declarations.length; i += 1 ) { + const declarator = this.declarations[i]; + + const prefix = empty ? '' : separator; // TODO indentation + + if ( declarator.id.type === 'Identifier' ) { + const proxy = declarator.proxies.get( declarator.id.name ); + const isExportedAndReassigned = !es && proxy.exportName && proxy.isReassigned; + + if ( isExportedAndReassigned ) { + if ( declarator.init ) { + code.overwrite( c, declarator.start, prefix ); + c = declarator.end; + empty = false; + } + } else if ( !treeshake || proxy.activated ) { + code.overwrite( c, declarator.start, `${prefix}${this.kind} ` ); // TODO indentation + c = declarator.end; + empty = false; + } + } + + else { + const exportAssignments = []; + let activated = false; + + extractNames( declarator.id ).forEach( name => { + const proxy = declarator.proxies.get( name ); + const isExportedAndReassigned = !es && proxy.exportName && proxy.isReassigned; + + if ( isExportedAndReassigned ) { + // code.overwrite( c, declarator.start, prefix ); + // c = declarator.end; + // empty = false; + exportAssignments.push( 'TODO' ); + } else if ( declarator.activated ) { + activated = true; + } + }); + + if ( !treeshake || activated ) { + code.overwrite( c, declarator.start, `${prefix}${this.kind} ` ); // TODO indentation + c = declarator.end; + empty = false; + } + + if ( exportAssignments.length ) { + throw new Error( 'TODO' ); + } + } + + declarator.render( code, es ); + } + + if ( treeshake && empty ) { + code.remove( this.leadingCommentStart || this.start, this.next || this.end ); + } else if ( this.end > c ) { + const hasSemicolon = code.original[ this.end - 1 ] === ';'; + code.overwrite( c, this.end, hasSemicolon ? ';' : '' ); + } + } +} diff --git a/src/ast/nodes/VariableDeclarator.js b/src/ast/nodes/VariableDeclarator.js new file mode 100644 index 0000000..61ca51b --- /dev/null +++ b/src/ast/nodes/VariableDeclarator.js @@ -0,0 +1,89 @@ +import Node from '../Node.js'; +import extractNames from '../utils/extractNames.js'; +import { UNKNOWN } from '../values.js'; + +class DeclaratorProxy { + constructor ( name, declarator, isTopLevel, init ) { + this.name = name; + this.declarator = declarator; + + this.activated = false; + this.isReassigned = false; + this.exportName = null; + + this.possibleValues = new Set( init ? [ init ] : null ); + } + + activate () { + this.activated = true; + this.declarator.activate(); + } + + addReference () { + /* noop? */ + } + + gatherPossibleValues ( values ) { + this.possibleValues.forEach( value => values.add( value ) ); + } + + getName ( es ) { + // TODO desctructuring... + if ( es ) return this.name; + if ( !this.isReassigned || !this.exportName ) return this.name; + + return `exports.${this.exportName}`; + } + + toString () { + return this.name; + } +} + +export default class VariableDeclarator extends Node { + activate () { + if ( this.activated ) return; + this.activated = true; + + this.run( this.findScope() ); + } + + hasEffects ( scope ) { + return this.init && this.init.hasEffects( scope ); + } + + initialise ( scope ) { + this.proxies = new Map(); + + const lexicalBoundary = scope.findLexicalBoundary(); + + const init = this.init ? + ( this.id.type === 'Identifier' ? this.init : UNKNOWN ) : // TODO maybe UNKNOWN is unnecessary + null; + + extractNames( this.id ).forEach( name => { + const proxy = new DeclaratorProxy( name, this, lexicalBoundary.isModuleScope, init ); + + this.proxies.set( name, proxy ); + scope.addDeclaration( name, proxy, this.parent.kind === 'var' ); + }); + + super.initialise( scope ); + } + + render ( code, es ) { + extractNames( this.id ).forEach( name => { + const declaration = this.proxies.get( name ); + + if ( !es && declaration.exportName && declaration.isReassigned ) { + if ( this.init ) { + code.overwrite( this.start, this.id.end, declaration.getName( es ) ); + } else if ( this.module.bundle.treeshake ) { + code.remove( this.start, this.end ); + } + } + }); + + super.render( code, es ); + } +} diff --git a/src/ast/nodes/index.js b/src/ast/nodes/index.js new file mode 100644 index 0000000..c276a34 --- /dev/null +++ b/src/ast/nodes/index.js @@ -0,0 +1,63 @@ +import ArrayExpression from './ArrayExpression.js'; +import ArrowFunctionExpression from './ArrowFunctionExpression.js'; +import AssignmentExpression from './AssignmentExpression.js'; +import BinaryExpression from './BinaryExpression.js'; +import BlockStatement from './BlockStatement.js'; +import CallExpression from './CallExpression.js'; +import ClassDeclaration from './ClassDeclaration.js'; +import ClassExpression from './ClassExpression.js'; +import ConditionalExpression from './ConditionalExpression.js'; +import ExportAllDeclaration from './ExportAllDeclaration.js'; +import ExportDefaultDeclaration from './ExportDefaultDeclaration.js'; +import ExportNamedDeclaration from './ExportNamedDeclaration.js'; +import ExpressionStatement from './ExpressionStatement.js'; +import FunctionDeclaration from './FunctionDeclaration.js'; +import FunctionExpression from './FunctionExpression.js'; +import Identifier from './Identifier.js'; +import IfStatement from './IfStatement.js'; +import ImportDeclaration from './ImportDeclaration.js'; +import Literal from './Literal.js'; +import MemberExpression from './MemberExpression.js'; +import NewExpression from './NewExpression.js'; +import ObjectExpression from './ObjectExpression.js'; +import ParenthesizedExpression from './ParenthesizedExpression.js'; +import ReturnStatement from './ReturnStatement.js'; +import TemplateLiteral from './TemplateLiteral.js'; +import ThisExpression from './ThisExpression.js'; +import UnaryExpression from './UnaryExpression.js'; +import UpdateExpression from './UpdateExpression.js'; +import VariableDeclarator from './VariableDeclarator.js'; +import VariableDeclaration from './VariableDeclaration.js'; + +export default { + ArrayExpression, + ArrowFunctionExpression, + AssignmentExpression, + BinaryExpression, + BlockStatement, + CallExpression, + ClassDeclaration, + ClassExpression, + ConditionalExpression, + ExportAllDeclaration, + ExportDefaultDeclaration, + ExportNamedDeclaration, + ExpressionStatement, + FunctionDeclaration, + FunctionExpression, + Identifier, + IfStatement, + ImportDeclaration, + Literal, + MemberExpression, + NewExpression, + ObjectExpression, + ParenthesizedExpression, + ReturnStatement, + TemplateLiteral, + ThisExpression, + UnaryExpression, + UpdateExpression, + VariableDeclarator, + VariableDeclaration +}; diff --git a/src/ast/nodes/shared/callHasEffects.js b/src/ast/nodes/shared/callHasEffects.js new file mode 100644 index 0000000..74bf0ba --- /dev/null +++ b/src/ast/nodes/shared/callHasEffects.js @@ -0,0 +1,65 @@ +import flatten from '../../utils/flatten.js'; +import isReference from '../../utils/isReference.js'; +import pureFunctions from './pureFunctions.js'; +import { UNKNOWN } from '../../values.js'; + +function fnHasEffects ( fn ) { + if ( fn._calling ) return true; // prevent infinite loops... TODO there must be a better way + fn._calling = true; + + // handle body-less arrow functions + const scope = fn.body.scope || fn.scope; + const body = fn.body.body || [ fn.body ]; + + for ( const node of body ) { + if ( node.hasEffects( scope ) ) { + fn._calling = false; + return true; + } + } + + fn._calling = false; + return false; +} + +export default function callHasEffects ( scope, callee ) { + const values = new Set([ callee ]); + + for ( const node of values ) { + if ( node === UNKNOWN ) return true; // err on side of caution + + if ( /Function/.test( node.type ) ) { + if ( fnHasEffects( node ) ) return true; + } + + else if ( isReference( node ) ) { + const flattened = flatten( node ); + const declaration = scope.findDeclaration( flattened.name ); + + if ( declaration.isGlobal ) { + if ( !pureFunctions[ flattened.keypath ] ) return true; + } + + else if ( declaration.isExternal ) { + return true; // TODO make this configurable? e.g. `path.[whatever]` + } + + else { + if ( node.declaration ) { + node.declaration.gatherPossibleValues( values ); + } else { + return true; + } + } + } + + else { + if ( !node.gatherPossibleValues ) { + throw new Error( 'TODO' ); + } + node.gatherPossibleValues( values ); + } + } + + return false; +} diff --git a/src/ast/nodes/shared/disallowIllegalReassignment.js b/src/ast/nodes/shared/disallowIllegalReassignment.js new file mode 100644 index 0000000..a40969f --- /dev/null +++ b/src/ast/nodes/shared/disallowIllegalReassignment.js @@ -0,0 +1,28 @@ +import getLocation from '../../../utils/getLocation.js'; +import error from '../../../utils/error.js'; + +// TODO tidy this up a bit (e.g. they can both use node.module.imports) +export default function disallowIllegalReassignment ( scope, node ) { + if ( node.type === 'MemberExpression' && node.object.type === 'Identifier' ) { + const declaration = scope.findDeclaration( node.object.name ); + if ( declaration.isNamespace ) { + error({ + message: `Illegal reassignment to import '${node.object.name}'`, + file: node.module.id, + pos: node.start, + loc: getLocation( node.module.code, node.start ) + }); + } + } + + else if ( node.type === 'Identifier' ) { + if ( node.module.imports[ node.name ] && !scope.contains( node.name ) ) { + error({ + message: `Illegal reassignment to import '${node.name}'`, + file: node.module.id, + pos: node.start, + loc: getLocation( node.module.code, node.start ) + }); + } + } +} diff --git a/src/ast/nodes/shared/isUsedByBundle.js b/src/ast/nodes/shared/isUsedByBundle.js new file mode 100644 index 0000000..a294c0c --- /dev/null +++ b/src/ast/nodes/shared/isUsedByBundle.js @@ -0,0 +1,48 @@ +import { + ARRAY, + BOOLEAN, + FUNCTION, + NUMBER, + OBJECT, + STRING, + UNKNOWN +} from '../../values.js'; + +export default function isUsedByBundle ( scope, node ) { + while ( node.type === 'ParenthesizedExpression' ) node = node.expression; + + const expression = node; + while ( node.type === 'MemberExpression' ) node = node.object; + + const declaration = scope.findDeclaration( node.name ); + + if ( declaration.isParam ) { + return true; + + // TODO if we mutate a parameter, assume the worst + // return node !== expression; + } + + if ( declaration.activated ) return true; + + const values = new Set(); + declaration.gatherPossibleValues( values ); + for ( const value of values ) { + if ( value === UNKNOWN ) { + return true; + } + + if ( value.type === 'Identifier' ) { + if ( value.declaration.activated ) { + return true; + } + value.declaration.gatherPossibleValues( values ); + } + + else if ( value.gatherPossibleValues ) { + value.gatherPossibleValues( values ); + } + } + + return false; +} diff --git a/src/utils/pureFunctions.js b/src/ast/nodes/shared/pureFunctions.js similarity index 100% rename from src/utils/pureFunctions.js rename to src/ast/nodes/shared/pureFunctions.js diff --git a/src/ast/scopes/BundleScope.js b/src/ast/scopes/BundleScope.js new file mode 100644 index 0000000..6c0f0ea --- /dev/null +++ b/src/ast/scopes/BundleScope.js @@ -0,0 +1,40 @@ +import Scope from './Scope.js'; +import { UNKNOWN } from '../values'; + +class SyntheticGlobalDeclaration { + constructor ( name ) { + this.name = name; + this.isExternal = true; + this.isGlobal = true; + this.isReassigned = false; + + this.activated = true; + } + + activate () { + /* noop */ + } + + addReference ( reference ) { + reference.declaration = this; + if ( reference.isReassignment ) this.isReassigned = true; + } + + gatherPossibleValues ( values ) { + values.add( UNKNOWN ); + } + + getName () { + return this.name; + } +} + +export default class BundleScope extends Scope { + findDeclaration ( name ) { + if ( !this.declarations[ name ] ) { + this.declarations[ name ] = new SyntheticGlobalDeclaration( name ); + } + + return this.declarations[ name ]; + } +} diff --git a/src/ast/scopes/ModuleScope.js b/src/ast/scopes/ModuleScope.js new file mode 100644 index 0000000..f451d20 --- /dev/null +++ b/src/ast/scopes/ModuleScope.js @@ -0,0 +1,47 @@ +import { forOwn } from '../../utils/object.js'; +import Scope from './Scope.js'; + +export default class ModuleScope extends Scope { + constructor ( module ) { + super({ + isBlockScope: false, + isLexicalBoundary: true, + isModuleScope: true, + parent: module.bundle.scope + }); + + this.module = module; + } + + deshadow ( names ) { + names = new Map( names ); + + forOwn( this.module.imports, specifier => { + if ( specifier.module.isExternal ) return; + + if ( specifier.name === '*' ) { + specifier.module.getExports().forEach( name => { + names.set( name, true ); + }); + } else { + const declaration = specifier.module.traceExport( specifier.name ); + const name = declaration.getName( true ); + if ( name !== specifier.name ) names.set( declaration.getName( true ) ); + } + }); + + super.deshadow( names ); + } + + findDeclaration ( name ) { + if ( this.declarations[ name ] ) { + return this.declarations[ name ]; + } + + return this.module.trace( name ) || this.parent.findDeclaration( name ); + } + + findLexicalBoundary () { + return this; + } +} diff --git a/src/ast/scopes/Scope.js b/src/ast/scopes/Scope.js new file mode 100644 index 0000000..c77e6d2 --- /dev/null +++ b/src/ast/scopes/Scope.js @@ -0,0 +1,91 @@ +import { blank, keys } from '../../utils/object.js'; +import { UNKNOWN } from '../values.js'; + +class Parameter { + constructor ( name ) { + this.name = name; + + this.isParam = true; + this.activated = true; + } + + activate () { + // noop + } + + addReference () { + // noop? + } + + gatherPossibleValues ( values ) { + values.add( UNKNOWN ); // TODO populate this at call time + } + + getName () { + return this.name; + } +} + +export default class Scope { + constructor ( options ) { + options = options || {}; + + this.parent = options.parent; + this.isBlockScope = !!options.isBlockScope; + this.isLexicalBoundary = !!options.isLexicalBoundary; + this.isModuleScope = !!options.isModuleScope; + + this.children = []; + if ( this.parent ) this.parent.children.push( this ); + + this.declarations = blank(); + + if ( this.isLexicalBoundary && !this.isModuleScope ) { + this.declarations.arguments = new Parameter( 'arguments' ); + } + } + + addDeclaration ( name, declaration, isVar, isParam ) { + if ( isVar && this.isBlockScope ) { + this.parent.addDeclaration( name, declaration, isVar, isParam ); + } else { + this.declarations[ name ] = isParam ? new Parameter( name ) : declaration; + } + } + + contains ( name ) { + return !!this.declarations[ name ] || + ( this.parent ? this.parent.contains( name ) : false ); + } + + deshadow ( names ) { + keys( this.declarations ).forEach( key => { + const declaration = this.declarations[ key ]; + + // we can disregard exports.foo etc + if ( declaration.exportName && declaration.isReassigned ) return; + + const name = declaration.getName( true ); + let deshadowed = name; + + let i = 1; + + while ( names.has( deshadowed ) ) { + deshadowed = `${name}$$${i++}`; + } + + declaration.name = deshadowed; + }); + + this.children.forEach( scope => scope.deshadow( names ) ); + } + + findDeclaration ( name ) { + return this.declarations[ name ] || + ( this.parent && this.parent.findDeclaration( name ) ); + } + + findLexicalBoundary () { + return this.isLexicalBoundary ? this : this.parent.findLexicalBoundary(); + } +} diff --git a/src/ast/extractNames.js b/src/ast/utils/extractNames.js similarity index 100% rename from src/ast/extractNames.js rename to src/ast/utils/extractNames.js diff --git a/src/ast/flatten.js b/src/ast/utils/flatten.js similarity index 100% rename from src/ast/flatten.js rename to src/ast/utils/flatten.js diff --git a/src/ast/isReference.js b/src/ast/utils/isReference.js similarity index 100% rename from src/ast/isReference.js rename to src/ast/utils/isReference.js diff --git a/src/ast/values.js b/src/ast/values.js new file mode 100644 index 0000000..dadd74a --- /dev/null +++ b/src/ast/values.js @@ -0,0 +1,8 @@ +// properties are for debugging purposes only +export const ARRAY = { ARRAY: true, toString: () => '[[ARRAY]]' }; +export const BOOLEAN = { BOOLEAN: true, toString: () => '[[BOOLEAN]]' }; +export const FUNCTION = { FUNCTION: true, toString: () => '[[FUNCTION]]' }; +export const NUMBER = { NUMBER: true, toString: () => '[[NUMBER]]' }; +export const OBJECT = { OBJECT: true, toString: () => '[[OBJECT]]' }; +export const STRING = { STRING: true, toString: () => '[[STRING]]' }; +export const UNKNOWN = { UNKNOWN: true, toString: () => '[[UNKNOWN]]' }; diff --git a/src/finalisers/es.js b/src/finalisers/es.js index 4e08ce3..8251fe3 100644 --- a/src/finalisers/es.js +++ b/src/finalisers/es.js @@ -26,8 +26,8 @@ export default function es ( bundle, magicString, { intro }, options ) { } } - const namespaceSpecifier = module.declarations['*'] ? `* as ${module.name}` : null; - const namedSpecifier = importedNames.length ? `{ ${importedNames.join( ', ' )} }` : null; + const namespaceSpecifier = module.declarations['*'] ? `* as ${module.name}` : null; // TODO prevent unnecessary namespace import, e.g form/external-imports + const namedSpecifier = importedNames.length ? `{ ${importedNames.sort().join( ', ' )} }` : null; if ( namespaceSpecifier && namedSpecifier ) { // Namespace and named specifiers cannot be combined. @@ -56,7 +56,7 @@ export default function es ( bundle, magicString, { intro }, options ) { const specifiers = module.getExports().filter( notDefault ).map( name => { const declaration = module.traceExport( name ); - const rendered = declaration.render( true ); + const rendered = declaration.getName( true ); return rendered === name ? name : @@ -67,7 +67,7 @@ export default function es ( bundle, magicString, { intro }, options ) { const defaultExport = module.exports.default || module.reexports.default; if ( defaultExport ) { - exportBlock += `export default ${module.traceExport( 'default' ).render( true )};`; + exportBlock += `export default ${module.traceExport( 'default' ).getName( true )};`; } if ( exportBlock ) magicString.append( '\n\n' + exportBlock.trim() ); diff --git a/src/finalisers/shared/getExportBlock.js b/src/finalisers/shared/getExportBlock.js index 972da59..5a7f34f 100644 --- a/src/finalisers/shared/getExportBlock.js +++ b/src/finalisers/shared/getExportBlock.js @@ -1,6 +1,6 @@ export default function getExportBlock ( entryModule, exportMode, mechanism = 'return' ) { if ( exportMode === 'default' ) { - return `${mechanism} ${entryModule.traceExport( 'default' ).render( false )};`; + return `${mechanism} ${entryModule.traceExport( 'default' ).getName( false )};`; } return entryModule.getExports() @@ -9,7 +9,9 @@ export default function getExportBlock ( entryModule, exportMode, mechanism = 'r const declaration = entryModule.traceExport( name ); const lhs = `exports${prop}`; - const rhs = declaration.render( false ); + const rhs = declaration ? + declaration.getName( false ) : + name; // exporting a global // prevent `exports.count = exports.count` if ( lhs === rhs ) return null; diff --git a/src/utils/run.js b/src/utils/run.js deleted file mode 100644 index 23b0493..0000000 --- a/src/utils/run.js +++ /dev/null @@ -1,119 +0,0 @@ -import { walk } from 'estree-walker'; -import modifierNodes, { isModifierNode } from '../ast/modifierNodes.js'; -import isReference from '../ast/isReference.js'; -import flatten from '../ast/flatten'; -import pureFunctions from './pureFunctions.js'; -import getLocation from './getLocation.js'; -import error from './error.js'; - -function call ( callee, scope, statement, strongDependencies ) { - while ( callee.type === 'ParenthesizedExpression' ) callee = callee.expression; - - if ( callee.type === 'Identifier' ) { - const declaration = scope.findDeclaration( callee.name ) || - statement.module.trace( callee.name ); - - if ( declaration ) { - if ( declaration.isNamespace ) { - error({ - message: `Cannot call a namespace ('${callee.name}')`, - file: statement.module.id, - pos: callee.start, - loc: getLocation( statement.module.code, callee.start ) - }); - } - - return declaration.run( strongDependencies ); - } - - return !pureFunctions[ callee.name ]; - } - - if ( /FunctionExpression/.test( callee.type ) ) { - return run( callee.body, scope, statement, strongDependencies ); - } - - if ( callee.type === 'MemberExpression' ) { - const flattened = flatten( callee ); - - if ( flattened ) { - // if we're calling e.g. Object.keys(thing), there are no side-effects - // TODO make pureFunctions configurable - const declaration = scope.findDeclaration( flattened.name ) || statement.module.trace( flattened.name ); - - return ( !!declaration || !pureFunctions[ flattened.keypath ] ); - } - } - - // complex case like `( a ? b : c )()` or foo[bar].baz()` - // – err on the side of caution - return true; -} - -export default function run ( node, scope, statement, strongDependencies, force ) { - let hasSideEffect = false; - - walk( node, { - enter ( node, parent ) { - if ( !force && /Function/.test( node.type ) ) return this.skip(); - - if ( node._scope ) scope = node._scope; - - if ( isReference( node, parent ) ) { - const flattened = flatten( node ); - - if ( flattened.name === 'arguments' ) { - hasSideEffect = true; - } - - else if ( !scope.contains( flattened.name ) ) { - const declaration = statement.module.trace( flattened.name ); - if ( declaration && !declaration.isExternal ) { - const module = declaration.module || declaration.statement.module; // TODO is this right? - if ( !module.isExternal && !~strongDependencies.indexOf( module ) ) strongDependencies.push( module ); - } - } - } - - else if ( node.type === 'DebuggerStatement' ) { - hasSideEffect = true; - } - - else if ( node.type === 'ThrowStatement' ) { - // we only care about errors thrown at the top level, otherwise - // any function with error checking gets included if called - if ( scope.isTopLevel ) hasSideEffect = true; - } - - else if ( node.type === 'CallExpression' || node.type === 'NewExpression' ) { - if ( call( node.callee, scope, statement, strongDependencies ) ) { - hasSideEffect = true; - } - } - - else if ( isModifierNode( node ) ) { - let subject = node[ modifierNodes[ node.type ] ]; - while ( subject.type === 'MemberExpression' ) subject = subject.object; - - let declaration = scope.findDeclaration( subject.name ); - - if ( declaration ) { - if ( declaration.isParam ) hasSideEffect = true; - } else if ( !scope.isTopLevel ) { - hasSideEffect = true; - } else { - declaration = statement.module.trace( subject.name ); - - if ( !declaration || declaration.isExternal || declaration.isUsed || ( declaration.original && declaration.original.isUsed ) ) { - hasSideEffect = true; - } - } - } - }, - leave ( node ) { - if ( node._scope ) scope = scope.parent; - } - }); - - return hasSideEffect; -} diff --git a/test/form/assignment-to-exports-class-declaration/_config.js b/test/form/assignment-to-exports-class-declaration/_config.js index 0f3d2ef..f7296e0 100644 --- a/test/form/assignment-to-exports-class-declaration/_config.js +++ b/test/form/assignment-to-exports-class-declaration/_config.js @@ -1,5 +1,5 @@ module.exports = { - description: 'does not rewrite class declaration IDs', + description: 'does not rewrite class expression IDs', options: { moduleName: 'myModule' } diff --git a/test/form/external-imports/_expected/es.js b/test/form/external-imports/_expected/es.js index 1c632ef..59d2b5e 100644 --- a/test/form/external-imports/_expected/es.js +++ b/test/form/external-imports/_expected/es.js @@ -1,11 +1,11 @@ import factory from 'factory'; import { bar, foo } from 'baz'; -import { port } from 'shipping-port'; +import { forEach, port } from 'shipping-port'; import * as containers from 'shipping-port'; import alphabet, { a } from 'alphabet'; factory( null ); foo( bar, port ); -containers.forEach( console.log, console ); +forEach( console.log, console ); console.log( a ); console.log( alphabet.length ); diff --git a/test/form/import-external-namespace-and-default/_expected/es.js b/test/form/import-external-namespace-and-default/_expected/es.js index 5655595..07d61c1 100644 --- a/test/form/import-external-namespace-and-default/_expected/es.js +++ b/test/form/import-external-namespace-and-default/_expected/es.js @@ -1,6 +1,7 @@ -import * as foo from 'foo'; +import { bar } from 'foo'; import foo__default from 'foo'; +import * as foo from 'foo'; -console.log( foo.bar ); +console.log( bar ); console.log( foo__default ); diff --git a/test/form/namespace-optimization/_expected/amd.js b/test/form/namespace-optimization/_expected/amd.js index a244c47..95231f1 100644 --- a/test/form/namespace-optimization/_expected/amd.js +++ b/test/form/namespace-optimization/_expected/amd.js @@ -2,6 +2,6 @@ define(function () { 'use strict'; function a () {} - a(); + console.log( a() ); }); diff --git a/test/form/namespace-optimization/_expected/cjs.js b/test/form/namespace-optimization/_expected/cjs.js index b52a7e5..33a83e9 100644 --- a/test/form/namespace-optimization/_expected/cjs.js +++ b/test/form/namespace-optimization/_expected/cjs.js @@ -2,4 +2,4 @@ function a () {} -a(); +console.log( a() ); diff --git a/test/form/namespace-optimization/_expected/es.js b/test/form/namespace-optimization/_expected/es.js index 8bee044..297521e 100644 --- a/test/form/namespace-optimization/_expected/es.js +++ b/test/form/namespace-optimization/_expected/es.js @@ -1,3 +1,3 @@ function a () {} -a(); +console.log( a() ); diff --git a/test/form/namespace-optimization/_expected/iife.js b/test/form/namespace-optimization/_expected/iife.js index 206c237..64673c9 100644 --- a/test/form/namespace-optimization/_expected/iife.js +++ b/test/form/namespace-optimization/_expected/iife.js @@ -3,6 +3,6 @@ function a () {} - a(); + console.log( a() ); }()); diff --git a/test/form/namespace-optimization/_expected/umd.js b/test/form/namespace-optimization/_expected/umd.js index b2bc94f..bf379ef 100644 --- a/test/form/namespace-optimization/_expected/umd.js +++ b/test/form/namespace-optimization/_expected/umd.js @@ -6,6 +6,6 @@ function a () {} - a(); + console.log( a() ); -}))); \ No newline at end of file +}))); diff --git a/test/form/namespace-optimization/main.js b/test/form/namespace-optimization/main.js index e902244..6b955f7 100644 --- a/test/form/namespace-optimization/main.js +++ b/test/form/namespace-optimization/main.js @@ -1,3 +1,3 @@ import * as foo from './foo'; -foo.bar.quux.a(); +console.log( foo.bar.quux.a() ); diff --git a/test/form/no-treeshake/_expected/es.js b/test/form/no-treeshake/_expected/es.js index 54f2add..ddcec9b 100644 --- a/test/form/no-treeshake/_expected/es.js +++ b/test/form/no-treeshake/_expected/es.js @@ -1,3 +1,4 @@ +import { value } from 'external'; import * as external from 'external'; var foo = 'unused'; @@ -11,7 +12,7 @@ function bar () { } function baz () { - return 13 + external.value; + return 13 + value; } var create = Object.create; diff --git a/test/form/removes-existing-sourcemap-comments/_expected/amd.js b/test/form/removes-existing-sourcemap-comments/_expected/amd.js index e614344..99bdf35 100644 --- a/test/form/removes-existing-sourcemap-comments/_expected/amd.js +++ b/test/form/removes-existing-sourcemap-comments/_expected/amd.js @@ -1,6 +1,6 @@ define(function () { 'use strict'; - function foo () { + var foo = function () { return 42; } diff --git a/test/form/removes-existing-sourcemap-comments/_expected/cjs.js b/test/form/removes-existing-sourcemap-comments/_expected/cjs.js index d7a8df2..8735c93 100644 --- a/test/form/removes-existing-sourcemap-comments/_expected/cjs.js +++ b/test/form/removes-existing-sourcemap-comments/_expected/cjs.js @@ -1,6 +1,6 @@ 'use strict'; -function foo () { +var foo = function () { return 42; } diff --git a/test/form/removes-existing-sourcemap-comments/_expected/es.js b/test/form/removes-existing-sourcemap-comments/_expected/es.js index c458828..2a09cfb 100644 --- a/test/form/removes-existing-sourcemap-comments/_expected/es.js +++ b/test/form/removes-existing-sourcemap-comments/_expected/es.js @@ -1,4 +1,4 @@ -function foo () { +var foo = function () { return 42; } diff --git a/test/form/removes-existing-sourcemap-comments/_expected/iife.js b/test/form/removes-existing-sourcemap-comments/_expected/iife.js index 5867d0f..6381613 100644 --- a/test/form/removes-existing-sourcemap-comments/_expected/iife.js +++ b/test/form/removes-existing-sourcemap-comments/_expected/iife.js @@ -1,7 +1,7 @@ (function () { 'use strict'; - function foo () { + var foo = function () { return 42; } diff --git a/test/form/removes-existing-sourcemap-comments/_expected/umd.js b/test/form/removes-existing-sourcemap-comments/_expected/umd.js index bbfb959..b291566 100644 --- a/test/form/removes-existing-sourcemap-comments/_expected/umd.js +++ b/test/form/removes-existing-sourcemap-comments/_expected/umd.js @@ -4,10 +4,10 @@ (factory()); }(this, (function () { 'use strict'; - function foo () { + var foo = function () { return 42; } console.log( foo() ); -}))); \ No newline at end of file +}))); diff --git a/test/form/side-effect-k/_expected/amd.js b/test/form/side-effect-k/_expected/amd.js index e903a01..9ce8a48 100644 --- a/test/form/side-effect-k/_expected/amd.js +++ b/test/form/side-effect-k/_expected/amd.js @@ -1,7 +1,8 @@ define(function () { 'use strict'; function augment ( x ) { - var prop, source; + var prop; + var source; var i = arguments.length; var sources = Array( i - 1 ); @@ -25,4 +26,4 @@ define(function () { 'use strict'; return x; -}); \ No newline at end of file +}); diff --git a/test/form/side-effect-k/_expected/cjs.js b/test/form/side-effect-k/_expected/cjs.js index 7ee74b0..77a3fa7 100644 --- a/test/form/side-effect-k/_expected/cjs.js +++ b/test/form/side-effect-k/_expected/cjs.js @@ -1,7 +1,8 @@ 'use strict'; function augment ( x ) { - var prop, source; + var prop; + var source; var i = arguments.length; var sources = Array( i - 1 ); @@ -23,4 +24,4 @@ function augment ( x ) { function x () {} augment( x.prototype ); -module.exports = x; \ No newline at end of file +module.exports = x; diff --git a/test/form/side-effect-k/_expected/es.js b/test/form/side-effect-k/_expected/es.js index 0cbda65..726addd 100644 --- a/test/form/side-effect-k/_expected/es.js +++ b/test/form/side-effect-k/_expected/es.js @@ -1,5 +1,6 @@ function augment ( x ) { - var prop, source; + var prop; + var source; var i = arguments.length; var sources = Array( i - 1 ); @@ -21,4 +22,4 @@ function augment ( x ) { function x () {} augment( x.prototype ); -export default x; \ No newline at end of file +export default x; diff --git a/test/form/side-effect-k/_expected/iife.js b/test/form/side-effect-k/_expected/iife.js index bccee01..6a6255f 100644 --- a/test/form/side-effect-k/_expected/iife.js +++ b/test/form/side-effect-k/_expected/iife.js @@ -2,7 +2,8 @@ var myBundle = (function () { 'use strict'; function augment ( x ) { - var prop, source; + var prop; + var source; var i = arguments.length; var sources = Array( i - 1 ); @@ -26,4 +27,4 @@ var myBundle = (function () { return x; -}()); \ No newline at end of file +}()); diff --git a/test/form/side-effect-k/_expected/umd.js b/test/form/side-effect-k/_expected/umd.js index 3d6da0b..f6fff9f 100644 --- a/test/form/side-effect-k/_expected/umd.js +++ b/test/form/side-effect-k/_expected/umd.js @@ -5,7 +5,8 @@ }(this, (function () { 'use strict'; function augment ( x ) { - var prop, source; + var prop; + var source; var i = arguments.length; var sources = Array( i - 1 ); @@ -29,4 +30,4 @@ return x; -}))); \ No newline at end of file +}))); diff --git a/test/form/skips-dead-branches-b/_config.js b/test/form/skips-dead-branches-b/_config.js new file mode 100644 index 0000000..22c399c --- /dev/null +++ b/test/form/skips-dead-branches-b/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'skips a dead branch (b)' +}; diff --git a/test/form/skips-dead-branches-b/_expected/amd.js b/test/form/skips-dead-branches-b/_expected/amd.js new file mode 100644 index 0000000..c192900 --- /dev/null +++ b/test/form/skips-dead-branches-b/_expected/amd.js @@ -0,0 +1,9 @@ +define(function () { 'use strict'; + + function bar () { + console.log( 'this should be included' ); + } + + bar(); + +}); diff --git a/test/form/skips-dead-branches-b/_expected/cjs.js b/test/form/skips-dead-branches-b/_expected/cjs.js new file mode 100644 index 0000000..b3e974b --- /dev/null +++ b/test/form/skips-dead-branches-b/_expected/cjs.js @@ -0,0 +1,7 @@ +'use strict'; + +function bar () { + console.log( 'this should be included' ); +} + +bar(); diff --git a/test/form/skips-dead-branches-b/_expected/es.js b/test/form/skips-dead-branches-b/_expected/es.js new file mode 100644 index 0000000..ec9eb20 --- /dev/null +++ b/test/form/skips-dead-branches-b/_expected/es.js @@ -0,0 +1,5 @@ +function bar () { + console.log( 'this should be included' ); +} + +bar(); diff --git a/test/form/skips-dead-branches-b/_expected/iife.js b/test/form/skips-dead-branches-b/_expected/iife.js new file mode 100644 index 0000000..3b894d9 --- /dev/null +++ b/test/form/skips-dead-branches-b/_expected/iife.js @@ -0,0 +1,10 @@ +(function () { + 'use strict'; + + function bar () { + console.log( 'this should be included' ); + } + + bar(); + +}()); diff --git a/test/form/skips-dead-branches-b/_expected/umd.js b/test/form/skips-dead-branches-b/_expected/umd.js new file mode 100644 index 0000000..b3926b0 --- /dev/null +++ b/test/form/skips-dead-branches-b/_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 bar () { + console.log( 'this should be included' ); + } + + bar(); + +}))); diff --git a/test/function/skips-dead-branches-b/main.js b/test/form/skips-dead-branches-b/main.js similarity index 100% rename from test/function/skips-dead-branches-b/main.js rename to test/form/skips-dead-branches-b/main.js diff --git a/test/form/skips-dead-branches-c/_config.js b/test/form/skips-dead-branches-c/_config.js new file mode 100644 index 0000000..8674142 --- /dev/null +++ b/test/form/skips-dead-branches-c/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'skips a dead branch (c)' +}; diff --git a/test/form/skips-dead-branches-c/_expected/amd.js b/test/form/skips-dead-branches-c/_expected/amd.js new file mode 100644 index 0000000..9dc50f5 --- /dev/null +++ b/test/form/skips-dead-branches-c/_expected/amd.js @@ -0,0 +1,9 @@ +define(function () { 'use strict'; + + function bar () { + console.log( 'this should be included' ); + } + + bar(); + +}); \ No newline at end of file diff --git a/test/form/skips-dead-branches-c/_expected/cjs.js b/test/form/skips-dead-branches-c/_expected/cjs.js new file mode 100644 index 0000000..d6aa952 --- /dev/null +++ b/test/form/skips-dead-branches-c/_expected/cjs.js @@ -0,0 +1,7 @@ +'use strict'; + +function bar () { + console.log( 'this should be included' ); +} + +bar(); \ No newline at end of file diff --git a/test/form/skips-dead-branches-c/_expected/es.js b/test/form/skips-dead-branches-c/_expected/es.js new file mode 100644 index 0000000..f39bfec --- /dev/null +++ b/test/form/skips-dead-branches-c/_expected/es.js @@ -0,0 +1,5 @@ +function bar () { + console.log( 'this should be included' ); +} + +bar(); \ No newline at end of file diff --git a/test/form/skips-dead-branches-c/_expected/iife.js b/test/form/skips-dead-branches-c/_expected/iife.js new file mode 100644 index 0000000..8d6dcb5 --- /dev/null +++ b/test/form/skips-dead-branches-c/_expected/iife.js @@ -0,0 +1,10 @@ +(function () { + 'use strict'; + + function bar () { + console.log( 'this should be included' ); + } + + bar(); + +}()); \ No newline at end of file diff --git a/test/form/skips-dead-branches-c/_expected/umd.js b/test/form/skips-dead-branches-c/_expected/umd.js new file mode 100644 index 0000000..67588d4 --- /dev/null +++ b/test/form/skips-dead-branches-c/_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 bar () { + console.log( 'this should be included' ); + } + + bar(); + +}))); \ No newline at end of file diff --git a/test/function/skips-dead-branches-c/main.js b/test/form/skips-dead-branches-c/main.js similarity index 100% rename from test/function/skips-dead-branches-c/main.js rename to test/form/skips-dead-branches-c/main.js diff --git a/test/form/skips-dead-branches-d/_config.js b/test/form/skips-dead-branches-d/_config.js new file mode 100644 index 0000000..9779139 --- /dev/null +++ b/test/form/skips-dead-branches-d/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'skips a dead branch (d)' +}; diff --git a/test/form/skips-dead-branches-d/_expected/amd.js b/test/form/skips-dead-branches-d/_expected/amd.js new file mode 100644 index 0000000..9dc50f5 --- /dev/null +++ b/test/form/skips-dead-branches-d/_expected/amd.js @@ -0,0 +1,9 @@ +define(function () { 'use strict'; + + function bar () { + console.log( 'this should be included' ); + } + + bar(); + +}); \ No newline at end of file diff --git a/test/form/skips-dead-branches-d/_expected/cjs.js b/test/form/skips-dead-branches-d/_expected/cjs.js new file mode 100644 index 0000000..d6aa952 --- /dev/null +++ b/test/form/skips-dead-branches-d/_expected/cjs.js @@ -0,0 +1,7 @@ +'use strict'; + +function bar () { + console.log( 'this should be included' ); +} + +bar(); \ No newline at end of file diff --git a/test/form/skips-dead-branches-d/_expected/es.js b/test/form/skips-dead-branches-d/_expected/es.js new file mode 100644 index 0000000..f39bfec --- /dev/null +++ b/test/form/skips-dead-branches-d/_expected/es.js @@ -0,0 +1,5 @@ +function bar () { + console.log( 'this should be included' ); +} + +bar(); \ No newline at end of file diff --git a/test/form/skips-dead-branches-d/_expected/iife.js b/test/form/skips-dead-branches-d/_expected/iife.js new file mode 100644 index 0000000..8d6dcb5 --- /dev/null +++ b/test/form/skips-dead-branches-d/_expected/iife.js @@ -0,0 +1,10 @@ +(function () { + 'use strict'; + + function bar () { + console.log( 'this should be included' ); + } + + bar(); + +}()); \ No newline at end of file diff --git a/test/form/skips-dead-branches-d/_expected/umd.js b/test/form/skips-dead-branches-d/_expected/umd.js new file mode 100644 index 0000000..67588d4 --- /dev/null +++ b/test/form/skips-dead-branches-d/_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 bar () { + console.log( 'this should be included' ); + } + + bar(); + +}))); \ No newline at end of file diff --git a/test/function/skips-dead-branches-d/main.js b/test/form/skips-dead-branches-d/main.js similarity index 100% rename from test/function/skips-dead-branches-d/main.js rename to test/form/skips-dead-branches-d/main.js diff --git a/test/form/skips-dead-branches-e/_config.js b/test/form/skips-dead-branches-e/_config.js new file mode 100644 index 0000000..d290c80 --- /dev/null +++ b/test/form/skips-dead-branches-e/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'skips a dead branch (e)' +}; diff --git a/test/form/skips-dead-branches-e/_expected/amd.js b/test/form/skips-dead-branches-e/_expected/amd.js new file mode 100644 index 0000000..9dc50f5 --- /dev/null +++ b/test/form/skips-dead-branches-e/_expected/amd.js @@ -0,0 +1,9 @@ +define(function () { 'use strict'; + + function bar () { + console.log( 'this should be included' ); + } + + bar(); + +}); \ No newline at end of file diff --git a/test/form/skips-dead-branches-e/_expected/cjs.js b/test/form/skips-dead-branches-e/_expected/cjs.js new file mode 100644 index 0000000..d6aa952 --- /dev/null +++ b/test/form/skips-dead-branches-e/_expected/cjs.js @@ -0,0 +1,7 @@ +'use strict'; + +function bar () { + console.log( 'this should be included' ); +} + +bar(); \ No newline at end of file diff --git a/test/form/skips-dead-branches-e/_expected/es.js b/test/form/skips-dead-branches-e/_expected/es.js new file mode 100644 index 0000000..f39bfec --- /dev/null +++ b/test/form/skips-dead-branches-e/_expected/es.js @@ -0,0 +1,5 @@ +function bar () { + console.log( 'this should be included' ); +} + +bar(); \ No newline at end of file diff --git a/test/form/skips-dead-branches-e/_expected/iife.js b/test/form/skips-dead-branches-e/_expected/iife.js new file mode 100644 index 0000000..8d6dcb5 --- /dev/null +++ b/test/form/skips-dead-branches-e/_expected/iife.js @@ -0,0 +1,10 @@ +(function () { + 'use strict'; + + function bar () { + console.log( 'this should be included' ); + } + + bar(); + +}()); \ No newline at end of file diff --git a/test/form/skips-dead-branches-e/_expected/umd.js b/test/form/skips-dead-branches-e/_expected/umd.js new file mode 100644 index 0000000..67588d4 --- /dev/null +++ b/test/form/skips-dead-branches-e/_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 bar () { + console.log( 'this should be included' ); + } + + bar(); + +}))); \ No newline at end of file diff --git a/test/function/skips-dead-branches-e/main.js b/test/form/skips-dead-branches-e/main.js similarity index 100% rename from test/function/skips-dead-branches-e/main.js rename to test/form/skips-dead-branches-e/main.js diff --git a/test/form/skips-dead-branches-f/_config.js b/test/form/skips-dead-branches-f/_config.js new file mode 100644 index 0000000..9ac1d23 --- /dev/null +++ b/test/form/skips-dead-branches-f/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'skips a dead branch (f)' +}; diff --git a/test/form/skips-dead-branches-f/_expected/amd.js b/test/form/skips-dead-branches-f/_expected/amd.js new file mode 100644 index 0000000..9dc50f5 --- /dev/null +++ b/test/form/skips-dead-branches-f/_expected/amd.js @@ -0,0 +1,9 @@ +define(function () { 'use strict'; + + function bar () { + console.log( 'this should be included' ); + } + + bar(); + +}); \ No newline at end of file diff --git a/test/form/skips-dead-branches-f/_expected/cjs.js b/test/form/skips-dead-branches-f/_expected/cjs.js new file mode 100644 index 0000000..d6aa952 --- /dev/null +++ b/test/form/skips-dead-branches-f/_expected/cjs.js @@ -0,0 +1,7 @@ +'use strict'; + +function bar () { + console.log( 'this should be included' ); +} + +bar(); \ No newline at end of file diff --git a/test/form/skips-dead-branches-f/_expected/es.js b/test/form/skips-dead-branches-f/_expected/es.js new file mode 100644 index 0000000..f39bfec --- /dev/null +++ b/test/form/skips-dead-branches-f/_expected/es.js @@ -0,0 +1,5 @@ +function bar () { + console.log( 'this should be included' ); +} + +bar(); \ No newline at end of file diff --git a/test/form/skips-dead-branches-f/_expected/iife.js b/test/form/skips-dead-branches-f/_expected/iife.js new file mode 100644 index 0000000..8d6dcb5 --- /dev/null +++ b/test/form/skips-dead-branches-f/_expected/iife.js @@ -0,0 +1,10 @@ +(function () { + 'use strict'; + + function bar () { + console.log( 'this should be included' ); + } + + bar(); + +}()); \ No newline at end of file diff --git a/test/form/skips-dead-branches-f/_expected/umd.js b/test/form/skips-dead-branches-f/_expected/umd.js new file mode 100644 index 0000000..67588d4 --- /dev/null +++ b/test/form/skips-dead-branches-f/_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 bar () { + console.log( 'this should be included' ); + } + + bar(); + +}))); \ No newline at end of file diff --git a/test/function/skips-dead-branches-f/main.js b/test/form/skips-dead-branches-f/main.js similarity index 100% rename from test/function/skips-dead-branches-f/main.js rename to test/form/skips-dead-branches-f/main.js diff --git a/test/form/skips-dead-branches-g/_config.js b/test/form/skips-dead-branches-g/_config.js new file mode 100644 index 0000000..f592e05 --- /dev/null +++ b/test/form/skips-dead-branches-g/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'skips a dead conditional expression branch (g)' +}; diff --git a/test/form/skips-dead-branches-g/_expected/amd.js b/test/form/skips-dead-branches-g/_expected/amd.js new file mode 100644 index 0000000..41371dd --- /dev/null +++ b/test/form/skips-dead-branches-g/_expected/amd.js @@ -0,0 +1,10 @@ +define(function () { 'use strict'; + + var a = 0; + var b = 1; + var x = a; + var y = b; + + console.log( x + y ); + +}); \ No newline at end of file diff --git a/test/form/skips-dead-branches-g/_expected/cjs.js b/test/form/skips-dead-branches-g/_expected/cjs.js new file mode 100644 index 0000000..44cffbf --- /dev/null +++ b/test/form/skips-dead-branches-g/_expected/cjs.js @@ -0,0 +1,8 @@ +'use strict'; + +var a = 0; +var b = 1; +var x = a; +var y = b; + +console.log( x + y ); \ No newline at end of file diff --git a/test/form/skips-dead-branches-g/_expected/es.js b/test/form/skips-dead-branches-g/_expected/es.js new file mode 100644 index 0000000..93d5198 --- /dev/null +++ b/test/form/skips-dead-branches-g/_expected/es.js @@ -0,0 +1,6 @@ +var a = 0; +var b = 1; +var x = a; +var y = b; + +console.log( x + y ); \ No newline at end of file diff --git a/test/form/skips-dead-branches-g/_expected/iife.js b/test/form/skips-dead-branches-g/_expected/iife.js new file mode 100644 index 0000000..ce654f4 --- /dev/null +++ b/test/form/skips-dead-branches-g/_expected/iife.js @@ -0,0 +1,11 @@ +(function () { + 'use strict'; + + var a = 0; + var b = 1; + var x = a; + var y = b; + + console.log( x + y ); + +}()); \ No newline at end of file diff --git a/test/form/skips-dead-branches-g/_expected/umd.js b/test/form/skips-dead-branches-g/_expected/umd.js new file mode 100644 index 0000000..40a0a54 --- /dev/null +++ b/test/form/skips-dead-branches-g/_expected/umd.js @@ -0,0 +1,14 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + var a = 0; + var b = 1; + var x = a; + var y = b; + + console.log( x + y ); + +}))); \ No newline at end of file diff --git a/test/form/skips-dead-branches-g/main.js b/test/form/skips-dead-branches-g/main.js new file mode 100644 index 0000000..352481e --- /dev/null +++ b/test/form/skips-dead-branches-g/main.js @@ -0,0 +1,8 @@ +var a = 0; +var b = 1; +var c = 2; + +var x = true ? a : b; +var y = false ? c : b; + +console.log( x + y ); diff --git a/test/form/skips-dead-branches/_config.js b/test/form/skips-dead-branches/_config.js new file mode 100644 index 0000000..078856d --- /dev/null +++ b/test/form/skips-dead-branches/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'skips a dead branch' +}; diff --git a/test/form/skips-dead-branches/_expected/amd.js b/test/form/skips-dead-branches/_expected/amd.js new file mode 100644 index 0000000..9dc50f5 --- /dev/null +++ b/test/form/skips-dead-branches/_expected/amd.js @@ -0,0 +1,9 @@ +define(function () { 'use strict'; + + function bar () { + console.log( 'this should be included' ); + } + + bar(); + +}); \ No newline at end of file diff --git a/test/form/skips-dead-branches/_expected/cjs.js b/test/form/skips-dead-branches/_expected/cjs.js new file mode 100644 index 0000000..d6aa952 --- /dev/null +++ b/test/form/skips-dead-branches/_expected/cjs.js @@ -0,0 +1,7 @@ +'use strict'; + +function bar () { + console.log( 'this should be included' ); +} + +bar(); \ No newline at end of file diff --git a/test/form/skips-dead-branches/_expected/es.js b/test/form/skips-dead-branches/_expected/es.js new file mode 100644 index 0000000..f39bfec --- /dev/null +++ b/test/form/skips-dead-branches/_expected/es.js @@ -0,0 +1,5 @@ +function bar () { + console.log( 'this should be included' ); +} + +bar(); \ No newline at end of file diff --git a/test/form/skips-dead-branches/_expected/iife.js b/test/form/skips-dead-branches/_expected/iife.js new file mode 100644 index 0000000..8d6dcb5 --- /dev/null +++ b/test/form/skips-dead-branches/_expected/iife.js @@ -0,0 +1,10 @@ +(function () { + 'use strict'; + + function bar () { + console.log( 'this should be included' ); + } + + bar(); + +}()); \ No newline at end of file diff --git a/test/form/skips-dead-branches/_expected/umd.js b/test/form/skips-dead-branches/_expected/umd.js new file mode 100644 index 0000000..67588d4 --- /dev/null +++ b/test/form/skips-dead-branches/_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 bar () { + console.log( 'this should be included' ); + } + + bar(); + +}))); \ No newline at end of file diff --git a/test/function/skips-dead-branches/main.js b/test/form/skips-dead-branches/main.js similarity index 100% rename from test/function/skips-dead-branches/main.js rename to test/form/skips-dead-branches/main.js diff --git a/test/form/string-indentation-b/_expected/amd.js b/test/form/string-indentation-b/_expected/amd.js index f646cb2..2eb44c9 100644 --- a/test/form/string-indentation-b/_expected/amd.js +++ b/test/form/string-indentation-b/_expected/amd.js @@ -2,7 +2,8 @@ define(function () { 'use strict'; var a = 'a'; var b = 'b'; + assert.equal( a, 'a' ); assert.equal( b, 'b' ); -}); \ No newline at end of file +}); diff --git a/test/form/string-indentation-b/_expected/cjs.js b/test/form/string-indentation-b/_expected/cjs.js index 7c799d3..8432e6e 100644 --- a/test/form/string-indentation-b/_expected/cjs.js +++ b/test/form/string-indentation-b/_expected/cjs.js @@ -2,5 +2,6 @@ var a = 'a'; var b = 'b'; + assert.equal( a, 'a' ); -assert.equal( b, 'b' ); \ No newline at end of file +assert.equal( b, 'b' ); diff --git a/test/form/string-indentation-b/_expected/es.js b/test/form/string-indentation-b/_expected/es.js index 7ab432c..6cd3b5a 100644 --- a/test/form/string-indentation-b/_expected/es.js +++ b/test/form/string-indentation-b/_expected/es.js @@ -1,4 +1,5 @@ var a = 'a'; var b = 'b'; + assert.equal( a, 'a' ); -assert.equal( b, 'b' ); \ No newline at end of file +assert.equal( b, 'b' ); diff --git a/test/form/string-indentation-b/_expected/iife.js b/test/form/string-indentation-b/_expected/iife.js index ae8c969..6907cdb 100644 --- a/test/form/string-indentation-b/_expected/iife.js +++ b/test/form/string-indentation-b/_expected/iife.js @@ -3,7 +3,8 @@ var a = 'a'; var b = 'b'; + assert.equal( a, 'a' ); assert.equal( b, 'b' ); -}()); \ No newline at end of file +}()); diff --git a/test/form/string-indentation-b/_expected/umd.js b/test/form/string-indentation-b/_expected/umd.js index 9ebd756..47597be 100644 --- a/test/form/string-indentation-b/_expected/umd.js +++ b/test/form/string-indentation-b/_expected/umd.js @@ -6,7 +6,8 @@ var a = 'a'; var b = 'b'; + assert.equal( a, 'a' ); assert.equal( b, 'b' ); -}))); \ No newline at end of file +}))); diff --git a/test/function/consistent-renaming-b/_config.js b/test/function/consistent-renaming-b/_config.js index 1b8b173..f1b3ee6 100644 --- a/test/function/consistent-renaming-b/_config.js +++ b/test/function/consistent-renaming-b/_config.js @@ -1,3 +1,3 @@ module.exports = { description: 'consistent renaming test b' -}; \ No newline at end of file +}; diff --git a/test/function/consistent-renaming-b/altdir/two.js b/test/function/consistent-renaming-b/altdir/two.js index b97aab5..bd84240 100644 --- a/test/function/consistent-renaming-b/altdir/two.js +++ b/test/function/consistent-renaming-b/altdir/two.js @@ -1,5 +1,6 @@ function two () { + // imported as _two by subdir/two.js return 2; } -export { two }; \ No newline at end of file +export { two }; diff --git a/test/function/consistent-renaming-b/subdir/one.js b/test/function/consistent-renaming-b/subdir/one.js index 0d1985f..26a060c 100644 --- a/test/function/consistent-renaming-b/subdir/one.js +++ b/test/function/consistent-renaming-b/subdir/one.js @@ -1,5 +1,5 @@ import { two } from '../altdir/two'; export default function one () { - return two() - 1; + return two() - 1; } diff --git a/test/function/consistent-renaming-b/subdir/two.js b/test/function/consistent-renaming-b/subdir/two.js index b0004b7..c31c2ff 100644 --- a/test/function/consistent-renaming-b/subdir/two.js +++ b/test/function/consistent-renaming-b/subdir/two.js @@ -1,5 +1,6 @@ import { two as _two } from '../altdir/two'; export default function two () { - return _two(); + // imported as Two by main.js + return _two(); } diff --git a/test/function/cycles-pathological/_config.js b/test/function/cycles-pathological/_config.js index 8d44d9a..e833d4f 100644 --- a/test/function/cycles-pathological/_config.js +++ b/test/function/cycles-pathological/_config.js @@ -1,17 +1,10 @@ var assert = require( 'assert' ); -var warned; - module.exports = { description: 'resolves pathological cyclical dependencies gracefully', buble: true, - options: { - onwarn: function ( message ) { - assert.ok( /Module .+B\.js may be unable to evaluate without .+A\.js, but is included first due to a cyclical dependency. Consider swapping the import statements in .+main\.js to ensure correct ordering/.test( message ) ); - warned = true; - } - }, - runtimeError: function () { - assert.ok( warned ); + warnings: warnings => { + assert.equal( warnings.length, warnings ); + assert.ok( /Module .+B\.js may be unable to evaluate without .+A\.js, but is included first due to a cyclical dependency. Consider swapping the import statements in .+main\.js to ensure correct ordering/.test( warnings[0] ) ); } }; diff --git a/test/function/iife-strong-dependencies/_config.js b/test/function/iife-strong-dependencies/_config.js index f1d7ee1..475e611 100644 --- a/test/function/iife-strong-dependencies/_config.js +++ b/test/function/iife-strong-dependencies/_config.js @@ -1,16 +1,9 @@ var assert = require( 'assert' ); -var warned; - module.exports = { description: 'does not treat references inside IIFEs as weak dependencies', // edge case encountered in THREE.js codebase - options: { - onwarn: function ( message ) { - assert.ok( /Module .+D\.js may be unable to evaluate without .+C\.js, but is included first due to a cyclical dependency. Consider swapping the import statements in .+main\.js to ensure correct ordering/.test( message ) ); - warned = true; - } - }, - runtimeError: function () { - assert.ok( warned ); + warnings: warnings => { + assert.equal( warnings.length, 1 ); + assert.ok( /Module .+D\.js may be unable to evaluate without .+C\.js, but is included first due to a cyclical dependency. Consider swapping the import statements in .+main\.js to ensure correct ordering/.test( warnings[0] ) ); } }; diff --git a/test/function/import-dependency-in-same-module/_config.js b/test/function/import-dependency-in-same-module/_config.js index 622d7d9..a08ff3c 100644 --- a/test/function/import-dependency-in-same-module/_config.js +++ b/test/function/import-dependency-in-same-module/_config.js @@ -1,3 +1,3 @@ module.exports = { description: 'imports a dependency from the same module' -}; \ No newline at end of file +}; diff --git a/test/function/no-imports/_config.js b/test/function/no-imports/_config.js index 47ae3de..be6c448 100644 --- a/test/function/no-imports/_config.js +++ b/test/function/no-imports/_config.js @@ -1,3 +1,3 @@ module.exports = { description: 'creates a bundle from a module with no imports' -}; \ No newline at end of file +}; diff --git a/test/function/reassign-import-fails/_config.js b/test/function/reassign-import-fails/_config.js index e01d090..22591fe 100644 --- a/test/function/reassign-import-fails/_config.js +++ b/test/function/reassign-import-fails/_config.js @@ -4,9 +4,9 @@ var assert = require( 'assert' ); module.exports = { description: 'disallows assignments to imported bindings', error: function ( err ) { + assert.ok( /Illegal reassignment/.test( err.message ) ); assert.equal( path.normalize(err.file), path.resolve( __dirname, 'main.js' ) ); assert.deepEqual( err.loc, { line: 8, column: 0 }); - assert.ok( /Illegal reassignment/.test( err.message ) ); } }; diff --git a/test/function/skips-dead-branches-b/_config.js b/test/function/skips-dead-branches-b/_config.js deleted file mode 100644 index 9833915..0000000 --- a/test/function/skips-dead-branches-b/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -var assert = require( 'assert' ); - -module.exports = { - description: 'skips a dead branch (b)', - code: function ( code ) { - assert.equal( code.indexOf( 'obj.foo = function' ), -1, code ); - } -}; diff --git a/test/function/skips-dead-branches-c/_config.js b/test/function/skips-dead-branches-c/_config.js deleted file mode 100644 index c925750..0000000 --- a/test/function/skips-dead-branches-c/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -var assert = require( 'assert' ); - -module.exports = { - description: 'skips a dead branch (c)', - code: function ( code ) { - assert.equal( code.indexOf( 'obj.foo = function' ), -1, code ); - } -}; diff --git a/test/function/skips-dead-branches-d/_config.js b/test/function/skips-dead-branches-d/_config.js deleted file mode 100644 index 67c8046..0000000 --- a/test/function/skips-dead-branches-d/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -var assert = require( 'assert' ); - -module.exports = { - description: 'skips a dead branch (d)', - code: function ( code ) { - assert.equal( code.indexOf( 'obj.foo = function' ), -1, code ); - } -}; diff --git a/test/function/skips-dead-branches-e/_config.js b/test/function/skips-dead-branches-e/_config.js deleted file mode 100644 index 5c6e6ab..0000000 --- a/test/function/skips-dead-branches-e/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -var assert = require( 'assert' ); - -module.exports = { - description: 'skips a dead branch (e)', - code: function ( code ) { - assert.equal( code.indexOf( 'obj.foo = function' ), -1, code ); - } -}; diff --git a/test/function/skips-dead-branches-f/_config.js b/test/function/skips-dead-branches-f/_config.js deleted file mode 100644 index d829cef..0000000 --- a/test/function/skips-dead-branches-f/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -var assert = require( 'assert' ); - -module.exports = { - description: 'skips a dead branch (f)', - code: function ( code ) { - assert.equal( code.indexOf( 'obj.foo = function' ), -1, code ); - } -}; diff --git a/test/function/skips-dead-branches-g/_config.js b/test/function/skips-dead-branches-g/_config.js deleted file mode 100644 index 90e7f11..0000000 --- a/test/function/skips-dead-branches-g/_config.js +++ /dev/null @@ -1,9 +0,0 @@ -var assert = require( 'assert' ); - -module.exports = { - description: 'skips a dead conditional expression branch (g)', - code: function ( code ) { - assert.ok( code.indexOf( 'var c = a;' ) >= 0, code ); - assert.ok( code.indexOf( 'var d = b;' ) >= 0, code ); - } -}; diff --git a/test/function/skips-dead-branches-g/main.js b/test/function/skips-dead-branches-g/main.js deleted file mode 100644 index 0c271f4..0000000 --- a/test/function/skips-dead-branches-g/main.js +++ /dev/null @@ -1,6 +0,0 @@ -var a = 0; -var b = 1; -var c = true ? a : b; -var d = false ? a : b; - -console.log( c + d ); diff --git a/test/function/skips-dead-branches/_config.js b/test/function/skips-dead-branches/_config.js deleted file mode 100644 index 9a0d225..0000000 --- a/test/function/skips-dead-branches/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -var assert = require( 'assert' ); - -module.exports = { - description: 'skips a dead branch', - code: function ( code ) { - assert.equal( code.indexOf( 'obj.foo = function' ), -1, code ); - } -}; diff --git a/test/function/tracks-alias-mutations/bar.js b/test/function/tracks-alias-mutations/bar.js index 4ec5140..88a92e5 100644 --- a/test/function/tracks-alias-mutations/bar.js +++ b/test/function/tracks-alias-mutations/bar.js @@ -1,6 +1,8 @@ import { foo } from './foo'; -var f = foo; -f.wasMutated = true; +var f = Math.random() <= 1 ? foo : {}; +var f2; +f2 = Math.random() <= 1 ? f : {}; +f2.wasMutated = true; export var bar = 'whatever'; diff --git a/test/test.js b/test/test.js index 92a88f0..615742f 100644 --- a/test/test.js +++ b/test/test.js @@ -243,16 +243,16 @@ describe( 'rollup', function () { } } + if ( config.show || unintendedError ) { + console.log( result.code + '\n\n\n' ); + } + if ( config.warnings ) { config.warnings( warnings ); } else if ( warnings.length ) { throw new Error( `Got unexpected warnings:\n${warnings.join('\n')}` ); } - if ( config.show || unintendedError ) { - console.log( code + '\n\n\n' ); - } - if ( config.solo ) console.groupEnd(); if ( unintendedError ) throw unintendedError; @@ -291,11 +291,12 @@ describe( 'rollup', function () { }, config.options ); ( config.skip ? describe.skip : config.solo ? describe.only : describe )( dir, () => { - const promise = rollup.rollup( options ); + let promise; + const createBundle = () => ( promise || ( promise = rollup.rollup( options ) ) ); PROFILES.forEach( profile => { it( 'generates ' + profile.format, () => { - return promise.then( bundle => { + return createBundle().then( bundle => { const options = extend( {}, config.options, { dest: FORM + '/' + dir + '/_actual/' + profile.format + '.js', format: profile.format From 9ded29e9ec1933708d496c5e6494af5434c0f40d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 6 Sep 2016 09:38:56 -0400 Subject: [PATCH 15/99] add test for #901 --- test/form/side-effect-p/_config.js | 3 +++ test/form/side-effect-p/_expected/amd.js | 11 +++++++++++ test/form/side-effect-p/_expected/cjs.js | 9 +++++++++ test/form/side-effect-p/_expected/es.js | 7 +++++++ test/form/side-effect-p/_expected/iife.js | 12 ++++++++++++ test/form/side-effect-p/_expected/umd.js | 15 +++++++++++++++ test/form/side-effect-p/bool.js | 1 + test/form/side-effect-p/main.js | 7 +++++++ 8 files changed, 65 insertions(+) create mode 100644 test/form/side-effect-p/_config.js create mode 100644 test/form/side-effect-p/_expected/amd.js create mode 100644 test/form/side-effect-p/_expected/cjs.js create mode 100644 test/form/side-effect-p/_expected/es.js create mode 100644 test/form/side-effect-p/_expected/iife.js create mode 100644 test/form/side-effect-p/_expected/umd.js create mode 100644 test/form/side-effect-p/bool.js create mode 100644 test/form/side-effect-p/main.js diff --git a/test/form/side-effect-p/_config.js b/test/form/side-effect-p/_config.js new file mode 100644 index 0000000..7a8d84b --- /dev/null +++ b/test/form/side-effect-p/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'detects mutation of globals' +}; diff --git a/test/form/side-effect-p/_expected/amd.js b/test/form/side-effect-p/_expected/amd.js new file mode 100644 index 0000000..35e8591 --- /dev/null +++ b/test/form/side-effect-p/_expected/amd.js @@ -0,0 +1,11 @@ +define(function () { 'use strict'; + + var bool = true; + + const hs = document.documentElement.style; + + if ( bool ) { + hs.color = "#222" + } + +}); \ No newline at end of file diff --git a/test/form/side-effect-p/_expected/cjs.js b/test/form/side-effect-p/_expected/cjs.js new file mode 100644 index 0000000..08dc383 --- /dev/null +++ b/test/form/side-effect-p/_expected/cjs.js @@ -0,0 +1,9 @@ +'use strict'; + +var bool = true; + +const hs = document.documentElement.style; + +if ( bool ) { + hs.color = "#222" +} \ No newline at end of file diff --git a/test/form/side-effect-p/_expected/es.js b/test/form/side-effect-p/_expected/es.js new file mode 100644 index 0000000..8e80199 --- /dev/null +++ b/test/form/side-effect-p/_expected/es.js @@ -0,0 +1,7 @@ +var bool = true; + +const hs = document.documentElement.style; + +if ( bool ) { + hs.color = "#222" +} \ No newline at end of file diff --git a/test/form/side-effect-p/_expected/iife.js b/test/form/side-effect-p/_expected/iife.js new file mode 100644 index 0000000..4ea2e35 --- /dev/null +++ b/test/form/side-effect-p/_expected/iife.js @@ -0,0 +1,12 @@ +(function () { + 'use strict'; + + var bool = true; + + const hs = document.documentElement.style; + + if ( bool ) { + hs.color = "#222" + } + +}()); \ No newline at end of file diff --git a/test/form/side-effect-p/_expected/umd.js b/test/form/side-effect-p/_expected/umd.js new file mode 100644 index 0000000..f99ce83 --- /dev/null +++ b/test/form/side-effect-p/_expected/umd.js @@ -0,0 +1,15 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + var bool = true; + + const hs = document.documentElement.style; + + if ( bool ) { + hs.color = "#222" + } + +}))); \ No newline at end of file diff --git a/test/form/side-effect-p/bool.js b/test/form/side-effect-p/bool.js new file mode 100644 index 0000000..ff3177b --- /dev/null +++ b/test/form/side-effect-p/bool.js @@ -0,0 +1 @@ +export default true; diff --git a/test/form/side-effect-p/main.js b/test/form/side-effect-p/main.js new file mode 100644 index 0000000..1ee7791 --- /dev/null +++ b/test/form/side-effect-p/main.js @@ -0,0 +1,7 @@ +import bool from './bool'; + +const hs = document.documentElement.style; + +if ( bool ) { + hs.color = "#222" +} From c096eb4b26d8aef573f657e97642f8fb1ae795ce Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 6 Sep 2016 09:46:54 -0400 Subject: [PATCH 16/99] test for #898 --- test/form/includes-all-namespace-declarations/_config.js | 4 ++++ .../includes-all-namespace-declarations/_expected/amd.js | 5 +++++ .../includes-all-namespace-declarations/_expected/cjs.js | 2 ++ .../includes-all-namespace-declarations/_expected/es.js | 0 .../_expected/iife.js | 6 ++++++ .../includes-all-namespace-declarations/_expected/umd.js | 9 +++++++++ .../includes-all-namespace-declarations/indirection.js | 7 +++++++ test/form/includes-all-namespace-declarations/main.js | 1 + test/form/includes-all-namespace-declarations/unused.js | 3 +++ 9 files changed, 37 insertions(+) create mode 100644 test/form/includes-all-namespace-declarations/_config.js create mode 100644 test/form/includes-all-namespace-declarations/_expected/amd.js create mode 100644 test/form/includes-all-namespace-declarations/_expected/cjs.js create mode 100644 test/form/includes-all-namespace-declarations/_expected/es.js create mode 100644 test/form/includes-all-namespace-declarations/_expected/iife.js create mode 100644 test/form/includes-all-namespace-declarations/_expected/umd.js create mode 100644 test/form/includes-all-namespace-declarations/indirection.js create mode 100644 test/form/includes-all-namespace-declarations/main.js create mode 100644 test/form/includes-all-namespace-declarations/unused.js diff --git a/test/form/includes-all-namespace-declarations/_config.js b/test/form/includes-all-namespace-declarations/_config.js new file mode 100644 index 0000000..66bd7c5 --- /dev/null +++ b/test/form/includes-all-namespace-declarations/_config.js @@ -0,0 +1,4 @@ +module.exports = { + solo: true, + description: 'includes all declarations referenced by reified namespaces' +} diff --git a/test/form/includes-all-namespace-declarations/_expected/amd.js b/test/form/includes-all-namespace-declarations/_expected/amd.js new file mode 100644 index 0000000..ec759b1 --- /dev/null +++ b/test/form/includes-all-namespace-declarations/_expected/amd.js @@ -0,0 +1,5 @@ +define(function () { 'use strict'; + + + +}); \ No newline at end of file diff --git a/test/form/includes-all-namespace-declarations/_expected/cjs.js b/test/form/includes-all-namespace-declarations/_expected/cjs.js new file mode 100644 index 0000000..eb109ab --- /dev/null +++ b/test/form/includes-all-namespace-declarations/_expected/cjs.js @@ -0,0 +1,2 @@ +'use strict'; + diff --git a/test/form/includes-all-namespace-declarations/_expected/es.js b/test/form/includes-all-namespace-declarations/_expected/es.js new file mode 100644 index 0000000..e69de29 diff --git a/test/form/includes-all-namespace-declarations/_expected/iife.js b/test/form/includes-all-namespace-declarations/_expected/iife.js new file mode 100644 index 0000000..f3d1016 --- /dev/null +++ b/test/form/includes-all-namespace-declarations/_expected/iife.js @@ -0,0 +1,6 @@ +(function () { + 'use strict'; + + + +}()); \ No newline at end of file diff --git a/test/form/includes-all-namespace-declarations/_expected/umd.js b/test/form/includes-all-namespace-declarations/_expected/umd.js new file mode 100644 index 0000000..d561e69 --- /dev/null +++ b/test/form/includes-all-namespace-declarations/_expected/umd.js @@ -0,0 +1,9 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + + +}))); \ No newline at end of file diff --git a/test/form/includes-all-namespace-declarations/indirection.js b/test/form/includes-all-namespace-declarations/indirection.js new file mode 100644 index 0000000..d98c18c --- /dev/null +++ b/test/form/includes-all-namespace-declarations/indirection.js @@ -0,0 +1,7 @@ +import * as unused from './unused.js'; + +var indirection = { + unused: unused +}; + +export { indirection }; diff --git a/test/form/includes-all-namespace-declarations/main.js b/test/form/includes-all-namespace-declarations/main.js new file mode 100644 index 0000000..0b45d9e --- /dev/null +++ b/test/form/includes-all-namespace-declarations/main.js @@ -0,0 +1 @@ +import { indirection } from './indirection.js'; diff --git a/test/form/includes-all-namespace-declarations/unused.js b/test/form/includes-all-namespace-declarations/unused.js new file mode 100644 index 0000000..b0ff512 --- /dev/null +++ b/test/form/includes-all-namespace-declarations/unused.js @@ -0,0 +1,3 @@ +function foo () {} + +export { foo }; From 0eecb7d5097039f022a78c51ea1b55a30e9b6dc9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 6 Sep 2016 09:49:41 -0400 Subject: [PATCH 17/99] re-enable all tests --- test/form/includes-all-namespace-declarations/_config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/form/includes-all-namespace-declarations/_config.js b/test/form/includes-all-namespace-declarations/_config.js index 66bd7c5..30ffc5e 100644 --- a/test/form/includes-all-namespace-declarations/_config.js +++ b/test/form/includes-all-namespace-declarations/_config.js @@ -1,4 +1,3 @@ module.exports = { - solo: true, description: 'includes all declarations referenced by reified namespaces' } From 37d8950d99e56f150c7e7f1a3d2294e9b9904a78 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 6 Sep 2016 09:50:50 -0400 Subject: [PATCH 18/99] skip module order tests for now --- test/function/cycles-pathological/_config.js | 1 + test/function/iife-strong-dependencies/_config.js | 1 + 2 files changed, 2 insertions(+) diff --git a/test/function/cycles-pathological/_config.js b/test/function/cycles-pathological/_config.js index e833d4f..f6d8526 100644 --- a/test/function/cycles-pathological/_config.js +++ b/test/function/cycles-pathological/_config.js @@ -1,6 +1,7 @@ var assert = require( 'assert' ); module.exports = { + skip: true, description: 'resolves pathological cyclical dependencies gracefully', buble: true, warnings: warnings => { diff --git a/test/function/iife-strong-dependencies/_config.js b/test/function/iife-strong-dependencies/_config.js index 475e611..110fa77 100644 --- a/test/function/iife-strong-dependencies/_config.js +++ b/test/function/iife-strong-dependencies/_config.js @@ -1,6 +1,7 @@ var assert = require( 'assert' ); module.exports = { + skip: true, description: 'does not treat references inside IIFEs as weak dependencies', // edge case encountered in THREE.js codebase warnings: warnings => { assert.equal( warnings.length, 1 ); From ad4ca750a59be6a710d322b9a7372dc631907c6a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 6 Sep 2016 10:08:04 -0400 Subject: [PATCH 19/99] linting --- .eslintrc | 5 ++++ src/ast/nodes/AssignmentExpression.js | 2 +- src/ast/nodes/ClassDeclaration.js | 2 +- src/ast/nodes/ExportAllDeclaration.js | 4 ++-- src/ast/nodes/ExportDefaultDeclaration.js | 28 ----------------------- src/ast/nodes/FunctionDeclaration.js | 2 +- src/ast/nodes/shared/isUsedByBundle.js | 12 ++-------- src/utils/path.js | 2 +- 8 files changed, 13 insertions(+), 44 deletions(-) diff --git a/.eslintrc b/.eslintrc index 9fded74..d9150f9 100644 --- a/.eslintrc +++ b/.eslintrc @@ -35,5 +35,10 @@ "parserOptions": { "ecmaVersion": 6, "sourceType": "module" + }, + "settings": { + "import/ignore": [ 0, [ + "\\.path.js$" + ] ] } } diff --git a/src/ast/nodes/AssignmentExpression.js b/src/ast/nodes/AssignmentExpression.js index dd3347d..b8dc0e2 100644 --- a/src/ast/nodes/AssignmentExpression.js +++ b/src/ast/nodes/AssignmentExpression.js @@ -1,7 +1,7 @@ import Node from '../Node.js'; import disallowIllegalReassignment from './shared/disallowIllegalReassignment.js'; import isUsedByBundle from './shared/isUsedByBundle.js'; -import { NUMBER, STRING, UNKNOWN } from '../values.js'; +import { NUMBER, STRING } from '../values.js'; export default class AssignmentExpression extends Node { bind ( scope ) { diff --git a/src/ast/nodes/ClassDeclaration.js b/src/ast/nodes/ClassDeclaration.js index a004b9f..ed773da 100644 --- a/src/ast/nodes/ClassDeclaration.js +++ b/src/ast/nodes/ClassDeclaration.js @@ -9,7 +9,7 @@ export default class ClassDeclaration extends Node { this.body.run(); } - addReference ( reference ) { + addReference () { /* noop? */ } diff --git a/src/ast/nodes/ExportAllDeclaration.js b/src/ast/nodes/ExportAllDeclaration.js index fe929b6..a0f5308 100644 --- a/src/ast/nodes/ExportAllDeclaration.js +++ b/src/ast/nodes/ExportAllDeclaration.js @@ -1,11 +1,11 @@ import Node from '../Node.js'; export default class ExportAllDeclaration extends Node { - initialise ( scope ) { + initialise () { this.isExportDeclaration = true; } - render ( code, es ) { + render ( code ) { code.remove( this.leadingCommentStart || this.start, this.next || this.end ); } } diff --git a/src/ast/nodes/ExportDefaultDeclaration.js b/src/ast/nodes/ExportDefaultDeclaration.js index d65d675..716ffcf 100644 --- a/src/ast/nodes/ExportDefaultDeclaration.js +++ b/src/ast/nodes/ExportDefaultDeclaration.js @@ -2,34 +2,6 @@ import Node from '../Node.js'; const functionOrClassDeclaration = /^(?:Function|Class)Declaration/; -class SyntheticDefaultDeclaration { - constructor ( node, name ) { - this.node = node; - this.name = name; - this.isDefault = true; - } - - activate () { - if ( this.activated ) return; - this.activated = true; - - this.node.run(); - } - - addReference ( reference ) { - this.name = reference.name; - if ( this.original ) this.original.addReference( reference ); - } - - render ( es ) { - if ( this.original && !this.original.isReassigned ) { - return this.original.getName( es ); - } - - return this.name; - } -} - export default class ExportDefaultDeclaration extends Node { initialise ( scope ) { this.isExportDeclaration = true; diff --git a/src/ast/nodes/FunctionDeclaration.js b/src/ast/nodes/FunctionDeclaration.js index 50e93bf..96cba0c 100644 --- a/src/ast/nodes/FunctionDeclaration.js +++ b/src/ast/nodes/FunctionDeclaration.js @@ -10,7 +10,7 @@ export default class FunctionDeclaration extends Node { this.body.run(); } - addReference ( reference ) { + addReference () { /* noop? */ } diff --git a/src/ast/nodes/shared/isUsedByBundle.js b/src/ast/nodes/shared/isUsedByBundle.js index a294c0c..07e315f 100644 --- a/src/ast/nodes/shared/isUsedByBundle.js +++ b/src/ast/nodes/shared/isUsedByBundle.js @@ -1,17 +1,9 @@ -import { - ARRAY, - BOOLEAN, - FUNCTION, - NUMBER, - OBJECT, - STRING, - UNKNOWN -} from '../../values.js'; +import { UNKNOWN } from '../../values.js'; export default function isUsedByBundle ( scope, node ) { while ( node.type === 'ParenthesizedExpression' ) node = node.expression; - const expression = node; + // const expression = node; while ( node.type === 'MemberExpression' ) node = node.object; const declaration = scope.findDeclaration( node.name ); diff --git a/src/utils/path.js b/src/utils/path.js index b787028..d2ce64d 100644 --- a/src/utils/path.js +++ b/src/utils/path.js @@ -13,4 +13,4 @@ export function normalize ( path ) { return path.replace( /\\/g, '/' ); } -export * from 'path'; +export { basename, dirname, extname, relative, resolve } from 'path'; From 2148516fad2a8911ba8a99d47ef3ec5f40f7b91e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 6 Sep 2016 10:13:56 -0400 Subject: [PATCH 20/99] remove 0.12 from build matrix, it doesnt matter any more --- .travis.yml | 1 - appveyor.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 83d1bca..956a81b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ sudo: false language: node_js node_js: - - "0.12" - "4" - "6" env: diff --git a/appveyor.yml b/appveyor.yml index a4389cb..8ab6c7d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,7 +10,6 @@ init: environment: matrix: # node.js - - nodejs_version: 0.12 - nodejs_version: 4 - nodejs_version: 6 From db0dc75f030b6451977e3437a61c738f74860948 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 6 Sep 2016 10:34:56 -0400 Subject: [PATCH 21/99] parse as ES7 --- src/Module.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module.js b/src/Module.js index d6a418f..205b38b 100644 --- a/src/Module.js +++ b/src/Module.js @@ -13,7 +13,7 @@ import ModuleScope from './ast/scopes/ModuleScope.js'; function tryParse ( code, comments, acornOptions, id ) { try { return parse( code, assign({ - ecmaVersion: 6, + ecmaVersion: 7, sourceType: 'module', onComment: ( block, text, start, end ) => comments.push({ block, text, start, end }), preserveParens: true From 83ccb9725374e0fde9d07043959c397b15d26c67 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 6 Sep 2016 17:10:48 -0400 Subject: [PATCH 22/99] clone ASTs, for fast incremental rebuilds --- src/Module.js | 6 +++--- src/utils/object.js | 21 +++++++++++++++++++++ test/test.js | 16 ++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/Module.js b/src/Module.js index 205b38b..fc3ebd1 100644 --- a/src/Module.js +++ b/src/Module.js @@ -1,6 +1,6 @@ import { parse } from 'acorn/src/index.js'; import MagicString from 'magic-string'; -import { assign, blank, keys } from './utils/object.js'; +import { assign, blank, deepClone, keys } from './utils/object.js'; import { basename, extname } from './utils/path.js'; import getLocation from './utils/getLocation.js'; import makeLegalIdentifier from './utils/makeLegalIdentifier.js'; @@ -35,6 +35,7 @@ export default class Module { this.comments = []; this.ast = ast || tryParse( code, this.comments, bundle.acornOptions, id ); // TODO what happens to comments if AST is provided? + this.astClone = deepClone( this.ast ); this.bundle = bundle; this.id = id; @@ -316,8 +317,7 @@ export default class Module { id: this.id, code: this.code, originalCode: this.originalCode, - // TODO reinstate AST caching (rewrite broke it, because AST is enhanced) - // ast: this.ast, + ast: this.astClone, sourceMapChain: this.sourceMapChain, resolvedIds: this.resolvedIds }; diff --git a/src/utils/object.js b/src/utils/object.js index 4234de3..947c8e1 100644 --- a/src/utils/object.js +++ b/src/utils/object.js @@ -17,3 +17,24 @@ export function assign ( target, ...sources ) { return target; } + +const isArray = Array.isArray; + +// used for cloning ASTs. Not for use with cyclical structures! +export function deepClone ( obj ) { + if ( !obj ) return obj; + if ( typeof obj !== 'object' ) return obj; + + if ( isArray( obj ) ) { + const clone = new Array( obj.length ); + for ( let i = 0; i < obj.length; i += 1 ) clone[i] = deepClone( obj[i] ); + return clone; + } + + const clone = {}; + for ( const key in obj ) { + clone[ key ] = deepClone( obj[ key ] ); + } + + return clone; +} diff --git a/test/test.js b/test/test.js index 615742f..9697e6e 100644 --- a/test/test.js +++ b/test/test.js @@ -6,6 +6,7 @@ const sander = require( 'sander' ); const assert = require( 'assert' ); const { exec } = require( 'child_process' ); const buble = require( 'buble' ); +const acorn = require( 'acorn' ); const rollup = require( '../dist/rollup' ); const FUNCTION = path.resolve( __dirname, 'function' ); @@ -587,6 +588,21 @@ describe( 'rollup', function () { assert.equal( executeBundle( bundle ), 21 ); }); }); + + it( 'keeps ASTs between runs', () => { + return rollup.rollup({ + entry: 'entry', + plugins: [ plugin ] + }).then( bundle => { + const asts = {}; + bundle.modules.forEach( module => { + asts[ module.id ] = module.ast; + }); + + assert.deepEqual( asts.entry, acorn.parse( modules.entry, { sourceType: 'module' }) ); + assert.deepEqual( asts.foo, acorn.parse( modules.foo, { sourceType: 'module' }) ); + }); + }); }); describe( 'hooks', () => { From ccb28cf56b5649edc4c2b5e26f500cc0d4b303ef Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 6 Sep 2016 17:14:26 -0400 Subject: [PATCH 23/99] add test for #893 --- test/function/tracks-alias-mutations-b/_config.js | 13 +++++++++++++ test/function/tracks-alias-mutations-b/main.js | 2 ++ 2 files changed, 15 insertions(+) create mode 100644 test/function/tracks-alias-mutations-b/_config.js create mode 100644 test/function/tracks-alias-mutations-b/main.js diff --git a/test/function/tracks-alias-mutations-b/_config.js b/test/function/tracks-alias-mutations-b/_config.js new file mode 100644 index 0000000..dde47e2 --- /dev/null +++ b/test/function/tracks-alias-mutations-b/_config.js @@ -0,0 +1,13 @@ +const assert = require( 'assert' ); + +const foo = {}; + +module.exports = { + description: 'tracks mutations of aliased objects', + context: { + foo + }, + exports () { + assert.equal( foo.x, 42 ); + } +}; diff --git a/test/function/tracks-alias-mutations-b/main.js b/test/function/tracks-alias-mutations-b/main.js new file mode 100644 index 0000000..81ef7f7 --- /dev/null +++ b/test/function/tracks-alias-mutations-b/main.js @@ -0,0 +1,2 @@ +var _foo = foo; +_foo.x = 42; From 8ecbd7853920f357b937aafd5d262d08eb32e16a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 6 Sep 2016 17:23:55 -0400 Subject: [PATCH 24/99] enable previously failing test for #733 --- test/function/export-two-ways-default-b/_config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/function/export-two-ways-default-b/_config.js b/test/function/export-two-ways-default-b/_config.js index 79fd012..4fa4888 100644 --- a/test/function/export-two-ways-default-b/_config.js +++ b/test/function/export-two-ways-default-b/_config.js @@ -1,4 +1,3 @@ module.exports = { - skip: true, description: 'side-effects are preserved if subject is exported in multiple ways, even if default export has no direct link to original (#733)' }; From 7e67b8b273c09051be17c90cd09f77eb21c88780 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 6 Sep 2016 17:34:35 -0400 Subject: [PATCH 25/99] test for #669 --- test/form/unused-var/_config.js | 3 +++ test/form/unused-var/_expected/amd.js | 7 +++++++ test/form/unused-var/_expected/cjs.js | 5 +++++ test/form/unused-var/_expected/es.js | 3 +++ test/form/unused-var/_expected/iife.js | 8 ++++++++ test/form/unused-var/_expected/umd.js | 11 +++++++++++ test/form/unused-var/foo.js | 6 ++++++ test/form/unused-var/main.js | 2 ++ 8 files changed, 45 insertions(+) create mode 100644 test/form/unused-var/_config.js create mode 100644 test/form/unused-var/_expected/amd.js create mode 100644 test/form/unused-var/_expected/cjs.js create mode 100644 test/form/unused-var/_expected/es.js create mode 100644 test/form/unused-var/_expected/iife.js create mode 100644 test/form/unused-var/_expected/umd.js create mode 100644 test/form/unused-var/foo.js create mode 100644 test/form/unused-var/main.js diff --git a/test/form/unused-var/_config.js b/test/form/unused-var/_config.js new file mode 100644 index 0000000..b89eb86 --- /dev/null +++ b/test/form/unused-var/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'omits unused var declaration' +}; diff --git a/test/form/unused-var/_expected/amd.js b/test/form/unused-var/_expected/amd.js new file mode 100644 index 0000000..babb23a --- /dev/null +++ b/test/form/unused-var/_expected/amd.js @@ -0,0 +1,7 @@ +define(function () { 'use strict'; + + var foo = 'lol'; + + console.log( foo ); + +}); \ No newline at end of file diff --git a/test/form/unused-var/_expected/cjs.js b/test/form/unused-var/_expected/cjs.js new file mode 100644 index 0000000..8628267 --- /dev/null +++ b/test/form/unused-var/_expected/cjs.js @@ -0,0 +1,5 @@ +'use strict'; + +var foo = 'lol'; + +console.log( foo ); \ No newline at end of file diff --git a/test/form/unused-var/_expected/es.js b/test/form/unused-var/_expected/es.js new file mode 100644 index 0000000..2e13c75 --- /dev/null +++ b/test/form/unused-var/_expected/es.js @@ -0,0 +1,3 @@ +var foo = 'lol'; + +console.log( foo ); \ No newline at end of file diff --git a/test/form/unused-var/_expected/iife.js b/test/form/unused-var/_expected/iife.js new file mode 100644 index 0000000..a9b45df --- /dev/null +++ b/test/form/unused-var/_expected/iife.js @@ -0,0 +1,8 @@ +(function () { + 'use strict'; + + var foo = 'lol'; + + console.log( foo ); + +}()); \ No newline at end of file diff --git a/test/form/unused-var/_expected/umd.js b/test/form/unused-var/_expected/umd.js new file mode 100644 index 0000000..71ba44f --- /dev/null +++ b/test/form/unused-var/_expected/umd.js @@ -0,0 +1,11 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + var foo = 'lol'; + + console.log( foo ); + +}))); \ No newline at end of file diff --git a/test/form/unused-var/foo.js b/test/form/unused-var/foo.js new file mode 100644 index 0000000..3b60a87 --- /dev/null +++ b/test/form/unused-var/foo.js @@ -0,0 +1,6 @@ +var foo = 'lol'; +var bar = 'wut'; + +var baz = bar || foo; + +export { foo }; diff --git a/test/form/unused-var/main.js b/test/form/unused-var/main.js new file mode 100644 index 0000000..1b3e409 --- /dev/null +++ b/test/form/unused-var/main.js @@ -0,0 +1,2 @@ +import { foo } from './foo.js'; +console.log( foo ); From 0913247901b566af2e8e73ef60e5d0cb26b107d6 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Tue, 6 Sep 2016 20:19:35 -0400 Subject: [PATCH 26/99] transpile acorn for now, pending new release --- rollup.config.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rollup.config.js b/rollup.config.js index a8ac2c8..f55dc7c 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -22,7 +22,11 @@ export default { entry: 'src/rollup.js', plugins: [ buble({ - include: [ 'src/**', 'node_modules/acorn/**' ], + include: [ 'node_modules/acorn/**' ] + }), + + buble({ + include: [ 'src/**' ], target: { node: 4 } From 92f55845468ae28db2e3b3a761382ba1229f36f6 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 7 Sep 2016 07:30:53 -0400 Subject: [PATCH 27/99] add browser/cli watch tasks --- package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0eabb8c..394a4ae 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,11 @@ "posttest-coverage": "remap-istanbul -i coverage/coverage-final.json -o coverage/coverage-remapped.json -b dist && remap-istanbul -i coverage/coverage-final.json -o coverage/coverage-remapped.lcov -t lcovonly -b dist && remap-istanbul -i coverage/coverage-final.json -o coverage/coverage-remapped -t html -b dist", "ci": "npm run test-coverage && codecov < coverage/coverage-remapped.lcov", "build": "git rev-parse HEAD > .commithash && rollup -c", - "watch": "rollup -c -w", "build:cli": "rollup -c rollup.config.cli.js", - "build:browser": "git rev-parse HEAD > .commithash && rollup -c rollup.config.browser.js -o dist/rollup.browser.js", + "build:browser": "git rev-parse HEAD > .commithash && rollup -c rollup.config.browser.js", + "watch": "rollup -c -w", + "watch:browser": "rollup -c rollup.config.browser.js -w", + "watch:cli": "rollup -c rollup.config.cli.js -w", "prepublish": "npm run lint && npm test && npm run build:browser", "lint": "eslint src browser test/test.js test/utils test/**/_config.js" }, From 4e96e95def2ad0ffe3bf0025cd3d0ceff7b03b5f Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 7 Sep 2016 07:31:26 -0400 Subject: [PATCH 28/99] configure entry in rollup.config.browser.js --- rollup.config.browser.js | 1 + 1 file changed, 1 insertion(+) diff --git a/rollup.config.browser.js b/rollup.config.browser.js index 687f257..e77e4c6 100644 --- a/rollup.config.browser.js +++ b/rollup.config.browser.js @@ -9,5 +9,6 @@ config.plugins.push({ }); config.format = 'umd'; +config.entry = 'dist/rollup.browser.js'; export default config; From 22951539647b8b098ad61ec6fbc26fa2084e7a11 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 7 Sep 2016 07:33:02 -0400 Subject: [PATCH 29/99] handle updates of parameters --- src/ast/nodes/UpdateExpression.js | 5 ++++- test/function/reassign-parameter/_config.js | 3 +++ test/function/reassign-parameter/main.js | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/function/reassign-parameter/_config.js create mode 100644 test/function/reassign-parameter/main.js diff --git a/src/ast/nodes/UpdateExpression.js b/src/ast/nodes/UpdateExpression.js index fc9d1ce..c5b9263 100644 --- a/src/ast/nodes/UpdateExpression.js +++ b/src/ast/nodes/UpdateExpression.js @@ -14,7 +14,10 @@ export default class UpdateExpression extends Node { if ( subject.type === 'Identifier' ) { const declaration = scope.findDeclaration( subject.name ); declaration.isReassigned = true; - declaration.possibleValues.add( NUMBER ); + + if ( declaration.possibleValues ) { + declaration.possibleValues.add( NUMBER ); + } } super.bind( scope ); diff --git a/test/function/reassign-parameter/_config.js b/test/function/reassign-parameter/_config.js new file mode 100644 index 0000000..1701a6f --- /dev/null +++ b/test/function/reassign-parameter/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'parameters can be reassigned/updated' +}; diff --git a/test/function/reassign-parameter/main.js b/test/function/reassign-parameter/main.js new file mode 100644 index 0000000..6c68061 --- /dev/null +++ b/test/function/reassign-parameter/main.js @@ -0,0 +1,7 @@ +function numbers ( i ) { + var array = new Array( i ); + while ( i-- ) array[i] = i + 1; + return array; +} + +assert.deepEqual( numbers( 5 ), [ 1, 2, 3, 4, 5 ] ); From cf1ac32a5339b6ed8f3b40b653c36442f7461199 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 7 Sep 2016 08:23:06 -0400 Subject: [PATCH 30/99] dont separate vars in for loop head --- src/ast/nodes/VariableDeclaration.js | 20 +++++++++++++++---- test/form/side-effect-k/_expected/amd.js | 3 +-- test/form/side-effect-k/_expected/cjs.js | 3 +-- test/form/side-effect-k/_expected/es.js | 3 +-- test/form/side-effect-k/_expected/iife.js | 3 +-- test/form/side-effect-k/_expected/umd.js | 3 +-- .../function/vars-in-for-loop-head/_config.js | 3 +++ test/function/vars-in-for-loop-head/main.js | 10 ++++++++++ 8 files changed, 34 insertions(+), 14 deletions(-) create mode 100644 test/function/vars-in-for-loop-head/_config.js create mode 100644 test/function/vars-in-for-loop-head/main.js diff --git a/src/ast/nodes/VariableDeclaration.js b/src/ast/nodes/VariableDeclaration.js index 5ce99e9..767aab2 100644 --- a/src/ast/nodes/VariableDeclaration.js +++ b/src/ast/nodes/VariableDeclaration.js @@ -15,9 +15,21 @@ function getSeparator ( code, start ) { } export default class VariableDeclaration extends Node { + initialise ( scope ) { + this.scope = scope; + super.initialise( scope ); + } + render ( code, es ) { const treeshake = this.module.bundle.treeshake; - const separator = this.declarations.length ? getSeparator( this.module.code, this.start ) : ''; + + let shouldSeparate = false; + let separator; + + if ( this.scope.isModuleScope && !/forStatement/.test( this.parent.type ) ) { + shouldSeparate = true; + separator = getSeparator( this.module.code, this.start ); + } let c = this.start; let empty = true; @@ -33,12 +45,12 @@ export default class VariableDeclaration extends Node { if ( isExportedAndReassigned ) { if ( declarator.init ) { - code.overwrite( c, declarator.start, prefix ); + if ( shouldSeparate ) code.overwrite( c, declarator.start, prefix ); c = declarator.end; empty = false; } } else if ( !treeshake || proxy.activated ) { - code.overwrite( c, declarator.start, `${prefix}${this.kind} ` ); // TODO indentation + if ( shouldSeparate ) code.overwrite( c, declarator.start, `${prefix}${this.kind} ` ); // TODO indentation c = declarator.end; empty = false; } @@ -63,7 +75,7 @@ export default class VariableDeclaration extends Node { }); if ( !treeshake || activated ) { - code.overwrite( c, declarator.start, `${prefix}${this.kind} ` ); // TODO indentation + if ( shouldSeparate ) code.overwrite( c, declarator.start, `${prefix}${this.kind} ` ); // TODO indentation c = declarator.end; empty = false; } diff --git a/test/form/side-effect-k/_expected/amd.js b/test/form/side-effect-k/_expected/amd.js index 9ce8a48..abfcb98 100644 --- a/test/form/side-effect-k/_expected/amd.js +++ b/test/form/side-effect-k/_expected/amd.js @@ -1,8 +1,7 @@ define(function () { 'use strict'; function augment ( x ) { - var prop; - var source; + var prop, source; var i = arguments.length; var sources = Array( i - 1 ); diff --git a/test/form/side-effect-k/_expected/cjs.js b/test/form/side-effect-k/_expected/cjs.js index 77a3fa7..8219021 100644 --- a/test/form/side-effect-k/_expected/cjs.js +++ b/test/form/side-effect-k/_expected/cjs.js @@ -1,8 +1,7 @@ 'use strict'; function augment ( x ) { - var prop; - var source; + var prop, source; var i = arguments.length; var sources = Array( i - 1 ); diff --git a/test/form/side-effect-k/_expected/es.js b/test/form/side-effect-k/_expected/es.js index 726addd..8647986 100644 --- a/test/form/side-effect-k/_expected/es.js +++ b/test/form/side-effect-k/_expected/es.js @@ -1,6 +1,5 @@ function augment ( x ) { - var prop; - var source; + var prop, source; var i = arguments.length; var sources = Array( i - 1 ); diff --git a/test/form/side-effect-k/_expected/iife.js b/test/form/side-effect-k/_expected/iife.js index 6a6255f..eeb57d4 100644 --- a/test/form/side-effect-k/_expected/iife.js +++ b/test/form/side-effect-k/_expected/iife.js @@ -2,8 +2,7 @@ var myBundle = (function () { 'use strict'; function augment ( x ) { - var prop; - var source; + var prop, source; var i = arguments.length; var sources = Array( i - 1 ); diff --git a/test/form/side-effect-k/_expected/umd.js b/test/form/side-effect-k/_expected/umd.js index f6fff9f..302275a 100644 --- a/test/form/side-effect-k/_expected/umd.js +++ b/test/form/side-effect-k/_expected/umd.js @@ -5,8 +5,7 @@ }(this, (function () { 'use strict'; function augment ( x ) { - var prop; - var source; + var prop, source; var i = arguments.length; var sources = Array( i - 1 ); diff --git a/test/function/vars-in-for-loop-head/_config.js b/test/function/vars-in-for-loop-head/_config.js new file mode 100644 index 0000000..166d6ee --- /dev/null +++ b/test/function/vars-in-for-loop-head/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'does not break apart vars in for loop head' +}; diff --git a/test/function/vars-in-for-loop-head/main.js b/test/function/vars-in-for-loop-head/main.js new file mode 100644 index 0000000..0b35f91 --- /dev/null +++ b/test/function/vars-in-for-loop-head/main.js @@ -0,0 +1,10 @@ +function clone ( things ) { + var result = []; + for ( var i = 0, list = things; i < list.length; i += 1 ) { + var thing = list[i]; + result.push( thing ); + } + return result; +} + +assert.deepEqual( clone([ 1, 2, 3 ]), [ 1, 2, 3 ] ); From fe3647815175c59cd3cefd733fcea046a679bc05 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 7 Sep 2016 08:29:41 -0400 Subject: [PATCH 31/99] do less work at end --- src/Bundle.js | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/Bundle.js b/src/Bundle.js index 9e5f010..881907a 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -130,28 +130,22 @@ export default class Bundle { while ( !settled ) { settled = true; - for ( const expression of this.dependentExpressions ) { - if ( expression.isUsedByBundle() ) { - const statement = expression.findParent( /ExpressionStatement/ ); - - if ( statement && !statement.ran ) { - settled = false; - statement.run( statement.findScope() ); - } + let i = this.dependentExpressions.length; + while ( i-- ) { + const expression = this.dependentExpressions[i]; + const statement = expression.findParent( /ExpressionStatement/ ); + + if ( !statement || statement.ran ) { + this.dependentExpressions.splice( i, 1 ); + } else if ( expression.isUsedByBundle() ) { + settled = false; + statement.run( statement.findScope() ); + this.dependentExpressions.splice( i, 1 ); } } } } - // let settled = false; - // while ( !settled ) { - // settled = true; - // - // this.modules.forEach( module => { - // if ( module.run( this.treeshake ) ) settled = false; - // }); - // } - // Phase 4 – final preparation. We order the modules with an // enhanced topological sort that accounts for cycles, then // ensure that names are deconflicted throughout the bundle From 1abcb17388acc49379cf27ba69a0eeee762ed500 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 7 Sep 2016 08:49:57 -0400 Subject: [PATCH 32/99] oops --- rollup.config.browser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollup.config.browser.js b/rollup.config.browser.js index e77e4c6..95977b4 100644 --- a/rollup.config.browser.js +++ b/rollup.config.browser.js @@ -9,6 +9,6 @@ config.plugins.push({ }); config.format = 'umd'; -config.entry = 'dist/rollup.browser.js'; +config.dest = 'dist/rollup.browser.js'; export default config; From 7df2f00b57869f6cd247f191b59f8a614b08e65f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 7 Sep 2016 17:59:58 -0400 Subject: [PATCH 33/99] add test for #716 but skip it for now --- test/form/duplicated-var-declarations/_config.js | 4 ++++ test/form/duplicated-var-declarations/main.js | 10 ++++++++++ 2 files changed, 14 insertions(+) create mode 100644 test/form/duplicated-var-declarations/_config.js create mode 100644 test/form/duplicated-var-declarations/main.js diff --git a/test/form/duplicated-var-declarations/_config.js b/test/form/duplicated-var-declarations/_config.js new file mode 100644 index 0000000..1dd69da --- /dev/null +++ b/test/form/duplicated-var-declarations/_config.js @@ -0,0 +1,4 @@ +module.exports = { + skip: true, + description: 'does not remove duplicated var declarations (#716)' +}; diff --git a/test/form/duplicated-var-declarations/main.js b/test/form/duplicated-var-declarations/main.js new file mode 100644 index 0000000..1eabb5b --- /dev/null +++ b/test/form/duplicated-var-declarations/main.js @@ -0,0 +1,10 @@ +var a = 1, b = 2; + +assert.equal( a, 1 ); +assert.equal( b, 2 ); + +var a = 3, b = 4, c = 5; + +assert.equal( a, 3 ); +assert.equal( b, 4 ); +assert.equal( c, 5 ); From beac2e6eb1032d6c1f9b4f20e070bdb5c878022b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 7 Sep 2016 18:00:12 -0400 Subject: [PATCH 34/99] fix regex --- src/ast/nodes/VariableDeclaration.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ast/nodes/VariableDeclaration.js b/src/ast/nodes/VariableDeclaration.js index 767aab2..e1be1a0 100644 --- a/src/ast/nodes/VariableDeclaration.js +++ b/src/ast/nodes/VariableDeclaration.js @@ -14,6 +14,8 @@ function getSeparator ( code, start ) { return `;\n${lineStart}`; } +const forStatement = /^For(?:Of|In)Statement/; + export default class VariableDeclaration extends Node { initialise ( scope ) { this.scope = scope; @@ -26,7 +28,7 @@ export default class VariableDeclaration extends Node { let shouldSeparate = false; let separator; - if ( this.scope.isModuleScope && !/forStatement/.test( this.parent.type ) ) { + if ( this.scope.isModuleScope && !forStatement.test( this.parent.type ) ) { shouldSeparate = true; separator = getSeparator( this.module.code, this.start ); } From 34a13a74d0ff9414c27a55358090a580f6537ac9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 7 Sep 2016 18:03:39 -0400 Subject: [PATCH 35/99] use a set to track which functions are being called --- src/ast/nodes/shared/callHasEffects.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ast/nodes/shared/callHasEffects.js b/src/ast/nodes/shared/callHasEffects.js index 74bf0ba..9a36717 100644 --- a/src/ast/nodes/shared/callHasEffects.js +++ b/src/ast/nodes/shared/callHasEffects.js @@ -3,9 +3,11 @@ import isReference from '../../utils/isReference.js'; import pureFunctions from './pureFunctions.js'; import { UNKNOWN } from '../../values.js'; +const currentlyCalling = new Set(); + function fnHasEffects ( fn ) { - if ( fn._calling ) return true; // prevent infinite loops... TODO there must be a better way - fn._calling = true; + if ( currentlyCalling.has( fn ) ) return true; // prevent infinite loops... TODO there must be a better way + currentlyCalling.add( fn ); // handle body-less arrow functions const scope = fn.body.scope || fn.scope; @@ -13,12 +15,12 @@ function fnHasEffects ( fn ) { for ( const node of body ) { if ( node.hasEffects( scope ) ) { - fn._calling = false; + currentlyCalling.delete( fn ); return true; } } - fn._calling = false; + currentlyCalling.delete( fn ); return false; } From 5e1e5f99c51c718cb3b4c1e4ce4e84189e9f8d54 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 7 Sep 2016 20:34:35 -0400 Subject: [PATCH 36/99] dont assume recursive function calls have side-effects --- src/ast/nodes/shared/callHasEffects.js | 2 +- .../_config.js | 3 +++ .../_expected/amd.js | 20 ++++++++++++++++ .../_expected/cjs.js | 18 ++++++++++++++ .../_expected/es.js | 16 +++++++++++++ .../_expected/iife.js | 21 ++++++++++++++++ .../_expected/umd.js | 24 +++++++++++++++++++ .../main.js | 16 +++++++++++++ test/form/self-calling-function/_config.js | 3 +++ .../self-calling-function/_expected/amd.js | 5 ++++ .../self-calling-function/_expected/cjs.js | 2 ++ .../self-calling-function/_expected/es.js | 0 .../self-calling-function/_expected/iife.js | 6 +++++ .../self-calling-function/_expected/umd.js | 9 +++++++ test/form/self-calling-function/main.js | 14 +++++++++++ 15 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 test/form/self-calling-function-with-effects/_config.js create mode 100644 test/form/self-calling-function-with-effects/_expected/amd.js create mode 100644 test/form/self-calling-function-with-effects/_expected/cjs.js create mode 100644 test/form/self-calling-function-with-effects/_expected/es.js create mode 100644 test/form/self-calling-function-with-effects/_expected/iife.js create mode 100644 test/form/self-calling-function-with-effects/_expected/umd.js create mode 100644 test/form/self-calling-function-with-effects/main.js create mode 100644 test/form/self-calling-function/_config.js create mode 100644 test/form/self-calling-function/_expected/amd.js create mode 100644 test/form/self-calling-function/_expected/cjs.js create mode 100644 test/form/self-calling-function/_expected/es.js create mode 100644 test/form/self-calling-function/_expected/iife.js create mode 100644 test/form/self-calling-function/_expected/umd.js create mode 100644 test/form/self-calling-function/main.js diff --git a/src/ast/nodes/shared/callHasEffects.js b/src/ast/nodes/shared/callHasEffects.js index 9a36717..cf8d964 100644 --- a/src/ast/nodes/shared/callHasEffects.js +++ b/src/ast/nodes/shared/callHasEffects.js @@ -6,7 +6,7 @@ import { UNKNOWN } from '../../values.js'; const currentlyCalling = new Set(); function fnHasEffects ( fn ) { - if ( currentlyCalling.has( fn ) ) return true; // prevent infinite loops... TODO there must be a better way + if ( currentlyCalling.has( fn ) ) return false; // prevent infinite loops... TODO there must be a better way currentlyCalling.add( fn ); // handle body-less arrow functions diff --git a/test/form/self-calling-function-with-effects/_config.js b/test/form/self-calling-function-with-effects/_config.js new file mode 100644 index 0000000..d20f89b --- /dev/null +++ b/test/form/self-calling-function-with-effects/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'discards a self-calling function with side-effects' +}; diff --git a/test/form/self-calling-function-with-effects/_expected/amd.js b/test/form/self-calling-function-with-effects/_expected/amd.js new file mode 100644 index 0000000..ad714bd --- /dev/null +++ b/test/form/self-calling-function-with-effects/_expected/amd.js @@ -0,0 +1,20 @@ +define(function () { 'use strict'; + + function foo ( x ) { + effect( x ); + if ( x > 0 ) foo( x - 1 ); + } + + function bar ( x ) { + effect( x ); + if ( x > 0 ) baz( x ); + } + + function baz ( x ) { + bar( x - 1 ); + } + + foo( 10 ); + bar( 10 ); + +}); \ No newline at end of file diff --git a/test/form/self-calling-function-with-effects/_expected/cjs.js b/test/form/self-calling-function-with-effects/_expected/cjs.js new file mode 100644 index 0000000..83a7a20 --- /dev/null +++ b/test/form/self-calling-function-with-effects/_expected/cjs.js @@ -0,0 +1,18 @@ +'use strict'; + +function foo ( x ) { + effect( x ); + if ( x > 0 ) foo( x - 1 ); +} + +function bar ( x ) { + effect( x ); + if ( x > 0 ) baz( x ); +} + +function baz ( x ) { + bar( x - 1 ); +} + +foo( 10 ); +bar( 10 ); \ No newline at end of file diff --git a/test/form/self-calling-function-with-effects/_expected/es.js b/test/form/self-calling-function-with-effects/_expected/es.js new file mode 100644 index 0000000..8e997b2 --- /dev/null +++ b/test/form/self-calling-function-with-effects/_expected/es.js @@ -0,0 +1,16 @@ +function foo ( x ) { + effect( x ); + if ( x > 0 ) foo( x - 1 ); +} + +function bar ( x ) { + effect( x ); + if ( x > 0 ) baz( x ); +} + +function baz ( x ) { + bar( x - 1 ); +} + +foo( 10 ); +bar( 10 ); \ No newline at end of file diff --git a/test/form/self-calling-function-with-effects/_expected/iife.js b/test/form/self-calling-function-with-effects/_expected/iife.js new file mode 100644 index 0000000..bb848ad --- /dev/null +++ b/test/form/self-calling-function-with-effects/_expected/iife.js @@ -0,0 +1,21 @@ +(function () { + 'use strict'; + + function foo ( x ) { + effect( x ); + if ( x > 0 ) foo( x - 1 ); + } + + function bar ( x ) { + effect( x ); + if ( x > 0 ) baz( x ); + } + + function baz ( x ) { + bar( x - 1 ); + } + + foo( 10 ); + bar( 10 ); + +}()); \ No newline at end of file diff --git a/test/form/self-calling-function-with-effects/_expected/umd.js b/test/form/self-calling-function-with-effects/_expected/umd.js new file mode 100644 index 0000000..a9fd5b2 --- /dev/null +++ b/test/form/self-calling-function-with-effects/_expected/umd.js @@ -0,0 +1,24 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + function foo ( x ) { + effect( x ); + if ( x > 0 ) foo( x - 1 ); + } + + function bar ( x ) { + effect( x ); + if ( x > 0 ) baz( x ); + } + + function baz ( x ) { + bar( x - 1 ); + } + + foo( 10 ); + bar( 10 ); + +}))); \ No newline at end of file diff --git a/test/form/self-calling-function-with-effects/main.js b/test/form/self-calling-function-with-effects/main.js new file mode 100644 index 0000000..7ec54d9 --- /dev/null +++ b/test/form/self-calling-function-with-effects/main.js @@ -0,0 +1,16 @@ +function foo ( x ) { + effect( x ); + if ( x > 0 ) foo( x - 1 ); +} + +function bar ( x ) { + effect( x ); + if ( x > 0 ) baz( x ); +} + +function baz ( x ) { + bar( x - 1 ); +} + +foo( 10 ); +bar( 10 ); diff --git a/test/form/self-calling-function/_config.js b/test/form/self-calling-function/_config.js new file mode 100644 index 0000000..3acb040 --- /dev/null +++ b/test/form/self-calling-function/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'discards a self-calling function without side-effects' +}; diff --git a/test/form/self-calling-function/_expected/amd.js b/test/form/self-calling-function/_expected/amd.js new file mode 100644 index 0000000..ec759b1 --- /dev/null +++ b/test/form/self-calling-function/_expected/amd.js @@ -0,0 +1,5 @@ +define(function () { 'use strict'; + + + +}); \ No newline at end of file diff --git a/test/form/self-calling-function/_expected/cjs.js b/test/form/self-calling-function/_expected/cjs.js new file mode 100644 index 0000000..eb109ab --- /dev/null +++ b/test/form/self-calling-function/_expected/cjs.js @@ -0,0 +1,2 @@ +'use strict'; + diff --git a/test/form/self-calling-function/_expected/es.js b/test/form/self-calling-function/_expected/es.js new file mode 100644 index 0000000..e69de29 diff --git a/test/form/self-calling-function/_expected/iife.js b/test/form/self-calling-function/_expected/iife.js new file mode 100644 index 0000000..f3d1016 --- /dev/null +++ b/test/form/self-calling-function/_expected/iife.js @@ -0,0 +1,6 @@ +(function () { + 'use strict'; + + + +}()); \ No newline at end of file diff --git a/test/form/self-calling-function/_expected/umd.js b/test/form/self-calling-function/_expected/umd.js new file mode 100644 index 0000000..d561e69 --- /dev/null +++ b/test/form/self-calling-function/_expected/umd.js @@ -0,0 +1,9 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + + +}))); \ No newline at end of file diff --git a/test/form/self-calling-function/main.js b/test/form/self-calling-function/main.js new file mode 100644 index 0000000..76ff633 --- /dev/null +++ b/test/form/self-calling-function/main.js @@ -0,0 +1,14 @@ +function foo ( x ) { + if ( x > 0 ) foo( x - 1 ); +} + +function bar ( x ) { + if ( x > 0 ) baz( x ); +} + +function baz ( x ) { + bar( x - 1 ); +} + +foo( 10 ); +bar( 10 ); From 2e32f237774599848a6324c19971673a8447ec84 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 7 Sep 2016 21:34:12 -0400 Subject: [PATCH 37/99] fix double var declaration bug (#716) --- src/ast/nodes/VariableDeclarator.js | 2 ++ src/ast/scopes/Scope.js | 11 +++++++++- .../duplicated-var-declarations/_config.js | 1 - .../_expected/amd.js | 17 +++++++++++++++ .../_expected/cjs.js | 15 +++++++++++++ .../_expected/es.js | 13 ++++++++++++ .../_expected/iife.js | 18 ++++++++++++++++ .../_expected/umd.js | 21 +++++++++++++++++++ 8 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 test/form/duplicated-var-declarations/_expected/amd.js create mode 100644 test/form/duplicated-var-declarations/_expected/cjs.js create mode 100644 test/form/duplicated-var-declarations/_expected/es.js create mode 100644 test/form/duplicated-var-declarations/_expected/iife.js create mode 100644 test/form/duplicated-var-declarations/_expected/umd.js diff --git a/src/ast/nodes/VariableDeclarator.js b/src/ast/nodes/VariableDeclarator.js index 61ca51b..dc35bff 100644 --- a/src/ast/nodes/VariableDeclarator.js +++ b/src/ast/nodes/VariableDeclarator.js @@ -11,12 +11,14 @@ class DeclaratorProxy { this.isReassigned = false; this.exportName = null; + this.duplicates = []; this.possibleValues = new Set( init ? [ init ] : null ); } activate () { this.activated = true; this.declarator.activate(); + this.duplicates.forEach( dupe => dupe.activate() ); } addReference () { diff --git a/src/ast/scopes/Scope.js b/src/ast/scopes/Scope.js index c77e6d2..25512b7 100644 --- a/src/ast/scopes/Scope.js +++ b/src/ast/scopes/Scope.js @@ -1,3 +1,5 @@ +import getLocation from '../../utils/getLocation.js'; +import error from '../../utils/error.js'; import { blank, keys } from '../../utils/object.js'; import { UNKNOWN } from '../values.js'; @@ -49,7 +51,14 @@ export default class Scope { if ( isVar && this.isBlockScope ) { this.parent.addDeclaration( name, declaration, isVar, isParam ); } else { - this.declarations[ name ] = isParam ? new Parameter( name ) : declaration; + const existingDeclaration = this.declarations[ name ]; + + if ( existingDeclaration && existingDeclaration.duplicates ) { + // TODO warn/throw on duplicates? + existingDeclaration.duplicates.push( declaration ); + } else { + this.declarations[ name ] = isParam ? new Parameter( name ) : declaration; + } } } diff --git a/test/form/duplicated-var-declarations/_config.js b/test/form/duplicated-var-declarations/_config.js index 1dd69da..4c3038c 100644 --- a/test/form/duplicated-var-declarations/_config.js +++ b/test/form/duplicated-var-declarations/_config.js @@ -1,4 +1,3 @@ module.exports = { - skip: true, description: 'does not remove duplicated var declarations (#716)' }; diff --git a/test/form/duplicated-var-declarations/_expected/amd.js b/test/form/duplicated-var-declarations/_expected/amd.js new file mode 100644 index 0000000..b43e2a4 --- /dev/null +++ b/test/form/duplicated-var-declarations/_expected/amd.js @@ -0,0 +1,17 @@ +define(function () { 'use strict'; + + var a = 1; + var b = 2; + + assert.equal( a, 1 ); + assert.equal( b, 2 ); + + var a = 3; + var b = 4; + var c = 5; + + assert.equal( a, 3 ); + assert.equal( b, 4 ); + assert.equal( c, 5 ); + +}); diff --git a/test/form/duplicated-var-declarations/_expected/cjs.js b/test/form/duplicated-var-declarations/_expected/cjs.js new file mode 100644 index 0000000..e47b61a --- /dev/null +++ b/test/form/duplicated-var-declarations/_expected/cjs.js @@ -0,0 +1,15 @@ +'use strict'; + +var a = 1; +var b = 2; + +assert.equal( a, 1 ); +assert.equal( b, 2 ); + +var a = 3; +var b = 4; +var c = 5; + +assert.equal( a, 3 ); +assert.equal( b, 4 ); +assert.equal( c, 5 ); diff --git a/test/form/duplicated-var-declarations/_expected/es.js b/test/form/duplicated-var-declarations/_expected/es.js new file mode 100644 index 0000000..cacf6d2 --- /dev/null +++ b/test/form/duplicated-var-declarations/_expected/es.js @@ -0,0 +1,13 @@ +var a = 1; +var b = 2; + +assert.equal( a, 1 ); +assert.equal( b, 2 ); + +var a = 3; +var b = 4; +var c = 5; + +assert.equal( a, 3 ); +assert.equal( b, 4 ); +assert.equal( c, 5 ); diff --git a/test/form/duplicated-var-declarations/_expected/iife.js b/test/form/duplicated-var-declarations/_expected/iife.js new file mode 100644 index 0000000..106066a --- /dev/null +++ b/test/form/duplicated-var-declarations/_expected/iife.js @@ -0,0 +1,18 @@ +(function () { + 'use strict'; + + var a = 1; + var b = 2; + + assert.equal( a, 1 ); + assert.equal( b, 2 ); + + var a = 3; + var b = 4; + var c = 5; + + assert.equal( a, 3 ); + assert.equal( b, 4 ); + assert.equal( c, 5 ); + +}()); diff --git a/test/form/duplicated-var-declarations/_expected/umd.js b/test/form/duplicated-var-declarations/_expected/umd.js new file mode 100644 index 0000000..4574799 --- /dev/null +++ b/test/form/duplicated-var-declarations/_expected/umd.js @@ -0,0 +1,21 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + var a = 1; + var b = 2; + + assert.equal( a, 1 ); + assert.equal( b, 2 ); + + var a = 3; + var b = 4; + var c = 5; + + assert.equal( a, 3 ); + assert.equal( b, 4 ); + assert.equal( c, 5 ); + +}))); From ef676b4081a670d51d61ee8ef29009389e1915bc Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 8 Sep 2016 09:12:57 -0400 Subject: [PATCH 38/99] disregard effects in dead branches --- src/ast/nodes/ConditionalExpression.js | 12 +++++------ src/ast/nodes/IfStatement.js | 26 ++++------------------- test/form/side-effect-q/_config.js | 3 +++ test/form/side-effect-q/_expected/amd.js | 5 +++++ test/form/side-effect-q/_expected/cjs.js | 2 ++ test/form/side-effect-q/_expected/es.js | 0 test/form/side-effect-q/_expected/iife.js | 6 ++++++ test/form/side-effect-q/_expected/umd.js | 9 ++++++++ test/form/side-effect-q/main.js | 5 +++++ 9 files changed, 39 insertions(+), 29 deletions(-) create mode 100644 test/form/side-effect-q/_config.js create mode 100644 test/form/side-effect-q/_expected/amd.js create mode 100644 test/form/side-effect-q/_expected/cjs.js create mode 100644 test/form/side-effect-q/_expected/es.js create mode 100644 test/form/side-effect-q/_expected/iife.js create mode 100644 test/form/side-effect-q/_expected/umd.js create mode 100644 test/form/side-effect-q/main.js diff --git a/src/ast/nodes/ConditionalExpression.js b/src/ast/nodes/ConditionalExpression.js index 6a6c803..c5e336d 100644 --- a/src/ast/nodes/ConditionalExpression.js +++ b/src/ast/nodes/ConditionalExpression.js @@ -4,13 +4,13 @@ import { UNKNOWN } from '../values.js'; export default class ConditionalExpression extends Node { initialise ( scope ) { if ( this.module.bundle.treeshake ) { - const testValue = this.test.getValue(); + this.testValue = this.test.getValue(); - if ( testValue === UNKNOWN ) { + if ( this.testValue === UNKNOWN ) { super.initialise( scope ); } - else if ( testValue ) { + else if ( this.testValue ) { this.consequent.initialise( scope ); this.alternate = null; } else if ( this.alternate ) { @@ -47,13 +47,11 @@ export default class ConditionalExpression extends Node { } else { - const testValue = this.test.getValue(); - - if ( testValue === UNKNOWN ) { + if ( this.testValue === UNKNOWN ) { super.render( code, es ); } - else if ( testValue ) { + else if ( this.testValue ) { code.remove( this.start, this.consequent.start ); code.remove( this.consequent.end, this.end ); } else { diff --git a/src/ast/nodes/IfStatement.js b/src/ast/nodes/IfStatement.js index baccbd5..1a5c184 100644 --- a/src/ast/nodes/IfStatement.js +++ b/src/ast/nodes/IfStatement.js @@ -3,26 +3,6 @@ import { UNKNOWN } from '../values.js'; // TODO DRY this out export default class IfStatement extends Node { - bind ( scope ) { - if ( this.module.bundle.treeshake ) { - if ( this.testValue === UNKNOWN ) { - super.bind( scope ); - } - - else if ( this.testValue ) { - this.consequent.bind( scope ); - this.alternate = null; - } else if ( this.alternate ) { - this.alternate.bind( scope ); - this.consequent = null; - } - } - - else { - super.bind( scope ); - } - } - initialise ( scope ) { this.testValue = this.test.getValue(); @@ -33,8 +13,10 @@ export default class IfStatement extends Node { else if ( this.testValue ) { this.consequent.initialise( scope ); - } else if ( this.alternate ) { - this.alternate.initialise( scope ); + this.alternate = null; + } else { + if ( this.alternate ) this.alternate.initialise( scope ); + this.consequent = null; } } diff --git a/test/form/side-effect-q/_config.js b/test/form/side-effect-q/_config.js new file mode 100644 index 0000000..944a58a --- /dev/null +++ b/test/form/side-effect-q/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'discards effects in conditional expressions with known test values' +}; diff --git a/test/form/side-effect-q/_expected/amd.js b/test/form/side-effect-q/_expected/amd.js new file mode 100644 index 0000000..ec759b1 --- /dev/null +++ b/test/form/side-effect-q/_expected/amd.js @@ -0,0 +1,5 @@ +define(function () { 'use strict'; + + + +}); \ No newline at end of file diff --git a/test/form/side-effect-q/_expected/cjs.js b/test/form/side-effect-q/_expected/cjs.js new file mode 100644 index 0000000..eb109ab --- /dev/null +++ b/test/form/side-effect-q/_expected/cjs.js @@ -0,0 +1,2 @@ +'use strict'; + diff --git a/test/form/side-effect-q/_expected/es.js b/test/form/side-effect-q/_expected/es.js new file mode 100644 index 0000000..e69de29 diff --git a/test/form/side-effect-q/_expected/iife.js b/test/form/side-effect-q/_expected/iife.js new file mode 100644 index 0000000..f3d1016 --- /dev/null +++ b/test/form/side-effect-q/_expected/iife.js @@ -0,0 +1,6 @@ +(function () { + 'use strict'; + + + +}()); \ No newline at end of file diff --git a/test/form/side-effect-q/_expected/umd.js b/test/form/side-effect-q/_expected/umd.js new file mode 100644 index 0000000..d561e69 --- /dev/null +++ b/test/form/side-effect-q/_expected/umd.js @@ -0,0 +1,9 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + + +}))); \ No newline at end of file diff --git a/test/form/side-effect-q/main.js b/test/form/side-effect-q/main.js new file mode 100644 index 0000000..2272edb --- /dev/null +++ b/test/form/side-effect-q/main.js @@ -0,0 +1,5 @@ +var x = true ? foo () : bar(); + +function foo () { + return 'should be removed, because x is unused'; +} From 95106fa770b19cff7b4f574079ea6cbde6b01721 Mon Sep 17 00:00:00 2001 From: Brian Donovan Date: Thu, 8 Sep 2016 08:56:11 -0700 Subject: [PATCH 39/99] Remove unused imports to fix linting. --- src/ast/scopes/Scope.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ast/scopes/Scope.js b/src/ast/scopes/Scope.js index 25512b7..7bb1671 100644 --- a/src/ast/scopes/Scope.js +++ b/src/ast/scopes/Scope.js @@ -1,5 +1,3 @@ -import getLocation from '../../utils/getLocation.js'; -import error from '../../utils/error.js'; import { blank, keys } from '../../utils/object.js'; import { UNKNOWN } from '../values.js'; From bd01c0acb40d5b135c6a7d11513e285fb57105f7 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 8 Sep 2016 23:01:03 -0400 Subject: [PATCH 40/99] correctly rename children of conditional expressions --- src/ast/nodes/ConditionalExpression.js | 2 ++ src/ast/scopes/Scope.js | 2 -- .../function/rename-conditional-expression-children/_config.js | 3 +++ test/function/rename-conditional-expression-children/foo.js | 2 ++ test/function/rename-conditional-expression-children/main.js | 3 +++ 5 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 test/function/rename-conditional-expression-children/_config.js create mode 100644 test/function/rename-conditional-expression-children/foo.js create mode 100644 test/function/rename-conditional-expression-children/main.js diff --git a/src/ast/nodes/ConditionalExpression.js b/src/ast/nodes/ConditionalExpression.js index c5e336d..9c7b444 100644 --- a/src/ast/nodes/ConditionalExpression.js +++ b/src/ast/nodes/ConditionalExpression.js @@ -54,9 +54,11 @@ export default class ConditionalExpression extends Node { else if ( this.testValue ) { code.remove( this.start, this.consequent.start ); code.remove( this.consequent.end, this.end ); + this.consequent.render( code, es ); } else { code.remove( this.start, this.alternate.start ); code.remove( this.alternate.end, this.end ); + this.alternate.render( code, es ); } } } diff --git a/src/ast/scopes/Scope.js b/src/ast/scopes/Scope.js index 25512b7..7bb1671 100644 --- a/src/ast/scopes/Scope.js +++ b/src/ast/scopes/Scope.js @@ -1,5 +1,3 @@ -import getLocation from '../../utils/getLocation.js'; -import error from '../../utils/error.js'; import { blank, keys } from '../../utils/object.js'; import { UNKNOWN } from '../values.js'; diff --git a/test/function/rename-conditional-expression-children/_config.js b/test/function/rename-conditional-expression-children/_config.js new file mode 100644 index 0000000..7c052c5 --- /dev/null +++ b/test/function/rename-conditional-expression-children/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'correctly renders children of ConditionalExpressions' +}; diff --git a/test/function/rename-conditional-expression-children/foo.js b/test/function/rename-conditional-expression-children/foo.js new file mode 100644 index 0000000..7fe4a4d --- /dev/null +++ b/test/function/rename-conditional-expression-children/foo.js @@ -0,0 +1,2 @@ +export const bar = 42; +export const baz = 43; diff --git a/test/function/rename-conditional-expression-children/main.js b/test/function/rename-conditional-expression-children/main.js new file mode 100644 index 0000000..16bfa0f --- /dev/null +++ b/test/function/rename-conditional-expression-children/main.js @@ -0,0 +1,3 @@ +import * as foo from './foo.js'; + +assert.equal( true ? foo.bar : foo.baz, 42 ); From 316f6041ab7abb4d23bbae353ba3661de2169663 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 08:11:12 -0400 Subject: [PATCH 41/99] always treat literals as leaf nodes --- src/ast/keys.js | 3 ++- test/test.js | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ast/keys.js b/src/ast/keys.js index 99d14ea..194ccf7 100644 --- a/src/ast/keys.js +++ b/src/ast/keys.js @@ -1,3 +1,4 @@ export default { - Program: [ 'body' ] + Program: [ 'body' ], + Literal: [] }; diff --git a/test/test.js b/test/test.js index 9697e6e..ca11679 100644 --- a/test/test.js +++ b/test/test.js @@ -93,6 +93,15 @@ describe( 'rollup', function () { assert.equal( err.message, 'Unexpected key \'plUgins\' found, expected one of: acorn, banner, cache, context, dest, entry, exports, external, footer, format, globals, indent, intro, moduleId, moduleName, noConflict, onwarn, outro, paths, plugins, preferConst, sourceMap, sourceMapFile, targets, treeshake, useStrict' ); }); }); + + it( 'treats Literals as leaf nodes, even if first literal encountered is null', () => { + // this test has to be up here, otherwise the bug doesn't have + // an opportunity to present itself + return rollup.rollup({ + entry: 'x', + plugins: [ loader({ x: `var a = null; a = 'a string';` }) ] + }); + }); }); describe( 'bundle.write()', () => { From 06d167b60dc5775c1f44d1422dffe100b6ff629f Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 08:59:22 -0400 Subject: [PATCH 42/99] failing test (skipped for now) --- test/form/side-effect-r/_config.js | 4 ++++ test/form/side-effect-r/main.js | 7 +++++++ 2 files changed, 11 insertions(+) create mode 100644 test/form/side-effect-r/_config.js create mode 100644 test/form/side-effect-r/main.js diff --git a/test/form/side-effect-r/_config.js b/test/form/side-effect-r/_config.js new file mode 100644 index 0000000..2c3f470 --- /dev/null +++ b/test/form/side-effect-r/_config.js @@ -0,0 +1,4 @@ +module.exports = { + skip: true, + description: 'discards unused function expression assigned to a variable that calls itself and a global' +}; diff --git a/test/form/side-effect-r/main.js b/test/form/side-effect-r/main.js new file mode 100644 index 0000000..716ee31 --- /dev/null +++ b/test/form/side-effect-r/main.js @@ -0,0 +1,7 @@ +var foo = function foo() { + if ( whatever ) { + foo(); + } else { + bar(); + } +}; From b05ceb24788aaec77919cb866efbc9c73186983c Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 09:20:10 -0400 Subject: [PATCH 43/99] doh --- src/Module.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module.js b/src/Module.js index fc3ebd1..1c65807 100644 --- a/src/Module.js +++ b/src/Module.js @@ -70,7 +70,7 @@ export default class Module { } this.declarations = blank(); - this.type === 'Module'; // TODO only necessary so that Scope knows this should be treated as a function scope... messy + this.type = 'Module'; // TODO only necessary so that Scope knows this should be treated as a function scope... messy this.scope = new ModuleScope( this ); this.analyse(); From 9fd72a655f67350a773afd34139c75293bdc54c9 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 09:30:14 -0400 Subject: [PATCH 44/99] -> v0.35.0 --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3739a73..d598c9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # rollup changelog +## 0.35.0 + +* Rewrite analysis/tree-shaking code ([#902](https://github.com/rollup/rollup/pull/902)) +* Include conditional mutations of global objects ([#901](https://github.com/rollup/rollup/issues/901)) +* Only reify namespaces if necessary ([#898](https://github.com/rollup/rollup/issues/898)) +* Track mutations of aliased globals ([#893](https://github.com/rollup/rollup/issues/893)) +* Include duplicated var declarations ([#716](https://github.com/rollup/rollup/issues/716)) + ## 0.34.13 * Pass `{ format }` through to `transformBundle` ([#867](https://github.com/rollup/rollup/issues/867)) diff --git a/package.json b/package.json index 394a4ae..6f5fb1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.34.13", + "version": "0.35.0", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From 928f7f4f9ab4c8185a0c53c83471419c2077fdc4 Mon Sep 17 00:00:00 2001 From: npmcdn-to-unpkg-bot Date: Sat, 10 Sep 2016 16:25:00 +0100 Subject: [PATCH 45/99] Replace npmcdn.com with unpkg.com --- test/form/paths-function/_config.js | 2 +- test/form/paths-function/_expected/amd.js | 2 +- test/form/paths-function/_expected/cjs.js | 2 +- test/form/paths-function/_expected/es.js | 2 +- test/form/paths-function/_expected/umd.js | 4 ++-- test/form/paths/_config.js | 2 +- test/form/paths/_expected/amd.js | 2 +- test/form/paths/_expected/cjs.js | 2 +- test/form/paths/_expected/es.js | 2 +- test/form/paths/_expected/umd.js | 4 ++-- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/form/paths-function/_config.js b/test/form/paths-function/_config.js index d4f6a0d..2d1f055 100644 --- a/test/form/paths-function/_config.js +++ b/test/form/paths-function/_config.js @@ -1,6 +1,6 @@ module.exports = { description: 'external paths (#754)', options: { - paths: id => `https://npmcdn.com/${id}` + paths: id => `https://unpkg.com/${id}` } }; diff --git a/test/form/paths-function/_expected/amd.js b/test/form/paths-function/_expected/amd.js index fe2d243..0893293 100644 --- a/test/form/paths-function/_expected/amd.js +++ b/test/form/paths-function/_expected/amd.js @@ -1,4 +1,4 @@ -define(['https://npmcdn.com/foo'], function (foo) { 'use strict'; +define(['https://unpkg.com/foo'], function (foo) { 'use strict'; foo = 'default' in foo ? foo['default'] : foo; diff --git a/test/form/paths-function/_expected/cjs.js b/test/form/paths-function/_expected/cjs.js index 864ce43..4bb0804 100644 --- a/test/form/paths-function/_expected/cjs.js +++ b/test/form/paths-function/_expected/cjs.js @@ -2,6 +2,6 @@ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } -var foo = _interopDefault(require('https://npmcdn.com/foo')); +var foo = _interopDefault(require('https://unpkg.com/foo')); assert.equal( foo, 42 ); diff --git a/test/form/paths-function/_expected/es.js b/test/form/paths-function/_expected/es.js index 1c4fe57..74bfea3 100644 --- a/test/form/paths-function/_expected/es.js +++ b/test/form/paths-function/_expected/es.js @@ -1,3 +1,3 @@ -import foo from 'https://npmcdn.com/foo'; +import foo from 'https://unpkg.com/foo'; assert.equal( foo, 42 ); diff --git a/test/form/paths-function/_expected/umd.js b/test/form/paths-function/_expected/umd.js index 8da109e..e86e892 100644 --- a/test/form/paths-function/_expected/umd.js +++ b/test/form/paths-function/_expected/umd.js @@ -1,6 +1,6 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('https://npmcdn.com/foo')) : - typeof define === 'function' && define.amd ? define(['https://npmcdn.com/foo'], factory) : + typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('https://unpkg.com/foo')) : + typeof define === 'function' && define.amd ? define(['https://unpkg.com/foo'], factory) : (factory(global.foo)); }(this, (function (foo) { 'use strict'; diff --git a/test/form/paths/_config.js b/test/form/paths/_config.js index c9b4ca3..1d6ed03 100644 --- a/test/form/paths/_config.js +++ b/test/form/paths/_config.js @@ -2,7 +2,7 @@ module.exports = { description: 'external paths (#754)', options: { paths: { - foo: 'https://npmcdn.com/foo' + foo: 'https://unpkg.com/foo' } } }; diff --git a/test/form/paths/_expected/amd.js b/test/form/paths/_expected/amd.js index fe2d243..0893293 100644 --- a/test/form/paths/_expected/amd.js +++ b/test/form/paths/_expected/amd.js @@ -1,4 +1,4 @@ -define(['https://npmcdn.com/foo'], function (foo) { 'use strict'; +define(['https://unpkg.com/foo'], function (foo) { 'use strict'; foo = 'default' in foo ? foo['default'] : foo; diff --git a/test/form/paths/_expected/cjs.js b/test/form/paths/_expected/cjs.js index 864ce43..4bb0804 100644 --- a/test/form/paths/_expected/cjs.js +++ b/test/form/paths/_expected/cjs.js @@ -2,6 +2,6 @@ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } -var foo = _interopDefault(require('https://npmcdn.com/foo')); +var foo = _interopDefault(require('https://unpkg.com/foo')); assert.equal( foo, 42 ); diff --git a/test/form/paths/_expected/es.js b/test/form/paths/_expected/es.js index 1c4fe57..74bfea3 100644 --- a/test/form/paths/_expected/es.js +++ b/test/form/paths/_expected/es.js @@ -1,3 +1,3 @@ -import foo from 'https://npmcdn.com/foo'; +import foo from 'https://unpkg.com/foo'; assert.equal( foo, 42 ); diff --git a/test/form/paths/_expected/umd.js b/test/form/paths/_expected/umd.js index 8da109e..e86e892 100644 --- a/test/form/paths/_expected/umd.js +++ b/test/form/paths/_expected/umd.js @@ -1,6 +1,6 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('https://npmcdn.com/foo')) : - typeof define === 'function' && define.amd ? define(['https://npmcdn.com/foo'], factory) : + typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('https://unpkg.com/foo')) : + typeof define === 'function' && define.amd ? define(['https://unpkg.com/foo'], factory) : (factory(global.foo)); }(this, (function (foo) { 'use strict'; From e8179c2013b7cfb4b1c7cba3b2fe441f7597ad97 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 12:21:29 -0400 Subject: [PATCH 46/99] include dependencies in bundle.modules (closes #903) --- src/Module.js | 1 + test/function/module-tree/_config.js | 37 +++++++++++++++++++++++++ test/function/module-tree/bar.js | 1 + test/function/module-tree/foo.js | 1 + test/function/module-tree/main.js | 2 ++ test/function/module-tree/nested/baz.js | 1 + test/function/module-tree/nested/qux.js | 1 + 7 files changed, 44 insertions(+) create mode 100644 test/function/module-tree/_config.js create mode 100644 test/function/module-tree/bar.js create mode 100644 test/function/module-tree/foo.js create mode 100644 test/function/module-tree/main.js create mode 100644 test/function/module-tree/nested/baz.js create mode 100644 test/function/module-tree/nested/qux.js diff --git a/src/Module.js b/src/Module.js index 1c65807..74f7b74 100644 --- a/src/Module.js +++ b/src/Module.js @@ -315,6 +315,7 @@ export default class Module { toJSON () { return { id: this.id, + dependencies: this.dependencies.map( module => module.id ), code: this.code, originalCode: this.originalCode, ast: this.astClone, diff --git a/test/function/module-tree/_config.js b/test/function/module-tree/_config.js new file mode 100644 index 0000000..97827df --- /dev/null +++ b/test/function/module-tree/_config.js @@ -0,0 +1,37 @@ +const path = require( 'path' ); +const assert = require( 'assert' ); + +module.exports = { + description: 'bundle.modules includes dependencies (#903)', + bundle ( bundle ) { + const modules = bundle.modules.map( module => { + return { + id: path.relative( __dirname, module.id ), + dependencies: module.dependencies.map( id => path.relative( __dirname, id ) ) + }; + }); + + assert.deepEqual( modules, [ + { + id: 'nested/qux.js', + dependencies: [] + }, + { + id: 'nested/baz.js', + dependencies: [ 'nested/qux.js' ] + }, + { + id: 'bar.js', + dependencies: [ 'nested/baz.js' ] + }, + { + id: 'foo.js', + dependencies: [ 'bar.js' ] + }, + { + id: 'main.js', + dependencies: [ 'foo.js', 'bar.js' ] + } + ]); + } +}; diff --git a/test/function/module-tree/bar.js b/test/function/module-tree/bar.js new file mode 100644 index 0000000..76340f6 --- /dev/null +++ b/test/function/module-tree/bar.js @@ -0,0 +1 @@ +import './nested/baz.js'; diff --git a/test/function/module-tree/foo.js b/test/function/module-tree/foo.js new file mode 100644 index 0000000..1df02c2 --- /dev/null +++ b/test/function/module-tree/foo.js @@ -0,0 +1 @@ +import './bar.js'; diff --git a/test/function/module-tree/main.js b/test/function/module-tree/main.js new file mode 100644 index 0000000..f257022 --- /dev/null +++ b/test/function/module-tree/main.js @@ -0,0 +1,2 @@ +import './foo.js'; +import './bar.js'; diff --git a/test/function/module-tree/nested/baz.js b/test/function/module-tree/nested/baz.js new file mode 100644 index 0000000..0ff2bf9 --- /dev/null +++ b/test/function/module-tree/nested/baz.js @@ -0,0 +1 @@ +import qux from './qux.js'; diff --git a/test/function/module-tree/nested/qux.js b/test/function/module-tree/nested/qux.js new file mode 100644 index 0000000..6d70fec --- /dev/null +++ b/test/function/module-tree/nested/qux.js @@ -0,0 +1 @@ +export default 'whatever'; From 7819d7b68822e7a767e227480b08598492b14bf9 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 12:26:31 -0400 Subject: [PATCH 47/99] update to acorn 4 --- package.json | 2 +- test/function/double-default-export/_config.js | 3 ++- test/function/double-named-export/_config.js | 3 ++- test/function/double-named-reexport/_config.js | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 6f5fb1a..230039c 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ }, "homepage": "https://github.com/rollup/rollup", "devDependencies": { - "acorn": "^3.2.0", + "acorn": "^4.0.1", "buble": "^0.12.5", "chalk": "^1.1.3", "codecov.io": "^0.1.6", diff --git a/test/function/double-default-export/_config.js b/test/function/double-default-export/_config.js index 26e86e7..d5f5254 100644 --- a/test/function/double-default-export/_config.js +++ b/test/function/double-default-export/_config.js @@ -1,8 +1,9 @@ +const path = require( 'path' ); const assert = require( 'assert' ); module.exports = { description: 'throws on double default exports', error: err => { - assert.equal( err.message, 'A module can only have one default export' ); + assert.equal( err.message, `Duplicate export 'default' (2:7) in ${path.resolve(__dirname, 'foo.js')}` ); } }; diff --git a/test/function/double-named-export/_config.js b/test/function/double-named-export/_config.js index 577b421..169ce76 100644 --- a/test/function/double-named-export/_config.js +++ b/test/function/double-named-export/_config.js @@ -1,8 +1,9 @@ +const path = require( 'path' ); const assert = require( 'assert' ); module.exports = { description: 'throws on duplicate named exports', error: err => { - assert.equal( err.message, `A module cannot have multiple exports with the same name ('foo')` ); + assert.equal( err.message, `Duplicate export 'foo' (3:9) in ${path.resolve(__dirname, 'foo.js')}` ); } }; diff --git a/test/function/double-named-reexport/_config.js b/test/function/double-named-reexport/_config.js index 577b421..169ce76 100644 --- a/test/function/double-named-reexport/_config.js +++ b/test/function/double-named-reexport/_config.js @@ -1,8 +1,9 @@ +const path = require( 'path' ); const assert = require( 'assert' ); module.exports = { description: 'throws on duplicate named exports', error: err => { - assert.equal( err.message, `A module cannot have multiple exports with the same name ('foo')` ); + assert.equal( err.message, `Duplicate export 'foo' (3:9) in ${path.resolve(__dirname, 'foo.js')}` ); } }; From e723cc5c468317341ce7c44c695e3dceec3522d9 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 12:29:34 -0400 Subject: [PATCH 48/99] only transpile acorn 4 down to node 4 support level --- rollup.config.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index f55dc7c..a8ac2c8 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -22,11 +22,7 @@ export default { entry: 'src/rollup.js', plugins: [ buble({ - include: [ 'node_modules/acorn/**' ] - }), - - buble({ - include: [ 'src/**' ], + include: [ 'src/**', 'node_modules/acorn/**' ], target: { node: 4 } From 4817eada3e656f4b166084cf0744245bee705a42 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Sat, 10 Sep 2016 20:01:38 +0300 Subject: [PATCH 49/99] Fix test on windows --- test/function/module-tree/_config.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/function/module-tree/_config.js b/test/function/module-tree/_config.js index 97827df..efb1a20 100644 --- a/test/function/module-tree/_config.js +++ b/test/function/module-tree/_config.js @@ -13,16 +13,16 @@ module.exports = { assert.deepEqual( modules, [ { - id: 'nested/qux.js', + id: path.normalize('nested/qux.js'), dependencies: [] }, { - id: 'nested/baz.js', - dependencies: [ 'nested/qux.js' ] + id: path.normalize('nested/baz.js'), + dependencies: [ path.normalize('nested/qux.js') ] }, { id: 'bar.js', - dependencies: [ 'nested/baz.js' ] + dependencies: [ path.normalize('nested/baz.js') ] }, { id: 'foo.js', From c1b7092b0dc292905012ac5d3a915c8b5dad5b4e Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Sat, 10 Sep 2016 20:02:33 +0300 Subject: [PATCH 50/99] Fix codestyle --- test/function/module-tree/_config.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/function/module-tree/_config.js b/test/function/module-tree/_config.js index efb1a20..553ab2b 100644 --- a/test/function/module-tree/_config.js +++ b/test/function/module-tree/_config.js @@ -13,16 +13,16 @@ module.exports = { assert.deepEqual( modules, [ { - id: path.normalize('nested/qux.js'), + id: path.normalize( 'nested/qux.js' ), dependencies: [] }, { - id: path.normalize('nested/baz.js'), - dependencies: [ path.normalize('nested/qux.js') ] + id: path.normalize( 'nested/baz.js' ), + dependencies: [ path.normalize( 'nested/qux.js' ) ] }, { id: 'bar.js', - dependencies: [ path.normalize('nested/baz.js') ] + dependencies: [ path.normalize( 'nested/baz.js' ) ] }, { id: 'foo.js', From 16da86e93c7047bea39212cbb761bf4f836ca7f4 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 13:12:22 -0400 Subject: [PATCH 51/99] deconflict-classes --- src/ast/nodes/ClassDeclaration.js | 6 ++++-- test/function/deconflicts-classes/_config.js | 4 ++++ test/function/deconflicts-classes/a.js | 2 ++ test/function/deconflicts-classes/b.js | 2 ++ test/function/deconflicts-classes/main.js | 4 ++++ 5 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 test/function/deconflicts-classes/_config.js create mode 100644 test/function/deconflicts-classes/a.js create mode 100644 test/function/deconflicts-classes/b.js create mode 100644 test/function/deconflicts-classes/main.js diff --git a/src/ast/nodes/ClassDeclaration.js b/src/ast/nodes/ClassDeclaration.js index ed773da..f67c87d 100644 --- a/src/ast/nodes/ClassDeclaration.js +++ b/src/ast/nodes/ClassDeclaration.js @@ -18,7 +18,7 @@ export default class ClassDeclaration extends Node { } getName () { - return this.id.name; + return this.name; } hasEffects () { @@ -26,7 +26,9 @@ export default class ClassDeclaration extends Node { } initialise ( scope ) { - scope.addDeclaration( this.id.name, this, false, false ); + this.name = this.id.name; + + scope.addDeclaration( this.name, this, false, false ); super.initialise( scope ); } diff --git a/test/function/deconflicts-classes/_config.js b/test/function/deconflicts-classes/_config.js new file mode 100644 index 0000000..d2e030b --- /dev/null +++ b/test/function/deconflicts-classes/_config.js @@ -0,0 +1,4 @@ +module.exports = { + solo: true, + description: 'deconflicts top-level classes' +}; diff --git a/test/function/deconflicts-classes/a.js b/test/function/deconflicts-classes/a.js new file mode 100644 index 0000000..8f88528 --- /dev/null +++ b/test/function/deconflicts-classes/a.js @@ -0,0 +1,2 @@ +var Foo = function Foo () {}; +export { Foo }; diff --git a/test/function/deconflicts-classes/b.js b/test/function/deconflicts-classes/b.js new file mode 100644 index 0000000..6f6f410 --- /dev/null +++ b/test/function/deconflicts-classes/b.js @@ -0,0 +1,2 @@ +class Foo {}; +export { Foo }; diff --git a/test/function/deconflicts-classes/main.js b/test/function/deconflicts-classes/main.js new file mode 100644 index 0000000..89ff7de --- /dev/null +++ b/test/function/deconflicts-classes/main.js @@ -0,0 +1,4 @@ +import { Foo as A } from './a.js'; +import { Foo as B } from './b.js'; + +assert.notEqual( A, B ); From db8fcc4273167df7dd1118f567540a24a61a82cf Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 13:19:12 -0400 Subject: [PATCH 52/99] -> v0.35.1 --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d598c9d..4f4626f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # rollup changelog +## 0.35.1 + +* Rewrite deconflicted class identifiers ([#915](https://github.com/rollup/rollup/pull/915)) +* Include `dependencies` in `bundle.modules` objects ([#903](https://github.com/rollup/rollup/issues/903)) +* Update to Acorn 4 ([#914](https://github.com/rollup/rollup/pull/914)) + ## 0.35.0 * Rewrite analysis/tree-shaking code ([#902](https://github.com/rollup/rollup/pull/902)) diff --git a/package.json b/package.json index 230039c..0ee877b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.0", + "version": "0.35.1", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From a6dd89b25d8c1d404ad4e6fc60e2699685670c51 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 13:24:20 -0400 Subject: [PATCH 53/99] -> v0.35.2 --- CHANGELOG.md | 4 ++++ package.json | 2 +- test/function/deconflicts-classes/_config.js | 1 - 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f4626f..5ac7487 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.35.2 + +* Fix broken build caused by out of date locally installed dependencies + ## 0.35.1 * Rewrite deconflicted class identifiers ([#915](https://github.com/rollup/rollup/pull/915)) diff --git a/package.json b/package.json index 0ee877b..ad9d319 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.1", + "version": "0.35.2", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", diff --git a/test/function/deconflicts-classes/_config.js b/test/function/deconflicts-classes/_config.js index d2e030b..ee66bd1 100644 --- a/test/function/deconflicts-classes/_config.js +++ b/test/function/deconflicts-classes/_config.js @@ -1,4 +1,3 @@ module.exports = { - solo: true, description: 'deconflicts top-level classes' }; From 81979770505e4d4844f500da398a873e710bc6f3 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Sat, 10 Sep 2016 21:01:24 +0300 Subject: [PATCH 54/99] Add performance labels --- src/Bundle.js | 27 +++++++++++++++++++++++++++ src/Module.js | 11 +++++++++++ src/rollup.js | 11 +++++++++++ src/utils/flushTime.js | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 src/utils/flushTime.js diff --git a/src/Bundle.js b/src/Bundle.js index 881907a..40a9211 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -1,3 +1,4 @@ +import { timeStart, timeEnd } from './utils/flushTime.js'; import { decode } from 'sourcemap-codec'; import { Bundle as MagicStringBundle } from 'magic-string'; import first from './utils/first.js'; @@ -102,12 +103,19 @@ export default class Bundle { // Phase 2 – binding. We link references to their declarations // to generate a complete picture of the bundle + + timeStart( 'phase 2' ); + this.modules.forEach( module => module.bindImportSpecifiers() ); this.modules.forEach( module => module.bindReferences() ); + timeEnd( 'phase 2' ); + // Phase 3 – marking. We 'run' each statement to see which ones // need to be included in the generated bundle + timeStart( 'phase 3' ); + // mark all export statements entryModule.getExports().forEach( name => { const declaration = entryModule.traceExport( name ); @@ -146,11 +154,18 @@ export default class Bundle { } } + timeEnd( 'phase 3' ); + // Phase 4 – final preparation. We order the modules with an // enhanced topological sort that accounts for cycles, then // ensure that names are deconflicted throughout the bundle + + timeStart( 'phase 4' ); + this.orderedModules = this.sort(); this.deconflict(); + + timeEnd( 'phase 4' ); }); } @@ -332,6 +347,8 @@ export default class Bundle { let magicString = new MagicStringBundle({ separator: '\n\n' }); const usedModules = []; + timeStart( 'render modules' ); + this.orderedModules.forEach( module => { const source = module.render( format === 'es' ); @@ -341,6 +358,8 @@ export default class Bundle { } }); + timeEnd( 'render modules' ); + let intro = [ options.intro ] .concat( this.plugins.map( plugin => plugin.intro && plugin.intro() ) @@ -355,8 +374,12 @@ export default class Bundle { const finalise = finalisers[ format ]; if ( !finalise ) throw new Error( `You must specify an output type - valid options are ${keys( finalisers ).join( ', ' )}` ); + timeStart( 'render format' ); + magicString = finalise( this, magicString.trim(), { exportMode, indentString, intro }, options ); + timeEnd( 'render format' ); + const banner = [ options.banner ] .concat( this.plugins.map( plugin => plugin.banner ) ) .map( callIfFunction ) @@ -380,6 +403,8 @@ export default class Bundle { .replace( new RegExp( `\\/\\/#\\s+${SOURCEMAPPING_URL}=.+\\n?`, 'g' ), '' ); if ( options.sourceMap ) { + timeStart( 'sourceMap' ); + let file = options.sourceMapFile || options.dest; if ( file ) file = resolve( typeof process !== 'undefined' ? process.cwd() : '', file ); @@ -394,6 +419,8 @@ export default class Bundle { } map.sources = map.sources.map( normalize ); + + timeEnd( 'sourceMap' ); } return { code, map }; diff --git a/src/Module.js b/src/Module.js index 1c65807..dc0b051 100644 --- a/src/Module.js +++ b/src/Module.js @@ -1,3 +1,4 @@ +import { timeStart, timeEnd } from './utils/flushTime.js'; import { parse } from 'acorn/src/index.js'; import MagicString from 'magic-string'; import { assign, blank, deepClone, keys } from './utils/object.js'; @@ -34,9 +35,14 @@ export default class Module { this.sourceMapChain = sourceMapChain; this.comments = []; + + timeStart( 'ast' ); + this.ast = ast || tryParse( code, this.comments, bundle.acornOptions, id ); // TODO what happens to comments if AST is provided? this.astClone = deepClone( this.ast ); + timeEnd( 'ast' ); + this.bundle = bundle; this.id = id; this.excludeFromSourcemap = /\0/.test( id ); @@ -72,8 +78,13 @@ export default class Module { this.declarations = blank(); this.type = 'Module'; // TODO only necessary so that Scope knows this should be treated as a function scope... messy this.scope = new ModuleScope( this ); + + timeStart( 'analyse' ); + this.analyse(); + timeEnd( 'analyse' ); + this.strongDependencies = []; } diff --git a/src/rollup.js b/src/rollup.js index 4c1a076..e0db03e 100644 --- a/src/rollup.js +++ b/src/rollup.js @@ -1,3 +1,4 @@ +import { timeStart, timeEnd, flushTime } from './utils/flushTime.js'; import { basename } from './utils/path.js'; import { writeFile } from './utils/fs.js'; import { assign, keys } from './utils/object.js'; @@ -54,10 +55,18 @@ export function rollup ( options ) { const bundle = new Bundle( options ); + timeStart( '--BUILD--' ); + return bundle.build().then( () => { + timeEnd( '--BUILD--' ); + function generate ( options ) { + timeStart( '--GENERATE--' ) + const rendered = bundle.render( options ); + timeEnd( '--GENERATE--' ); + bundle.plugins.forEach( plugin => { if ( plugin.ongenerate ) { plugin.ongenerate( assign({ @@ -66,6 +75,8 @@ export function rollup ( options ) { } }); + flushTime(); + return rendered; } diff --git a/src/utils/flushTime.js b/src/utils/flushTime.js new file mode 100644 index 0000000..e54e1b2 --- /dev/null +++ b/src/utils/flushTime.js @@ -0,0 +1,35 @@ +const DEBUG = false; +const map = new Map; + +export function timeStart( label ) { + if ( !map.has( label ) ) { + map.set( label, { + time: 0 + }); + } + map.get( label ).start = process.hrtime(); +} + +export function timeEnd( label ) { + if ( map.has( label ) ) { + const item = map.get( label ); + item.time += toMilliseconds( process.hrtime( item.start ) ); + } +} + +export function flushTime( log = defaultLog ) { + for ( const item of map.entries() ) { + log( item[0], item[1].time ); + } + map.clear(); +} + +function toMilliseconds( time ) { + return time[0] * 1e+3 + Math.floor( time[1] * 1e-6 ); +} + +function defaultLog( label, time ) { + if ( DEBUG ) { + console.info( '%dms: %s', time, label ); + } +} From 50d263203ee04f0ff4e91257d65ae9d5b3f7b754 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Sat, 10 Sep 2016 21:04:52 +0300 Subject: [PATCH 55/99] Fix lint --- src/rollup.js | 2 +- src/utils/flushTime.js | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/rollup.js b/src/rollup.js index e0db03e..2ab5b51 100644 --- a/src/rollup.js +++ b/src/rollup.js @@ -61,7 +61,7 @@ export function rollup ( options ) { timeEnd( '--BUILD--' ); function generate ( options ) { - timeStart( '--GENERATE--' ) + timeStart( '--GENERATE--' ); const rendered = bundle.render( options ); diff --git a/src/utils/flushTime.js b/src/utils/flushTime.js index e54e1b2..fb4d331 100644 --- a/src/utils/flushTime.js +++ b/src/utils/flushTime.js @@ -1,7 +1,7 @@ const DEBUG = false; const map = new Map; -export function timeStart( label ) { +export function timeStart ( label ) { if ( !map.has( label ) ) { map.set( label, { time: 0 @@ -10,26 +10,28 @@ export function timeStart( label ) { map.get( label ).start = process.hrtime(); } -export function timeEnd( label ) { +export function timeEnd ( label ) { if ( map.has( label ) ) { const item = map.get( label ); item.time += toMilliseconds( process.hrtime( item.start ) ); } } -export function flushTime( log = defaultLog ) { +export function flushTime ( log = defaultLog ) { for ( const item of map.entries() ) { log( item[0], item[1].time ); } map.clear(); } -function toMilliseconds( time ) { +function toMilliseconds ( time ) { return time[0] * 1e+3 + Math.floor( time[1] * 1e-6 ); } -function defaultLog( label, time ) { +function defaultLog ( label, time ) { if ( DEBUG ) { + /* eslint-disable no-console */ console.info( '%dms: %s', time, label ); + /* eslint-enable no-console */ } } From 8446b06e8d13ce742085af88c21032e3eab5c91f Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 14:43:06 -0400 Subject: [PATCH 56/99] render identifiers inside template literals --- src/ast/nodes/TemplateLiteral.js | 3 ++- test/function/identifiers-in-template-literals/_config.js | 3 +++ test/function/identifiers-in-template-literals/a.js | 7 +++++++ test/function/identifiers-in-template-literals/b.js | 7 +++++++ test/function/identifiers-in-template-literals/main.js | 5 +++++ 5 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 test/function/identifiers-in-template-literals/_config.js create mode 100644 test/function/identifiers-in-template-literals/a.js create mode 100644 test/function/identifiers-in-template-literals/b.js create mode 100644 test/function/identifiers-in-template-literals/main.js diff --git a/src/ast/nodes/TemplateLiteral.js b/src/ast/nodes/TemplateLiteral.js index 057ce12..b25ef48 100644 --- a/src/ast/nodes/TemplateLiteral.js +++ b/src/ast/nodes/TemplateLiteral.js @@ -1,7 +1,8 @@ import Node from '../Node.js'; export default class TemplateLiteral extends Node { - render ( code ) { + render ( code, es ) { code.indentExclusionRanges.push([ this.start, this.end ]); + super.render( code, es ); } } diff --git a/test/function/identifiers-in-template-literals/_config.js b/test/function/identifiers-in-template-literals/_config.js new file mode 100644 index 0000000..8b031cd --- /dev/null +++ b/test/function/identifiers-in-template-literals/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'identifiers in template literals are rendered correctly' +}; diff --git a/test/function/identifiers-in-template-literals/a.js b/test/function/identifiers-in-template-literals/a.js new file mode 100644 index 0000000..26a1db8 --- /dev/null +++ b/test/function/identifiers-in-template-literals/a.js @@ -0,0 +1,7 @@ +function x ( keypath ) { + return 'a'; +} + +export default function a () { + return x(); +} diff --git a/test/function/identifiers-in-template-literals/b.js b/test/function/identifiers-in-template-literals/b.js new file mode 100644 index 0000000..7511028 --- /dev/null +++ b/test/function/identifiers-in-template-literals/b.js @@ -0,0 +1,7 @@ +function x ( name ) { + return 'b'; +} + +export default function b () { + return `${x()}`; +} diff --git a/test/function/identifiers-in-template-literals/main.js b/test/function/identifiers-in-template-literals/main.js new file mode 100644 index 0000000..eedb4fb --- /dev/null +++ b/test/function/identifiers-in-template-literals/main.js @@ -0,0 +1,5 @@ +import a from './a.js'; +import b from './b.js'; + +assert.equal( a(), 'a' ); +assert.equal( b(), 'b' ); From c00399bf20ba60d522c9d7d299b0975eda007c97 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 14:44:07 -0400 Subject: [PATCH 57/99] -> v0.35.3 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ac7487..009ae46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.35.3 + +* Render identifiers inside template literals + ## 0.35.2 * Fix broken build caused by out of date locally installed dependencies diff --git a/package.json b/package.json index ad9d319..de10926 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.2", + "version": "0.35.3", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From 56d64da1a4170b5974e7d962f2403736d958cf39 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 14:47:06 -0400 Subject: [PATCH 58/99] build self with 0.35 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index de10926..bcb8ff7 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "mocha": "^3.0.0", "remap-istanbul": "^0.6.4", "require-relative": "^0.8.7", - "rollup": "^0.34.2", + "rollup": "^0.35.3", "rollup-plugin-buble": "^0.12.1", "rollup-plugin-commonjs": "^3.0.0", "rollup-plugin-json": "^2.0.0", From b82375c2734a1da91f0d793723bdfa0f28e8a931 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 16:04:28 -0400 Subject: [PATCH 59/99] preserve effects in for-of loop (#870) --- src/ast/nodes/ForInStatement.js | 15 ++++++++++++ src/ast/nodes/ForOfStatement.js | 15 ++++++++++++ src/ast/nodes/index.js | 4 ++++ test/form/effect-in-for-of-loop/_config.js | 3 +++ .../effect-in-for-of-loop/_expected/amd.js | 19 +++++++++++++++ .../effect-in-for-of-loop/_expected/cjs.js | 17 ++++++++++++++ .../effect-in-for-of-loop/_expected/es.js | 15 ++++++++++++ .../effect-in-for-of-loop/_expected/iife.js | 20 ++++++++++++++++ .../effect-in-for-of-loop/_expected/umd.js | 23 +++++++++++++++++++ test/form/effect-in-for-of-loop/main.js | 23 +++++++++++++++++++ 10 files changed, 154 insertions(+) create mode 100644 src/ast/nodes/ForInStatement.js create mode 100644 src/ast/nodes/ForOfStatement.js create mode 100644 test/form/effect-in-for-of-loop/_config.js create mode 100644 test/form/effect-in-for-of-loop/_expected/amd.js create mode 100644 test/form/effect-in-for-of-loop/_expected/cjs.js create mode 100644 test/form/effect-in-for-of-loop/_expected/es.js create mode 100644 test/form/effect-in-for-of-loop/_expected/iife.js create mode 100644 test/form/effect-in-for-of-loop/_expected/umd.js create mode 100644 test/form/effect-in-for-of-loop/main.js diff --git a/src/ast/nodes/ForInStatement.js b/src/ast/nodes/ForInStatement.js new file mode 100644 index 0000000..002dd0a --- /dev/null +++ b/src/ast/nodes/ForInStatement.js @@ -0,0 +1,15 @@ +import Node from '../Node.js'; +import { STRING } from '../values.js'; + +export default class ForInStatement extends Node { + initialise ( scope ) { + super.initialise( scope ); + + // special case + if ( this.left.type === 'VariableDeclaration' ) { + for ( const proxy of this.left.declarations[0].proxies.values() ) { + proxy.possibleValues.add( STRING ); + } + } + } +} diff --git a/src/ast/nodes/ForOfStatement.js b/src/ast/nodes/ForOfStatement.js new file mode 100644 index 0000000..201d491 --- /dev/null +++ b/src/ast/nodes/ForOfStatement.js @@ -0,0 +1,15 @@ +import Node from '../Node.js'; +import { UNKNOWN } from '../values.js'; + +export default class ForOfStatement extends Node { + initialise ( scope ) { + super.initialise( scope ); + + // special case + if ( this.left.type === 'VariableDeclaration' ) { + for ( const proxy of this.left.declarations[0].proxies.values() ) { + proxy.possibleValues.add( UNKNOWN ); + } + } + } +} diff --git a/src/ast/nodes/index.js b/src/ast/nodes/index.js index c276a34..3cca1ad 100644 --- a/src/ast/nodes/index.js +++ b/src/ast/nodes/index.js @@ -11,6 +11,8 @@ import ExportAllDeclaration from './ExportAllDeclaration.js'; import ExportDefaultDeclaration from './ExportDefaultDeclaration.js'; import ExportNamedDeclaration from './ExportNamedDeclaration.js'; import ExpressionStatement from './ExpressionStatement.js'; +import ForInStatement from './ForInStatement.js'; +import ForOfStatement from './ForOfStatement.js'; import FunctionDeclaration from './FunctionDeclaration.js'; import FunctionExpression from './FunctionExpression.js'; import Identifier from './Identifier.js'; @@ -43,6 +45,8 @@ export default { ExportDefaultDeclaration, ExportNamedDeclaration, ExpressionStatement, + ForInStatement, + ForOfStatement, FunctionDeclaration, FunctionExpression, Identifier, diff --git a/test/form/effect-in-for-of-loop/_config.js b/test/form/effect-in-for-of-loop/_config.js new file mode 100644 index 0000000..79abe74 --- /dev/null +++ b/test/form/effect-in-for-of-loop/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'includes effects in for-of loop (#870)' +} diff --git a/test/form/effect-in-for-of-loop/_expected/amd.js b/test/form/effect-in-for-of-loop/_expected/amd.js new file mode 100644 index 0000000..57223a0 --- /dev/null +++ b/test/form/effect-in-for-of-loop/_expected/amd.js @@ -0,0 +1,19 @@ +define(function () { 'use strict'; + + const items = [{}, {}, {}]; + + function x () { + for ( const item of items.children ) { + item.foo = 'bar'; + } + } + + x(); + + assert.deepEqual( items, [ + { foo: 'bar' }, + { foo: 'bar' }, + { foo: 'bar' } + ]); + +}); diff --git a/test/form/effect-in-for-of-loop/_expected/cjs.js b/test/form/effect-in-for-of-loop/_expected/cjs.js new file mode 100644 index 0000000..fd94a9d --- /dev/null +++ b/test/form/effect-in-for-of-loop/_expected/cjs.js @@ -0,0 +1,17 @@ +'use strict'; + +const items = [{}, {}, {}]; + +function x () { + for ( const item of items.children ) { + item.foo = 'bar'; + } +} + +x(); + +assert.deepEqual( items, [ + { foo: 'bar' }, + { foo: 'bar' }, + { foo: 'bar' } +]); diff --git a/test/form/effect-in-for-of-loop/_expected/es.js b/test/form/effect-in-for-of-loop/_expected/es.js new file mode 100644 index 0000000..cabffc4 --- /dev/null +++ b/test/form/effect-in-for-of-loop/_expected/es.js @@ -0,0 +1,15 @@ +const items = [{}, {}, {}]; + +function x () { + for ( const item of items.children ) { + item.foo = 'bar'; + } +} + +x(); + +assert.deepEqual( items, [ + { foo: 'bar' }, + { foo: 'bar' }, + { foo: 'bar' } +]); diff --git a/test/form/effect-in-for-of-loop/_expected/iife.js b/test/form/effect-in-for-of-loop/_expected/iife.js new file mode 100644 index 0000000..96e91ea --- /dev/null +++ b/test/form/effect-in-for-of-loop/_expected/iife.js @@ -0,0 +1,20 @@ +(function () { + 'use strict'; + + const items = [{}, {}, {}]; + + function x () { + for ( const item of items.children ) { + item.foo = 'bar'; + } + } + + x(); + + assert.deepEqual( items, [ + { foo: 'bar' }, + { foo: 'bar' }, + { foo: 'bar' } + ]); + +}()); diff --git a/test/form/effect-in-for-of-loop/_expected/umd.js b/test/form/effect-in-for-of-loop/_expected/umd.js new file mode 100644 index 0000000..83a6d30 --- /dev/null +++ b/test/form/effect-in-for-of-loop/_expected/umd.js @@ -0,0 +1,23 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + const items = [{}, {}, {}]; + + function x () { + for ( const item of items.children ) { + item.foo = 'bar'; + } + } + + x(); + + assert.deepEqual( items, [ + { foo: 'bar' }, + { foo: 'bar' }, + { foo: 'bar' } + ]); + +}))); diff --git a/test/form/effect-in-for-of-loop/main.js b/test/form/effect-in-for-of-loop/main.js new file mode 100644 index 0000000..401dcf2 --- /dev/null +++ b/test/form/effect-in-for-of-loop/main.js @@ -0,0 +1,23 @@ +const items = [{}, {}, {}]; + +function x () { + for ( const item of items.children ) { + item.foo = 'bar'; + } +} + +x(); + +function y () { + for ( const item of items.children ) { + // do nothing + } +} + +y(); + +assert.deepEqual( items, [ + { foo: 'bar' }, + { foo: 'bar' }, + { foo: 'bar' } +]); From 6da374cea2ad72b3a6f729d0e89a532170cf0858 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 16:49:37 -0400 Subject: [PATCH 60/99] preserve effects in for-of and for-in statements. fixes #870 --- src/ast/nodes/ExpressionStatement.js | 15 +------ src/ast/nodes/ForInStatement.js | 13 ++---- src/ast/nodes/ForOfStatement.js | 13 ++---- src/ast/nodes/IfStatement.js | 4 +- src/ast/nodes/ThrowStatement.js | 7 ++++ src/ast/nodes/index.js | 2 + src/ast/nodes/shared/Statement.js | 16 ++++++++ src/ast/nodes/shared/assignTo.js | 29 +++++++++++++ .../_config.js | 3 ++ .../_expected/amd.js | 28 +++++++++++++ .../_expected/cjs.js | 26 ++++++++++++ .../_expected/es.js | 24 +++++++++++ .../_expected/iife.js | 29 +++++++++++++ .../_expected/umd.js | 32 +++++++++++++++ .../main.js | 41 +++++++++++++++++++ .../effect-in-for-of-loop/_expected/amd.js | 17 ++++---- .../effect-in-for-of-loop/_expected/cjs.js | 17 ++++---- .../effect-in-for-of-loop/_expected/es.js | 17 ++++---- .../effect-in-for-of-loop/_expected/iife.js | 17 ++++---- .../effect-in-for-of-loop/_expected/umd.js | 17 ++++---- test/form/effect-in-for-of-loop/main.js | 28 +++++++------ 21 files changed, 309 insertions(+), 86 deletions(-) create mode 100644 src/ast/nodes/ThrowStatement.js create mode 100644 src/ast/nodes/shared/Statement.js create mode 100644 src/ast/nodes/shared/assignTo.js create mode 100644 test/form/effect-in-for-of-loop-in-functions/_config.js create mode 100644 test/form/effect-in-for-of-loop-in-functions/_expected/amd.js create mode 100644 test/form/effect-in-for-of-loop-in-functions/_expected/cjs.js create mode 100644 test/form/effect-in-for-of-loop-in-functions/_expected/es.js create mode 100644 test/form/effect-in-for-of-loop-in-functions/_expected/iife.js create mode 100644 test/form/effect-in-for-of-loop-in-functions/_expected/umd.js create mode 100644 test/form/effect-in-for-of-loop-in-functions/main.js diff --git a/src/ast/nodes/ExpressionStatement.js b/src/ast/nodes/ExpressionStatement.js index c5c3255..237441a 100644 --- a/src/ast/nodes/ExpressionStatement.js +++ b/src/ast/nodes/ExpressionStatement.js @@ -1,16 +1,5 @@ -import Node from '../Node.js'; +import Statement from './shared/Statement.js'; -export default class ExpressionStatement extends Node { - render ( code, es ) { - if ( !this.module.bundle.treeshake || this.shouldInclude ) { - super.render( code, es ); - } else { - code.remove( this.leadingCommentStart || this.start, this.next || this.end ); - } - } +export default class ExpressionStatement extends Statement { - run ( scope ) { - this.shouldInclude = true; - super.run( scope ); - } } diff --git a/src/ast/nodes/ForInStatement.js b/src/ast/nodes/ForInStatement.js index 002dd0a..a59bfd2 100644 --- a/src/ast/nodes/ForInStatement.js +++ b/src/ast/nodes/ForInStatement.js @@ -1,15 +1,10 @@ -import Node from '../Node.js'; +import Statement from './shared/Statement.js'; +import assignTo from './shared/assignTo.js'; import { STRING } from '../values.js'; -export default class ForInStatement extends Node { +export default class ForInStatement extends Statement { initialise ( scope ) { super.initialise( scope ); - - // special case - if ( this.left.type === 'VariableDeclaration' ) { - for ( const proxy of this.left.declarations[0].proxies.values() ) { - proxy.possibleValues.add( STRING ); - } - } + assignTo( this.left, scope, STRING ); } } diff --git a/src/ast/nodes/ForOfStatement.js b/src/ast/nodes/ForOfStatement.js index 201d491..9e663a0 100644 --- a/src/ast/nodes/ForOfStatement.js +++ b/src/ast/nodes/ForOfStatement.js @@ -1,15 +1,10 @@ -import Node from '../Node.js'; +import Statement from './shared/Statement.js'; +import assignTo from './shared/assignTo.js'; import { UNKNOWN } from '../values.js'; -export default class ForOfStatement extends Node { +export default class ForOfStatement extends Statement { initialise ( scope ) { super.initialise( scope ); - - // special case - if ( this.left.type === 'VariableDeclaration' ) { - for ( const proxy of this.left.declarations[0].proxies.values() ) { - proxy.possibleValues.add( UNKNOWN ); - } - } + assignTo( this.left, scope, UNKNOWN ); } } diff --git a/src/ast/nodes/IfStatement.js b/src/ast/nodes/IfStatement.js index 1a5c184..9e6f0fc 100644 --- a/src/ast/nodes/IfStatement.js +++ b/src/ast/nodes/IfStatement.js @@ -1,8 +1,8 @@ -import Node from '../Node.js'; +import Statement from './shared/Statement.js'; import { UNKNOWN } from '../values.js'; // TODO DRY this out -export default class IfStatement extends Node { +export default class IfStatement extends Statement { initialise ( scope ) { this.testValue = this.test.getValue(); diff --git a/src/ast/nodes/ThrowStatement.js b/src/ast/nodes/ThrowStatement.js new file mode 100644 index 0000000..4e9047b --- /dev/null +++ b/src/ast/nodes/ThrowStatement.js @@ -0,0 +1,7 @@ +import Node from '../Node.js'; + +export default class ThrowStatement extends Node { + hasEffects ( scope ) { + return scope.findLexicalBoundary().isModuleScope; // TODO should this just be `true`? probably... + } +} diff --git a/src/ast/nodes/index.js b/src/ast/nodes/index.js index 3cca1ad..b5babe0 100644 --- a/src/ast/nodes/index.js +++ b/src/ast/nodes/index.js @@ -26,6 +26,7 @@ import ParenthesizedExpression from './ParenthesizedExpression.js'; import ReturnStatement from './ReturnStatement.js'; import TemplateLiteral from './TemplateLiteral.js'; import ThisExpression from './ThisExpression.js'; +import ThrowStatement from './ThrowStatement.js'; import UnaryExpression from './UnaryExpression.js'; import UpdateExpression from './UpdateExpression.js'; import VariableDeclarator from './VariableDeclarator.js'; @@ -60,6 +61,7 @@ export default { ReturnStatement, TemplateLiteral, ThisExpression, + ThrowStatement, UnaryExpression, UpdateExpression, VariableDeclarator, diff --git a/src/ast/nodes/shared/Statement.js b/src/ast/nodes/shared/Statement.js new file mode 100644 index 0000000..5667652 --- /dev/null +++ b/src/ast/nodes/shared/Statement.js @@ -0,0 +1,16 @@ +import Node from '../../Node.js'; + +export default class Statement extends Node { + render ( code, es ) { + if ( !this.module.bundle.treeshake || this.shouldInclude ) { + super.render( code, es ); + } else { + code.remove( this.leadingCommentStart || this.start, this.next || this.end ); + } + } + + run ( scope ) { + this.shouldInclude = true; + super.run( scope ); + } +} diff --git a/src/ast/nodes/shared/assignTo.js b/src/ast/nodes/shared/assignTo.js new file mode 100644 index 0000000..03512e1 --- /dev/null +++ b/src/ast/nodes/shared/assignTo.js @@ -0,0 +1,29 @@ +import extractNames from '../../utils/extractNames.js'; + +export default function assignToForLoopLeft ( node, scope, value ) { + if ( node.type === 'VariableDeclaration' ) { + for ( const proxy of node.declarations[0].proxies.values() ) { + proxy.possibleValues.add( value ); + } + } + + else { + while ( node.type === 'ParenthesizedExpression' ) node = node.expression; + + if ( node.type === 'MemberExpression' ) { + // apparently this is legal JavaScript? Though I don't know what + // kind of monster would write `for ( foo.bar of thing ) {...}` + + // for now, do nothing, as I'm not sure anything needs to happen... + } + + else { + for ( const name of extractNames( node ) ) { + const declaration = scope.findDeclaration( name ); + if ( declaration.possibleValues ) { + declaration.possibleValues.add( value ); + } + } + } + } +} diff --git a/test/form/effect-in-for-of-loop-in-functions/_config.js b/test/form/effect-in-for-of-loop-in-functions/_config.js new file mode 100644 index 0000000..79abe74 --- /dev/null +++ b/test/form/effect-in-for-of-loop-in-functions/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'includes effects in for-of loop (#870)' +} diff --git a/test/form/effect-in-for-of-loop-in-functions/_expected/amd.js b/test/form/effect-in-for-of-loop-in-functions/_expected/amd.js new file mode 100644 index 0000000..a7c1b48 --- /dev/null +++ b/test/form/effect-in-for-of-loop-in-functions/_expected/amd.js @@ -0,0 +1,28 @@ +define(function () { 'use strict'; + + const items = [{}, {}, {}]; + + function a () { + for ( const item of items.children ) { + item.foo = 'a'; + } + } + + a(); + + function c () { + let item; + for ( item of items.children ) { + item.bar = 'c'; + } + } + + c(); + + assert.deepEqual( items, [ + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' } + ]); + +}); diff --git a/test/form/effect-in-for-of-loop-in-functions/_expected/cjs.js b/test/form/effect-in-for-of-loop-in-functions/_expected/cjs.js new file mode 100644 index 0000000..51c5f3b --- /dev/null +++ b/test/form/effect-in-for-of-loop-in-functions/_expected/cjs.js @@ -0,0 +1,26 @@ +'use strict'; + +const items = [{}, {}, {}]; + +function a () { + for ( const item of items.children ) { + item.foo = 'a'; + } +} + +a(); + +function c () { + let item; + for ( item of items.children ) { + item.bar = 'c'; + } +} + +c(); + +assert.deepEqual( items, [ + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' } +]); diff --git a/test/form/effect-in-for-of-loop-in-functions/_expected/es.js b/test/form/effect-in-for-of-loop-in-functions/_expected/es.js new file mode 100644 index 0000000..0aafee1 --- /dev/null +++ b/test/form/effect-in-for-of-loop-in-functions/_expected/es.js @@ -0,0 +1,24 @@ +const items = [{}, {}, {}]; + +function a () { + for ( const item of items.children ) { + item.foo = 'a'; + } +} + +a(); + +function c () { + let item; + for ( item of items.children ) { + item.bar = 'c'; + } +} + +c(); + +assert.deepEqual( items, [ + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' } +]); diff --git a/test/form/effect-in-for-of-loop-in-functions/_expected/iife.js b/test/form/effect-in-for-of-loop-in-functions/_expected/iife.js new file mode 100644 index 0000000..406ab3a --- /dev/null +++ b/test/form/effect-in-for-of-loop-in-functions/_expected/iife.js @@ -0,0 +1,29 @@ +(function () { + 'use strict'; + + const items = [{}, {}, {}]; + + function a () { + for ( const item of items.children ) { + item.foo = 'a'; + } + } + + a(); + + function c () { + let item; + for ( item of items.children ) { + item.bar = 'c'; + } + } + + c(); + + assert.deepEqual( items, [ + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' } + ]); + +}()); diff --git a/test/form/effect-in-for-of-loop-in-functions/_expected/umd.js b/test/form/effect-in-for-of-loop-in-functions/_expected/umd.js new file mode 100644 index 0000000..33518c0 --- /dev/null +++ b/test/form/effect-in-for-of-loop-in-functions/_expected/umd.js @@ -0,0 +1,32 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + const items = [{}, {}, {}]; + + function a () { + for ( const item of items.children ) { + item.foo = 'a'; + } + } + + a(); + + function c () { + let item; + for ( item of items.children ) { + item.bar = 'c'; + } + } + + c(); + + assert.deepEqual( items, [ + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' } + ]); + +}))); diff --git a/test/form/effect-in-for-of-loop-in-functions/main.js b/test/form/effect-in-for-of-loop-in-functions/main.js new file mode 100644 index 0000000..7ba1241 --- /dev/null +++ b/test/form/effect-in-for-of-loop-in-functions/main.js @@ -0,0 +1,41 @@ +const items = [{}, {}, {}]; + +function a () { + for ( const item of items.children ) { + item.foo = 'a'; + } +} + +a(); + +function b () { + for ( const item of items.children ) { + // do nothing + } +} + +b(); + +function c () { + let item; + for ( item of items.children ) { + item.bar = 'c'; + } +} + +c(); + +function d () { + let item; + for ( item of items.children ) { + // do nothing + } +} + +d(); + +assert.deepEqual( items, [ + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' } +]); diff --git a/test/form/effect-in-for-of-loop/_expected/amd.js b/test/form/effect-in-for-of-loop/_expected/amd.js index 57223a0..c252e26 100644 --- a/test/form/effect-in-for-of-loop/_expected/amd.js +++ b/test/form/effect-in-for-of-loop/_expected/amd.js @@ -2,18 +2,19 @@ define(function () { 'use strict'; const items = [{}, {}, {}]; - function x () { - for ( const item of items.children ) { - item.foo = 'bar'; - } + for ( const a of items.children ) { + a.foo = 'a'; } - x(); + let c; + for ( c of items.children ) { + c.bar = 'c'; + } assert.deepEqual( items, [ - { foo: 'bar' }, - { foo: 'bar' }, - { foo: 'bar' } + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' } ]); }); diff --git a/test/form/effect-in-for-of-loop/_expected/cjs.js b/test/form/effect-in-for-of-loop/_expected/cjs.js index fd94a9d..197b73e 100644 --- a/test/form/effect-in-for-of-loop/_expected/cjs.js +++ b/test/form/effect-in-for-of-loop/_expected/cjs.js @@ -2,16 +2,17 @@ const items = [{}, {}, {}]; -function x () { - for ( const item of items.children ) { - item.foo = 'bar'; - } +for ( const a of items.children ) { + a.foo = 'a'; } -x(); +let c; +for ( c of items.children ) { + c.bar = 'c'; +} assert.deepEqual( items, [ - { foo: 'bar' }, - { foo: 'bar' }, - { foo: 'bar' } + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' } ]); diff --git a/test/form/effect-in-for-of-loop/_expected/es.js b/test/form/effect-in-for-of-loop/_expected/es.js index cabffc4..3869419 100644 --- a/test/form/effect-in-for-of-loop/_expected/es.js +++ b/test/form/effect-in-for-of-loop/_expected/es.js @@ -1,15 +1,16 @@ const items = [{}, {}, {}]; -function x () { - for ( const item of items.children ) { - item.foo = 'bar'; - } +for ( const a of items.children ) { + a.foo = 'a'; } -x(); +let c; +for ( c of items.children ) { + c.bar = 'c'; +} assert.deepEqual( items, [ - { foo: 'bar' }, - { foo: 'bar' }, - { foo: 'bar' } + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' } ]); diff --git a/test/form/effect-in-for-of-loop/_expected/iife.js b/test/form/effect-in-for-of-loop/_expected/iife.js index 96e91ea..7ca26c8 100644 --- a/test/form/effect-in-for-of-loop/_expected/iife.js +++ b/test/form/effect-in-for-of-loop/_expected/iife.js @@ -3,18 +3,19 @@ const items = [{}, {}, {}]; - function x () { - for ( const item of items.children ) { - item.foo = 'bar'; - } + for ( const a of items.children ) { + a.foo = 'a'; } - x(); + let c; + for ( c of items.children ) { + c.bar = 'c'; + } assert.deepEqual( items, [ - { foo: 'bar' }, - { foo: 'bar' }, - { foo: 'bar' } + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' } ]); }()); diff --git a/test/form/effect-in-for-of-loop/_expected/umd.js b/test/form/effect-in-for-of-loop/_expected/umd.js index 83a6d30..08ef206 100644 --- a/test/form/effect-in-for-of-loop/_expected/umd.js +++ b/test/form/effect-in-for-of-loop/_expected/umd.js @@ -6,18 +6,19 @@ const items = [{}, {}, {}]; - function x () { - for ( const item of items.children ) { - item.foo = 'bar'; - } + for ( const a of items.children ) { + a.foo = 'a'; } - x(); + let c; + for ( c of items.children ) { + c.bar = 'c'; + } assert.deepEqual( items, [ - { foo: 'bar' }, - { foo: 'bar' }, - { foo: 'bar' } + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' } ]); }))); diff --git a/test/form/effect-in-for-of-loop/main.js b/test/form/effect-in-for-of-loop/main.js index 401dcf2..122bf0f 100644 --- a/test/form/effect-in-for-of-loop/main.js +++ b/test/form/effect-in-for-of-loop/main.js @@ -1,23 +1,25 @@ const items = [{}, {}, {}]; -function x () { - for ( const item of items.children ) { - item.foo = 'bar'; - } +for ( const a of items.children ) { + a.foo = 'a'; } -x(); +for ( const b of items.children ) { + // do nothing +} -function y () { - for ( const item of items.children ) { - // do nothing - } +let c; +for ( c of items.children ) { + c.bar = 'c'; } -y(); +let d; +for ( d of items.children ) { + // do nothing +} assert.deepEqual( items, [ - { foo: 'bar' }, - { foo: 'bar' }, - { foo: 'bar' } + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' }, + { foo: 'a', bar: 'c' } ]); From 9d9cfccd32f85bd19dc53c5c0164d120687f2db6 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 18:32:43 -0400 Subject: [PATCH 61/99] remove empty statements --- src/ast/nodes/ArrowFunctionExpression.js | 29 +++++++++-------- src/ast/nodes/AssignmentExpression.js | 4 ++- src/ast/nodes/BlockStatement.js | 32 +++---------------- src/ast/nodes/EmptyStatement.js | 7 ++++ src/ast/nodes/ForInStatement.js | 5 +-- src/ast/nodes/ForOfStatement.js | 5 +-- src/ast/nodes/ForStatement.js | 31 ++++++++++++++++++ src/ast/nodes/FunctionDeclaration.js | 4 +-- src/ast/nodes/FunctionExpression.js | 4 +-- src/ast/nodes/index.js | 11 ++++++- test/form/empty-block-statement/_config.js | 3 ++ .../empty-block-statement/_expected/amd.js | 6 ++++ .../empty-block-statement/_expected/cjs.js | 4 +++ .../empty-block-statement/_expected/es.js | 2 ++ .../empty-block-statement/_expected/iife.js | 7 ++++ .../empty-block-statement/_expected/umd.js | 10 ++++++ test/form/empty-block-statement/main.js | 5 +++ test/form/empty-do-while-statement/_config.js | 3 ++ .../empty-do-while-statement/_expected/amd.js | 6 ++++ .../empty-do-while-statement/_expected/cjs.js | 4 +++ .../empty-do-while-statement/_expected/es.js | 2 ++ .../_expected/iife.js | 7 ++++ .../empty-do-while-statement/_expected/umd.js | 10 ++++++ test/form/empty-do-while-statement/main.js | 6 ++++ test/form/empty-for-in-statement/_config.js | 3 ++ .../empty-for-in-statement/_expected/amd.js | 6 ++++ .../empty-for-in-statement/_expected/cjs.js | 4 +++ .../empty-for-in-statement/_expected/es.js | 2 ++ .../empty-for-in-statement/_expected/iife.js | 7 ++++ .../empty-for-in-statement/_expected/umd.js | 10 ++++++ test/form/empty-for-in-statement/main.js | 5 +++ test/form/empty-for-of-statement/_config.js | 3 ++ .../empty-for-of-statement/_expected/amd.js | 6 ++++ .../empty-for-of-statement/_expected/cjs.js | 4 +++ .../empty-for-of-statement/_expected/es.js | 2 ++ .../empty-for-of-statement/_expected/iife.js | 7 ++++ .../empty-for-of-statement/_expected/umd.js | 10 ++++++ test/form/empty-for-of-statement/main.js | 5 +++ test/form/empty-for-statement/_config.js | 3 ++ .../form/empty-for-statement/_expected/amd.js | 6 ++++ .../form/empty-for-statement/_expected/cjs.js | 4 +++ test/form/empty-for-statement/_expected/es.js | 2 ++ .../empty-for-statement/_expected/iife.js | 7 ++++ .../form/empty-for-statement/_expected/umd.js | 10 ++++++ test/form/empty-for-statement/main.js | 5 +++ test/form/empty-if-statement/_config.js | 3 ++ test/form/empty-if-statement/_expected/amd.js | 6 ++++ test/form/empty-if-statement/_expected/cjs.js | 4 +++ test/form/empty-if-statement/_expected/es.js | 2 ++ .../form/empty-if-statement/_expected/iife.js | 7 ++++ test/form/empty-if-statement/_expected/umd.js | 10 ++++++ test/form/empty-if-statement/main.js | 5 +++ test/form/empty-statement/_config.js | 3 ++ test/form/empty-statement/_expected/amd.js | 7 ++++ test/form/empty-statement/_expected/cjs.js | 5 +++ test/form/empty-statement/_expected/es.js | 3 ++ test/form/empty-statement/_expected/iife.js | 8 +++++ test/form/empty-statement/_expected/umd.js | 11 +++++++ test/form/empty-statement/main.js | 4 +++ test/form/empty-switch-statement/_config.js | 3 ++ .../empty-switch-statement/_expected/amd.js | 6 ++++ .../empty-switch-statement/_expected/cjs.js | 4 +++ .../empty-switch-statement/_expected/es.js | 2 ++ .../empty-switch-statement/_expected/iife.js | 7 ++++ .../empty-switch-statement/_expected/umd.js | 10 ++++++ test/form/empty-switch-statement/main.js | 11 +++++++ .../form/empty-try-catch-statement/_config.js | 3 ++ .../_expected/amd.js | 6 ++++ .../_expected/cjs.js | 4 +++ .../empty-try-catch-statement/_expected/es.js | 2 ++ .../_expected/iife.js | 7 ++++ .../_expected/umd.js | 10 ++++++ test/form/empty-try-catch-statement/main.js | 9 ++++++ test/form/empty-while-statement/_config.js | 3 ++ .../empty-while-statement/_expected/amd.js | 6 ++++ .../empty-while-statement/_expected/cjs.js | 4 +++ .../empty-while-statement/_expected/es.js | 2 ++ .../empty-while-statement/_expected/iife.js | 7 ++++ .../empty-while-statement/_expected/umd.js | 10 ++++++ test/form/empty-while-statement/main.js | 6 ++++ .../_expected/amd.js | 4 +-- .../_expected/cjs.js | 4 +-- .../_expected/es.js | 4 +-- .../_expected/iife.js | 4 +-- .../_expected/umd.js | 4 +-- .../_expected/amd.js | 2 +- .../_expected/cjs.js | 2 +- .../_expected/es.js | 2 +- .../_expected/iife.js | 2 +- .../_expected/umd.js | 4 +-- 90 files changed, 484 insertions(+), 66 deletions(-) create mode 100644 src/ast/nodes/EmptyStatement.js create mode 100644 src/ast/nodes/ForStatement.js create mode 100644 test/form/empty-block-statement/_config.js create mode 100644 test/form/empty-block-statement/_expected/amd.js create mode 100644 test/form/empty-block-statement/_expected/cjs.js create mode 100644 test/form/empty-block-statement/_expected/es.js create mode 100644 test/form/empty-block-statement/_expected/iife.js create mode 100644 test/form/empty-block-statement/_expected/umd.js create mode 100644 test/form/empty-block-statement/main.js create mode 100644 test/form/empty-do-while-statement/_config.js create mode 100644 test/form/empty-do-while-statement/_expected/amd.js create mode 100644 test/form/empty-do-while-statement/_expected/cjs.js create mode 100644 test/form/empty-do-while-statement/_expected/es.js create mode 100644 test/form/empty-do-while-statement/_expected/iife.js create mode 100644 test/form/empty-do-while-statement/_expected/umd.js create mode 100644 test/form/empty-do-while-statement/main.js create mode 100644 test/form/empty-for-in-statement/_config.js create mode 100644 test/form/empty-for-in-statement/_expected/amd.js create mode 100644 test/form/empty-for-in-statement/_expected/cjs.js create mode 100644 test/form/empty-for-in-statement/_expected/es.js create mode 100644 test/form/empty-for-in-statement/_expected/iife.js create mode 100644 test/form/empty-for-in-statement/_expected/umd.js create mode 100644 test/form/empty-for-in-statement/main.js create mode 100644 test/form/empty-for-of-statement/_config.js create mode 100644 test/form/empty-for-of-statement/_expected/amd.js create mode 100644 test/form/empty-for-of-statement/_expected/cjs.js create mode 100644 test/form/empty-for-of-statement/_expected/es.js create mode 100644 test/form/empty-for-of-statement/_expected/iife.js create mode 100644 test/form/empty-for-of-statement/_expected/umd.js create mode 100644 test/form/empty-for-of-statement/main.js create mode 100644 test/form/empty-for-statement/_config.js create mode 100644 test/form/empty-for-statement/_expected/amd.js create mode 100644 test/form/empty-for-statement/_expected/cjs.js create mode 100644 test/form/empty-for-statement/_expected/es.js create mode 100644 test/form/empty-for-statement/_expected/iife.js create mode 100644 test/form/empty-for-statement/_expected/umd.js create mode 100644 test/form/empty-for-statement/main.js create mode 100644 test/form/empty-if-statement/_config.js create mode 100644 test/form/empty-if-statement/_expected/amd.js create mode 100644 test/form/empty-if-statement/_expected/cjs.js create mode 100644 test/form/empty-if-statement/_expected/es.js create mode 100644 test/form/empty-if-statement/_expected/iife.js create mode 100644 test/form/empty-if-statement/_expected/umd.js create mode 100644 test/form/empty-if-statement/main.js create mode 100644 test/form/empty-statement/_config.js create mode 100644 test/form/empty-statement/_expected/amd.js create mode 100644 test/form/empty-statement/_expected/cjs.js create mode 100644 test/form/empty-statement/_expected/es.js create mode 100644 test/form/empty-statement/_expected/iife.js create mode 100644 test/form/empty-statement/_expected/umd.js create mode 100644 test/form/empty-statement/main.js create mode 100644 test/form/empty-switch-statement/_config.js create mode 100644 test/form/empty-switch-statement/_expected/amd.js create mode 100644 test/form/empty-switch-statement/_expected/cjs.js create mode 100644 test/form/empty-switch-statement/_expected/es.js create mode 100644 test/form/empty-switch-statement/_expected/iife.js create mode 100644 test/form/empty-switch-statement/_expected/umd.js create mode 100644 test/form/empty-switch-statement/main.js create mode 100644 test/form/empty-try-catch-statement/_config.js create mode 100644 test/form/empty-try-catch-statement/_expected/amd.js create mode 100644 test/form/empty-try-catch-statement/_expected/cjs.js create mode 100644 test/form/empty-try-catch-statement/_expected/es.js create mode 100644 test/form/empty-try-catch-statement/_expected/iife.js create mode 100644 test/form/empty-try-catch-statement/_expected/umd.js create mode 100644 test/form/empty-try-catch-statement/main.js create mode 100644 test/form/empty-while-statement/_config.js create mode 100644 test/form/empty-while-statement/_expected/amd.js create mode 100644 test/form/empty-while-statement/_expected/cjs.js create mode 100644 test/form/empty-while-statement/_expected/es.js create mode 100644 test/form/empty-while-statement/_expected/iife.js create mode 100644 test/form/empty-while-statement/_expected/umd.js create mode 100644 test/form/empty-while-statement/main.js diff --git a/src/ast/nodes/ArrowFunctionExpression.js b/src/ast/nodes/ArrowFunctionExpression.js index 2071965..257c7a3 100644 --- a/src/ast/nodes/ArrowFunctionExpression.js +++ b/src/ast/nodes/ArrowFunctionExpression.js @@ -3,8 +3,22 @@ import Scope from '../scopes/Scope.js'; import extractNames from '../utils/extractNames.js'; export default class ArrowFunctionExpression extends Node { + bind ( scope ) { + super.bind( this.scope || scope ); + } + + findScope ( functionScope ) { + return this.scope || this.parent.findScope( functionScope ); + } + + hasEffects () { + return false; + } + initialise ( scope ) { - if ( this.body.type !== 'BlockStatement' ) { + if ( this.body.type === 'BlockStatement' ) { + this.body.createScope( scope ); + } else { this.scope = new Scope({ parent: scope, isBlockScope: false, @@ -18,18 +32,7 @@ export default class ArrowFunctionExpression extends Node { } } + scope = this.scope || this.body.scope; super.initialise( scope ); } - - bind ( scope ) { - super.bind( this.scope || scope ); - } - - findScope ( functionScope ) { - return this.scope || this.parent.findScope( functionScope ); - } - - hasEffects () { - return false; - } } diff --git a/src/ast/nodes/AssignmentExpression.js b/src/ast/nodes/AssignmentExpression.js index b8dc0e2..f1fbcf3 100644 --- a/src/ast/nodes/AssignmentExpression.js +++ b/src/ast/nodes/AssignmentExpression.js @@ -35,11 +35,13 @@ export default class AssignmentExpression extends Node { } initialise ( scope ) { + this.scope = scope; + this.module.bundle.dependentExpressions.push( this ); super.initialise( scope ); } isUsedByBundle () { - return isUsedByBundle( this.findScope(), this.subject ); + return isUsedByBundle( this.scope, this.subject ); } } diff --git a/src/ast/nodes/BlockStatement.js b/src/ast/nodes/BlockStatement.js index ea341d5..78e6e67 100644 --- a/src/ast/nodes/BlockStatement.js +++ b/src/ast/nodes/BlockStatement.js @@ -1,8 +1,8 @@ -import Node from '../Node.js'; +import Statement from './shared/Statement.js'; import Scope from '../scopes/Scope.js'; import extractNames from '../utils/extractNames.js'; -export default class BlockStatement extends Node { +export default class BlockStatement extends Statement { bind () { for ( const node of this.body ) { node.bind( this.scope ); @@ -14,9 +14,9 @@ export default class BlockStatement extends Node { this.isFunctionBlock = this.parentIsFunction || this.parent.type === 'Module'; this.scope = new Scope({ + parent, isBlockScope: !this.isFunctionBlock, isLexicalBoundary: this.isFunctionBlock && this.parent.type !== 'ArrowFunctionExpression', - parent: parent || this.parent.findScope( false ), // TODO always supply parent owner: this // TODO is this used anywhere? }); @@ -35,14 +35,8 @@ export default class BlockStatement extends Node { return functionScope && !this.isFunctionBlock ? this.parent.findScope( functionScope ) : this.scope; } - hasEffects () { - for ( const node of this.body ) { - if ( node.hasEffects( this.scope ) ) return true; - } - } - - initialise () { - if ( !this.scope ) this.createScope(); // scope can be created early in some cases, e.g for (let i... ) + initialise ( scope ) { + if ( !this.scope ) this.createScope( scope ); // scope can be created early in some cases, e.g for (let i... ) let lastNode; for ( const node of this.body ) { @@ -52,20 +46,4 @@ export default class BlockStatement extends Node { lastNode = node; } } - - render ( code, es ) { - for ( const node of this.body ) { - node.render( code, es ); - } - } - - run () { - if ( this.ran ) return; - this.ran = true; - - for ( const node of this.body ) { - // TODO only include non-top-level statements if necessary - node.run( this.scope ); - } - } } diff --git a/src/ast/nodes/EmptyStatement.js b/src/ast/nodes/EmptyStatement.js new file mode 100644 index 0000000..27830c4 --- /dev/null +++ b/src/ast/nodes/EmptyStatement.js @@ -0,0 +1,7 @@ +import Statement from './shared/Statement.js'; + +export default class EmptyStatement extends Statement { + render ( code ) { + code.remove( this.start, this.end ); + } +} diff --git a/src/ast/nodes/ForInStatement.js b/src/ast/nodes/ForInStatement.js index a59bfd2..0838aa1 100644 --- a/src/ast/nodes/ForInStatement.js +++ b/src/ast/nodes/ForInStatement.js @@ -4,7 +4,8 @@ import { STRING } from '../values.js'; export default class ForInStatement extends Statement { initialise ( scope ) { - super.initialise( scope ); - assignTo( this.left, scope, STRING ); + this.body.createScope( scope ); + super.initialise( this.body.scope ); + assignTo( this.left, this.body.scope, STRING ); } } diff --git a/src/ast/nodes/ForOfStatement.js b/src/ast/nodes/ForOfStatement.js index 9e663a0..e761433 100644 --- a/src/ast/nodes/ForOfStatement.js +++ b/src/ast/nodes/ForOfStatement.js @@ -4,7 +4,8 @@ import { UNKNOWN } from '../values.js'; export default class ForOfStatement extends Statement { initialise ( scope ) { - super.initialise( scope ); - assignTo( this.left, scope, UNKNOWN ); + this.body.createScope( scope ); + super.initialise( this.body.scope ); + assignTo( this.left, this.body.scope, UNKNOWN ); } } diff --git a/src/ast/nodes/ForStatement.js b/src/ast/nodes/ForStatement.js new file mode 100644 index 0000000..bd5a57f --- /dev/null +++ b/src/ast/nodes/ForStatement.js @@ -0,0 +1,31 @@ +import Statement from './shared/Statement.js'; + +export default class ForStatement extends Statement { + bind () { + const scope = this.body.scope; + + this.init.bind( scope ); + this.test.bind( scope ); + this.update.bind( scope ); + this.body.bind( scope ); + } + + hasEffects () { + return super.hasEffects( this.body.scope ); + } + + initialise ( scope ) { + this.body.createScope( scope ); + scope = this.body.scope; + + // can't use super, because we need to control the order + this.init.initialise( scope ); + this.test.initialise( scope ); + this.update.initialise( scope ); + this.body.initialise( scope ); + } + + run ( scope ) { + super.run( scope ); + } +} diff --git a/src/ast/nodes/FunctionDeclaration.js b/src/ast/nodes/FunctionDeclaration.js index 96cba0c..dffed95 100644 --- a/src/ast/nodes/FunctionDeclaration.js +++ b/src/ast/nodes/FunctionDeclaration.js @@ -36,11 +36,11 @@ export default class FunctionDeclaration extends Node { this.name = this.id.name; // may be overridden by bundle.deconflict scope.addDeclaration( this.name, this, false, false ); - this.body.createScope(); + this.body.createScope( scope ); this.id.initialise( scope ); this.params.forEach( param => param.initialise( this.body.scope ) ); - this.body.initialise( scope ); + this.body.initialise(); } render ( code, es ) { diff --git a/src/ast/nodes/FunctionExpression.js b/src/ast/nodes/FunctionExpression.js index 56c87ee..4d170af 100644 --- a/src/ast/nodes/FunctionExpression.js +++ b/src/ast/nodes/FunctionExpression.js @@ -11,8 +11,8 @@ export default class FunctionExpression extends Node { return false; } - initialise () { - this.body.createScope(); // TODO we'll also need to do this for For[Of|In]Statement + initialise ( scope ) { + this.body.createScope( scope ); if ( this.id ) this.id.initialise( this.body.scope ); this.params.forEach( param => param.initialise( this.body.scope ) ); diff --git a/src/ast/nodes/index.js b/src/ast/nodes/index.js index b5babe0..94d2f4c 100644 --- a/src/ast/nodes/index.js +++ b/src/ast/nodes/index.js @@ -7,10 +7,12 @@ import CallExpression from './CallExpression.js'; import ClassDeclaration from './ClassDeclaration.js'; import ClassExpression from './ClassExpression.js'; import ConditionalExpression from './ConditionalExpression.js'; +import EmptyStatement from './EmptyStatement.js'; import ExportAllDeclaration from './ExportAllDeclaration.js'; import ExportDefaultDeclaration from './ExportDefaultDeclaration.js'; import ExportNamedDeclaration from './ExportNamedDeclaration.js'; import ExpressionStatement from './ExpressionStatement.js'; +import ForStatement from './ForStatement.js'; import ForInStatement from './ForInStatement.js'; import ForOfStatement from './ForOfStatement.js'; import FunctionDeclaration from './FunctionDeclaration.js'; @@ -24,6 +26,7 @@ import NewExpression from './NewExpression.js'; import ObjectExpression from './ObjectExpression.js'; import ParenthesizedExpression from './ParenthesizedExpression.js'; import ReturnStatement from './ReturnStatement.js'; +import Statement from './shared/Statement.js'; import TemplateLiteral from './TemplateLiteral.js'; import ThisExpression from './ThisExpression.js'; import ThrowStatement from './ThrowStatement.js'; @@ -42,10 +45,13 @@ export default { ClassDeclaration, ClassExpression, ConditionalExpression, + DoWhileStatement: Statement, + EmptyStatement, ExportAllDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExpressionStatement, + ForStatement, ForInStatement, ForOfStatement, FunctionDeclaration, @@ -59,11 +65,14 @@ export default { ObjectExpression, ParenthesizedExpression, ReturnStatement, + SwitchStatement: Statement, TemplateLiteral, ThisExpression, ThrowStatement, + TryStatement: Statement, UnaryExpression, UpdateExpression, VariableDeclarator, - VariableDeclaration + VariableDeclaration, + WhileStatement: Statement }; diff --git a/test/form/empty-block-statement/_config.js b/test/form/empty-block-statement/_config.js new file mode 100644 index 0000000..283b0b6 --- /dev/null +++ b/test/form/empty-block-statement/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'removes an empty block statement' +}; diff --git a/test/form/empty-block-statement/_expected/amd.js b/test/form/empty-block-statement/_expected/amd.js new file mode 100644 index 0000000..6036f28 --- /dev/null +++ b/test/form/empty-block-statement/_expected/amd.js @@ -0,0 +1,6 @@ +define(function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}); diff --git a/test/form/empty-block-statement/_expected/cjs.js b/test/form/empty-block-statement/_expected/cjs.js new file mode 100644 index 0000000..33f9de0 --- /dev/null +++ b/test/form/empty-block-statement/_expected/cjs.js @@ -0,0 +1,4 @@ +'use strict'; + +console.log( 1 ); +console.log( 2 ); diff --git a/test/form/empty-block-statement/_expected/es.js b/test/form/empty-block-statement/_expected/es.js new file mode 100644 index 0000000..472e544 --- /dev/null +++ b/test/form/empty-block-statement/_expected/es.js @@ -0,0 +1,2 @@ +console.log( 1 ); +console.log( 2 ); diff --git a/test/form/empty-block-statement/_expected/iife.js b/test/form/empty-block-statement/_expected/iife.js new file mode 100644 index 0000000..08a84a9 --- /dev/null +++ b/test/form/empty-block-statement/_expected/iife.js @@ -0,0 +1,7 @@ +(function () { + 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}()); diff --git a/test/form/empty-block-statement/_expected/umd.js b/test/form/empty-block-statement/_expected/umd.js new file mode 100644 index 0000000..0aae43f --- /dev/null +++ b/test/form/empty-block-statement/_expected/umd.js @@ -0,0 +1,10 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}))); diff --git a/test/form/empty-block-statement/main.js b/test/form/empty-block-statement/main.js new file mode 100644 index 0000000..edca010 --- /dev/null +++ b/test/form/empty-block-statement/main.js @@ -0,0 +1,5 @@ +console.log( 1 ); +{ + // empty +} +console.log( 2 ); diff --git a/test/form/empty-do-while-statement/_config.js b/test/form/empty-do-while-statement/_config.js new file mode 100644 index 0000000..00672ed --- /dev/null +++ b/test/form/empty-do-while-statement/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'removes an empty do-while statement' +}; diff --git a/test/form/empty-do-while-statement/_expected/amd.js b/test/form/empty-do-while-statement/_expected/amd.js new file mode 100644 index 0000000..e44e556 --- /dev/null +++ b/test/form/empty-do-while-statement/_expected/amd.js @@ -0,0 +1,6 @@ +define(function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}); \ No newline at end of file diff --git a/test/form/empty-do-while-statement/_expected/cjs.js b/test/form/empty-do-while-statement/_expected/cjs.js new file mode 100644 index 0000000..1afb143 --- /dev/null +++ b/test/form/empty-do-while-statement/_expected/cjs.js @@ -0,0 +1,4 @@ +'use strict'; + +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-do-while-statement/_expected/es.js b/test/form/empty-do-while-statement/_expected/es.js new file mode 100644 index 0000000..f97f3d0 --- /dev/null +++ b/test/form/empty-do-while-statement/_expected/es.js @@ -0,0 +1,2 @@ +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-do-while-statement/_expected/iife.js b/test/form/empty-do-while-statement/_expected/iife.js new file mode 100644 index 0000000..39aedbf --- /dev/null +++ b/test/form/empty-do-while-statement/_expected/iife.js @@ -0,0 +1,7 @@ +(function () { + 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}()); \ No newline at end of file diff --git a/test/form/empty-do-while-statement/_expected/umd.js b/test/form/empty-do-while-statement/_expected/umd.js new file mode 100644 index 0000000..0445193 --- /dev/null +++ b/test/form/empty-do-while-statement/_expected/umd.js @@ -0,0 +1,10 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}))); \ No newline at end of file diff --git a/test/form/empty-do-while-statement/main.js b/test/form/empty-do-while-statement/main.js new file mode 100644 index 0000000..8b04429 --- /dev/null +++ b/test/form/empty-do-while-statement/main.js @@ -0,0 +1,6 @@ +console.log( 1 ); +var condition = true; +do { + condition = false; +} while ( condition ); +console.log( 2 ); diff --git a/test/form/empty-for-in-statement/_config.js b/test/form/empty-for-in-statement/_config.js new file mode 100644 index 0000000..b95837c --- /dev/null +++ b/test/form/empty-for-in-statement/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'removes an empty for-in statement' +}; diff --git a/test/form/empty-for-in-statement/_expected/amd.js b/test/form/empty-for-in-statement/_expected/amd.js new file mode 100644 index 0000000..e44e556 --- /dev/null +++ b/test/form/empty-for-in-statement/_expected/amd.js @@ -0,0 +1,6 @@ +define(function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}); \ No newline at end of file diff --git a/test/form/empty-for-in-statement/_expected/cjs.js b/test/form/empty-for-in-statement/_expected/cjs.js new file mode 100644 index 0000000..1afb143 --- /dev/null +++ b/test/form/empty-for-in-statement/_expected/cjs.js @@ -0,0 +1,4 @@ +'use strict'; + +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-for-in-statement/_expected/es.js b/test/form/empty-for-in-statement/_expected/es.js new file mode 100644 index 0000000..f97f3d0 --- /dev/null +++ b/test/form/empty-for-in-statement/_expected/es.js @@ -0,0 +1,2 @@ +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-for-in-statement/_expected/iife.js b/test/form/empty-for-in-statement/_expected/iife.js new file mode 100644 index 0000000..39aedbf --- /dev/null +++ b/test/form/empty-for-in-statement/_expected/iife.js @@ -0,0 +1,7 @@ +(function () { + 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}()); \ No newline at end of file diff --git a/test/form/empty-for-in-statement/_expected/umd.js b/test/form/empty-for-in-statement/_expected/umd.js new file mode 100644 index 0000000..0445193 --- /dev/null +++ b/test/form/empty-for-in-statement/_expected/umd.js @@ -0,0 +1,10 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}))); \ No newline at end of file diff --git a/test/form/empty-for-in-statement/main.js b/test/form/empty-for-in-statement/main.js new file mode 100644 index 0000000..cb706f7 --- /dev/null +++ b/test/form/empty-for-in-statement/main.js @@ -0,0 +1,5 @@ +console.log( 1 ); +for ( const i in whatever ) { + // do nothing +} +console.log( 2 ); diff --git a/test/form/empty-for-of-statement/_config.js b/test/form/empty-for-of-statement/_config.js new file mode 100644 index 0000000..4a7bd91 --- /dev/null +++ b/test/form/empty-for-of-statement/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'removes an empty for-of statement' +}; diff --git a/test/form/empty-for-of-statement/_expected/amd.js b/test/form/empty-for-of-statement/_expected/amd.js new file mode 100644 index 0000000..e44e556 --- /dev/null +++ b/test/form/empty-for-of-statement/_expected/amd.js @@ -0,0 +1,6 @@ +define(function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}); \ No newline at end of file diff --git a/test/form/empty-for-of-statement/_expected/cjs.js b/test/form/empty-for-of-statement/_expected/cjs.js new file mode 100644 index 0000000..1afb143 --- /dev/null +++ b/test/form/empty-for-of-statement/_expected/cjs.js @@ -0,0 +1,4 @@ +'use strict'; + +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-for-of-statement/_expected/es.js b/test/form/empty-for-of-statement/_expected/es.js new file mode 100644 index 0000000..f97f3d0 --- /dev/null +++ b/test/form/empty-for-of-statement/_expected/es.js @@ -0,0 +1,2 @@ +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-for-of-statement/_expected/iife.js b/test/form/empty-for-of-statement/_expected/iife.js new file mode 100644 index 0000000..39aedbf --- /dev/null +++ b/test/form/empty-for-of-statement/_expected/iife.js @@ -0,0 +1,7 @@ +(function () { + 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}()); \ No newline at end of file diff --git a/test/form/empty-for-of-statement/_expected/umd.js b/test/form/empty-for-of-statement/_expected/umd.js new file mode 100644 index 0000000..0445193 --- /dev/null +++ b/test/form/empty-for-of-statement/_expected/umd.js @@ -0,0 +1,10 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}))); \ No newline at end of file diff --git a/test/form/empty-for-of-statement/main.js b/test/form/empty-for-of-statement/main.js new file mode 100644 index 0000000..e591dde --- /dev/null +++ b/test/form/empty-for-of-statement/main.js @@ -0,0 +1,5 @@ +console.log( 1 ); +for ( const i of whatever ) { + // do nothing +} +console.log( 2 ); diff --git a/test/form/empty-for-statement/_config.js b/test/form/empty-for-statement/_config.js new file mode 100644 index 0000000..f448ddf --- /dev/null +++ b/test/form/empty-for-statement/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'removes an empty for statement' +}; diff --git a/test/form/empty-for-statement/_expected/amd.js b/test/form/empty-for-statement/_expected/amd.js new file mode 100644 index 0000000..6036f28 --- /dev/null +++ b/test/form/empty-for-statement/_expected/amd.js @@ -0,0 +1,6 @@ +define(function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}); diff --git a/test/form/empty-for-statement/_expected/cjs.js b/test/form/empty-for-statement/_expected/cjs.js new file mode 100644 index 0000000..33f9de0 --- /dev/null +++ b/test/form/empty-for-statement/_expected/cjs.js @@ -0,0 +1,4 @@ +'use strict'; + +console.log( 1 ); +console.log( 2 ); diff --git a/test/form/empty-for-statement/_expected/es.js b/test/form/empty-for-statement/_expected/es.js new file mode 100644 index 0000000..472e544 --- /dev/null +++ b/test/form/empty-for-statement/_expected/es.js @@ -0,0 +1,2 @@ +console.log( 1 ); +console.log( 2 ); diff --git a/test/form/empty-for-statement/_expected/iife.js b/test/form/empty-for-statement/_expected/iife.js new file mode 100644 index 0000000..08a84a9 --- /dev/null +++ b/test/form/empty-for-statement/_expected/iife.js @@ -0,0 +1,7 @@ +(function () { + 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}()); diff --git a/test/form/empty-for-statement/_expected/umd.js b/test/form/empty-for-statement/_expected/umd.js new file mode 100644 index 0000000..0aae43f --- /dev/null +++ b/test/form/empty-for-statement/_expected/umd.js @@ -0,0 +1,10 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}))); diff --git a/test/form/empty-for-statement/main.js b/test/form/empty-for-statement/main.js new file mode 100644 index 0000000..3b5d176 --- /dev/null +++ b/test/form/empty-for-statement/main.js @@ -0,0 +1,5 @@ +console.log( 1 ); +for ( let i = 0; i < 10; i += 1 ) { + // do nothing +} +console.log( 2 ); diff --git a/test/form/empty-if-statement/_config.js b/test/form/empty-if-statement/_config.js new file mode 100644 index 0000000..abea889 --- /dev/null +++ b/test/form/empty-if-statement/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'removes an empty if statement' +}; diff --git a/test/form/empty-if-statement/_expected/amd.js b/test/form/empty-if-statement/_expected/amd.js new file mode 100644 index 0000000..e44e556 --- /dev/null +++ b/test/form/empty-if-statement/_expected/amd.js @@ -0,0 +1,6 @@ +define(function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}); \ No newline at end of file diff --git a/test/form/empty-if-statement/_expected/cjs.js b/test/form/empty-if-statement/_expected/cjs.js new file mode 100644 index 0000000..1afb143 --- /dev/null +++ b/test/form/empty-if-statement/_expected/cjs.js @@ -0,0 +1,4 @@ +'use strict'; + +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-if-statement/_expected/es.js b/test/form/empty-if-statement/_expected/es.js new file mode 100644 index 0000000..f97f3d0 --- /dev/null +++ b/test/form/empty-if-statement/_expected/es.js @@ -0,0 +1,2 @@ +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-if-statement/_expected/iife.js b/test/form/empty-if-statement/_expected/iife.js new file mode 100644 index 0000000..39aedbf --- /dev/null +++ b/test/form/empty-if-statement/_expected/iife.js @@ -0,0 +1,7 @@ +(function () { + 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}()); \ No newline at end of file diff --git a/test/form/empty-if-statement/_expected/umd.js b/test/form/empty-if-statement/_expected/umd.js new file mode 100644 index 0000000..0445193 --- /dev/null +++ b/test/form/empty-if-statement/_expected/umd.js @@ -0,0 +1,10 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}))); \ No newline at end of file diff --git a/test/form/empty-if-statement/main.js b/test/form/empty-if-statement/main.js new file mode 100644 index 0000000..6db9dc1 --- /dev/null +++ b/test/form/empty-if-statement/main.js @@ -0,0 +1,5 @@ +console.log( 1 ); +if ( nothing ) { + // empty +} +console.log( 2 ); diff --git a/test/form/empty-statement/_config.js b/test/form/empty-statement/_config.js new file mode 100644 index 0000000..76e4fa3 --- /dev/null +++ b/test/form/empty-statement/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'removes an empty statement' +}; diff --git a/test/form/empty-statement/_expected/amd.js b/test/form/empty-statement/_expected/amd.js new file mode 100644 index 0000000..d7ee383 --- /dev/null +++ b/test/form/empty-statement/_expected/amd.js @@ -0,0 +1,7 @@ +define(function () { 'use strict'; + + console.log( 1 ); + + console.log( 2 ); + +}); diff --git a/test/form/empty-statement/_expected/cjs.js b/test/form/empty-statement/_expected/cjs.js new file mode 100644 index 0000000..2ad8cd2 --- /dev/null +++ b/test/form/empty-statement/_expected/cjs.js @@ -0,0 +1,5 @@ +'use strict'; + +console.log( 1 ); + +console.log( 2 ); diff --git a/test/form/empty-statement/_expected/es.js b/test/form/empty-statement/_expected/es.js new file mode 100644 index 0000000..b149085 --- /dev/null +++ b/test/form/empty-statement/_expected/es.js @@ -0,0 +1,3 @@ +console.log( 1 ); + +console.log( 2 ); diff --git a/test/form/empty-statement/_expected/iife.js b/test/form/empty-statement/_expected/iife.js new file mode 100644 index 0000000..c9dbff5 --- /dev/null +++ b/test/form/empty-statement/_expected/iife.js @@ -0,0 +1,8 @@ +(function () { + 'use strict'; + + console.log( 1 ); + + console.log( 2 ); + +}()); diff --git a/test/form/empty-statement/_expected/umd.js b/test/form/empty-statement/_expected/umd.js new file mode 100644 index 0000000..e97ebff --- /dev/null +++ b/test/form/empty-statement/_expected/umd.js @@ -0,0 +1,11 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + console.log( 1 ); + + console.log( 2 ); + +}))); diff --git a/test/form/empty-statement/main.js b/test/form/empty-statement/main.js new file mode 100644 index 0000000..43f7037 --- /dev/null +++ b/test/form/empty-statement/main.js @@ -0,0 +1,4 @@ +; +console.log( 1 );; +; +console.log( 2 );; diff --git a/test/form/empty-switch-statement/_config.js b/test/form/empty-switch-statement/_config.js new file mode 100644 index 0000000..29c0517 --- /dev/null +++ b/test/form/empty-switch-statement/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'removes an empty switch statement' +}; diff --git a/test/form/empty-switch-statement/_expected/amd.js b/test/form/empty-switch-statement/_expected/amd.js new file mode 100644 index 0000000..e44e556 --- /dev/null +++ b/test/form/empty-switch-statement/_expected/amd.js @@ -0,0 +1,6 @@ +define(function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}); \ No newline at end of file diff --git a/test/form/empty-switch-statement/_expected/cjs.js b/test/form/empty-switch-statement/_expected/cjs.js new file mode 100644 index 0000000..1afb143 --- /dev/null +++ b/test/form/empty-switch-statement/_expected/cjs.js @@ -0,0 +1,4 @@ +'use strict'; + +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-switch-statement/_expected/es.js b/test/form/empty-switch-statement/_expected/es.js new file mode 100644 index 0000000..f97f3d0 --- /dev/null +++ b/test/form/empty-switch-statement/_expected/es.js @@ -0,0 +1,2 @@ +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-switch-statement/_expected/iife.js b/test/form/empty-switch-statement/_expected/iife.js new file mode 100644 index 0000000..39aedbf --- /dev/null +++ b/test/form/empty-switch-statement/_expected/iife.js @@ -0,0 +1,7 @@ +(function () { + 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}()); \ No newline at end of file diff --git a/test/form/empty-switch-statement/_expected/umd.js b/test/form/empty-switch-statement/_expected/umd.js new file mode 100644 index 0000000..0445193 --- /dev/null +++ b/test/form/empty-switch-statement/_expected/umd.js @@ -0,0 +1,10 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}))); \ No newline at end of file diff --git a/test/form/empty-switch-statement/main.js b/test/form/empty-switch-statement/main.js new file mode 100644 index 0000000..58da4d4 --- /dev/null +++ b/test/form/empty-switch-statement/main.js @@ -0,0 +1,11 @@ +console.log( 1 ); +var result; +switch ( whatever ) { + case foo: + result = 'foo'; + break; + + default: + result = 'default'; +} +console.log( 2 ); diff --git a/test/form/empty-try-catch-statement/_config.js b/test/form/empty-try-catch-statement/_config.js new file mode 100644 index 0000000..70bf652 --- /dev/null +++ b/test/form/empty-try-catch-statement/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'removes an empty try-catch-finally statement' +}; diff --git a/test/form/empty-try-catch-statement/_expected/amd.js b/test/form/empty-try-catch-statement/_expected/amd.js new file mode 100644 index 0000000..e44e556 --- /dev/null +++ b/test/form/empty-try-catch-statement/_expected/amd.js @@ -0,0 +1,6 @@ +define(function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}); \ No newline at end of file diff --git a/test/form/empty-try-catch-statement/_expected/cjs.js b/test/form/empty-try-catch-statement/_expected/cjs.js new file mode 100644 index 0000000..1afb143 --- /dev/null +++ b/test/form/empty-try-catch-statement/_expected/cjs.js @@ -0,0 +1,4 @@ +'use strict'; + +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-try-catch-statement/_expected/es.js b/test/form/empty-try-catch-statement/_expected/es.js new file mode 100644 index 0000000..f97f3d0 --- /dev/null +++ b/test/form/empty-try-catch-statement/_expected/es.js @@ -0,0 +1,2 @@ +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-try-catch-statement/_expected/iife.js b/test/form/empty-try-catch-statement/_expected/iife.js new file mode 100644 index 0000000..39aedbf --- /dev/null +++ b/test/form/empty-try-catch-statement/_expected/iife.js @@ -0,0 +1,7 @@ +(function () { + 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}()); \ No newline at end of file diff --git a/test/form/empty-try-catch-statement/_expected/umd.js b/test/form/empty-try-catch-statement/_expected/umd.js new file mode 100644 index 0000000..0445193 --- /dev/null +++ b/test/form/empty-try-catch-statement/_expected/umd.js @@ -0,0 +1,10 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}))); \ No newline at end of file diff --git a/test/form/empty-try-catch-statement/main.js b/test/form/empty-try-catch-statement/main.js new file mode 100644 index 0000000..52ed8c1 --- /dev/null +++ b/test/form/empty-try-catch-statement/main.js @@ -0,0 +1,9 @@ +console.log( 1 ); +try { + // do nothing +} catch ( err ) { + // do nothing +} finally { + // do nothing +} +console.log( 2 ); diff --git a/test/form/empty-while-statement/_config.js b/test/form/empty-while-statement/_config.js new file mode 100644 index 0000000..8db2e7f --- /dev/null +++ b/test/form/empty-while-statement/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'removes an empty while statement' +}; diff --git a/test/form/empty-while-statement/_expected/amd.js b/test/form/empty-while-statement/_expected/amd.js new file mode 100644 index 0000000..e44e556 --- /dev/null +++ b/test/form/empty-while-statement/_expected/amd.js @@ -0,0 +1,6 @@ +define(function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}); \ No newline at end of file diff --git a/test/form/empty-while-statement/_expected/cjs.js b/test/form/empty-while-statement/_expected/cjs.js new file mode 100644 index 0000000..1afb143 --- /dev/null +++ b/test/form/empty-while-statement/_expected/cjs.js @@ -0,0 +1,4 @@ +'use strict'; + +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-while-statement/_expected/es.js b/test/form/empty-while-statement/_expected/es.js new file mode 100644 index 0000000..f97f3d0 --- /dev/null +++ b/test/form/empty-while-statement/_expected/es.js @@ -0,0 +1,2 @@ +console.log( 1 ); +console.log( 2 ); \ No newline at end of file diff --git a/test/form/empty-while-statement/_expected/iife.js b/test/form/empty-while-statement/_expected/iife.js new file mode 100644 index 0000000..39aedbf --- /dev/null +++ b/test/form/empty-while-statement/_expected/iife.js @@ -0,0 +1,7 @@ +(function () { + 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}()); \ No newline at end of file diff --git a/test/form/empty-while-statement/_expected/umd.js b/test/form/empty-while-statement/_expected/umd.js new file mode 100644 index 0000000..0445193 --- /dev/null +++ b/test/form/empty-while-statement/_expected/umd.js @@ -0,0 +1,10 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + console.log( 1 ); + console.log( 2 ); + +}))); \ No newline at end of file diff --git a/test/form/empty-while-statement/main.js b/test/form/empty-while-statement/main.js new file mode 100644 index 0000000..c499a7f --- /dev/null +++ b/test/form/empty-while-statement/main.js @@ -0,0 +1,6 @@ +console.log( 1 ); +var condition = true; +while ( condition ) { + condition = false; +} +console.log( 2 ); diff --git a/test/form/erroneous-nested-member-expression/_expected/amd.js b/test/form/erroneous-nested-member-expression/_expected/amd.js index 6499b90..4b402c7 100644 --- a/test/form/erroneous-nested-member-expression/_expected/amd.js +++ b/test/form/erroneous-nested-member-expression/_expected/amd.js @@ -6,8 +6,8 @@ define(function () { 'use strict'; console.log('har?'); } }; - }; + } yar.har(); -}); \ No newline at end of file +}); diff --git a/test/form/erroneous-nested-member-expression/_expected/cjs.js b/test/form/erroneous-nested-member-expression/_expected/cjs.js index aa4b949..01c96f9 100644 --- a/test/form/erroneous-nested-member-expression/_expected/cjs.js +++ b/test/form/erroneous-nested-member-expression/_expected/cjs.js @@ -6,6 +6,6 @@ function yar() { console.log('har?'); } }; -}; +} -yar.har(); \ No newline at end of file +yar.har(); diff --git a/test/form/erroneous-nested-member-expression/_expected/es.js b/test/form/erroneous-nested-member-expression/_expected/es.js index 34c4e2e..ee4eace 100644 --- a/test/form/erroneous-nested-member-expression/_expected/es.js +++ b/test/form/erroneous-nested-member-expression/_expected/es.js @@ -4,6 +4,6 @@ function yar() { console.log('har?'); } }; -}; +} -yar.har(); \ No newline at end of file +yar.har(); diff --git a/test/form/erroneous-nested-member-expression/_expected/iife.js b/test/form/erroneous-nested-member-expression/_expected/iife.js index 4233cfe..8a8d65e 100644 --- a/test/form/erroneous-nested-member-expression/_expected/iife.js +++ b/test/form/erroneous-nested-member-expression/_expected/iife.js @@ -7,8 +7,8 @@ console.log('har?'); } }; - }; + } yar.har(); -}()); \ No newline at end of file +}()); diff --git a/test/form/erroneous-nested-member-expression/_expected/umd.js b/test/form/erroneous-nested-member-expression/_expected/umd.js index 2a8dba1..94dee16 100644 --- a/test/form/erroneous-nested-member-expression/_expected/umd.js +++ b/test/form/erroneous-nested-member-expression/_expected/umd.js @@ -10,8 +10,8 @@ console.log('har?'); } }; - }; + } yar.har(); -}))); \ No newline at end of file +}))); diff --git a/test/form/spacing-after-function-with-semicolon/_expected/amd.js b/test/form/spacing-after-function-with-semicolon/_expected/amd.js index 37f42f3..4db965c 100644 --- a/test/form/spacing-after-function-with-semicolon/_expected/amd.js +++ b/test/form/spacing-after-function-with-semicolon/_expected/amd.js @@ -1,6 +1,6 @@ define(function () { 'use strict'; - function x () { return 'x' }; + function x () { return 'x' } assert.equal( x(), 'x' ); diff --git a/test/form/spacing-after-function-with-semicolon/_expected/cjs.js b/test/form/spacing-after-function-with-semicolon/_expected/cjs.js index 503ea73..bac28c1 100644 --- a/test/form/spacing-after-function-with-semicolon/_expected/cjs.js +++ b/test/form/spacing-after-function-with-semicolon/_expected/cjs.js @@ -1,5 +1,5 @@ 'use strict'; -function x () { return 'x' }; +function x () { return 'x' } assert.equal( x(), 'x' ); diff --git a/test/form/spacing-after-function-with-semicolon/_expected/es.js b/test/form/spacing-after-function-with-semicolon/_expected/es.js index 3b3f5e7..f5b5029 100644 --- a/test/form/spacing-after-function-with-semicolon/_expected/es.js +++ b/test/form/spacing-after-function-with-semicolon/_expected/es.js @@ -1,3 +1,3 @@ -function x () { return 'x' }; +function x () { return 'x' } assert.equal( x(), 'x' ); diff --git a/test/form/spacing-after-function-with-semicolon/_expected/iife.js b/test/form/spacing-after-function-with-semicolon/_expected/iife.js index 7ff78ec..f8d6992 100644 --- a/test/form/spacing-after-function-with-semicolon/_expected/iife.js +++ b/test/form/spacing-after-function-with-semicolon/_expected/iife.js @@ -1,7 +1,7 @@ (function () { 'use strict'; - function x () { return 'x' }; + function x () { return 'x' } assert.equal( x(), 'x' ); diff --git a/test/form/spacing-after-function-with-semicolon/_expected/umd.js b/test/form/spacing-after-function-with-semicolon/_expected/umd.js index 8b7954a..75d31a1 100644 --- a/test/form/spacing-after-function-with-semicolon/_expected/umd.js +++ b/test/form/spacing-after-function-with-semicolon/_expected/umd.js @@ -4,8 +4,8 @@ (factory()); }(this, (function () { 'use strict'; - function x () { return 'x' }; + function x () { return 'x' } assert.equal( x(), 'x' ); -}))); \ No newline at end of file +}))); From 4379d486f8b0523cbbcef00996ddd6ea43e8312b Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 18:42:14 -0400 Subject: [PATCH 62/99] -> v0.35.4 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 009ae46..d2fcbc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # rollup changelog +## 0.35.4 + +* Preserve effects in for-of and for-in loops ([#870](https://github.com/rollup/rollup/issues/870)) +* Remove empty statements ([#918](https://github.com/rollup/rollup/pull/918)) + ## 0.35.3 * Render identifiers inside template literals diff --git a/package.json b/package.json index bcb8ff7..097202f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.3", + "version": "0.35.4", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From eba7a70350ac22973421eb7a45a9187ad3d264c2 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 19:20:53 -0400 Subject: [PATCH 63/99] =?UTF-8?q?allow=20empty=20for=20loop=20heads=20?= =?UTF-8?q?=E2=80=93=20fixes=20#919?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ast/nodes/ForStatement.js | 15 +++------------ test/form/for-loop-with-empty-head/_config.js | 3 +++ .../for-loop-with-empty-head/_expected/amd.js | 7 +++++++ .../for-loop-with-empty-head/_expected/cjs.js | 5 +++++ .../form/for-loop-with-empty-head/_expected/es.js | 3 +++ .../for-loop-with-empty-head/_expected/iife.js | 8 ++++++++ .../for-loop-with-empty-head/_expected/umd.js | 11 +++++++++++ test/form/for-loop-with-empty-head/main.js | 3 +++ 8 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 test/form/for-loop-with-empty-head/_config.js create mode 100644 test/form/for-loop-with-empty-head/_expected/amd.js create mode 100644 test/form/for-loop-with-empty-head/_expected/cjs.js create mode 100644 test/form/for-loop-with-empty-head/_expected/es.js create mode 100644 test/form/for-loop-with-empty-head/_expected/iife.js create mode 100644 test/form/for-loop-with-empty-head/_expected/umd.js create mode 100644 test/form/for-loop-with-empty-head/main.js diff --git a/src/ast/nodes/ForStatement.js b/src/ast/nodes/ForStatement.js index bd5a57f..1d61b02 100644 --- a/src/ast/nodes/ForStatement.js +++ b/src/ast/nodes/ForStatement.js @@ -1,15 +1,6 @@ import Statement from './shared/Statement.js'; export default class ForStatement extends Statement { - bind () { - const scope = this.body.scope; - - this.init.bind( scope ); - this.test.bind( scope ); - this.update.bind( scope ); - this.body.bind( scope ); - } - hasEffects () { return super.hasEffects( this.body.scope ); } @@ -19,9 +10,9 @@ export default class ForStatement extends Statement { scope = this.body.scope; // can't use super, because we need to control the order - this.init.initialise( scope ); - this.test.initialise( scope ); - this.update.initialise( scope ); + if ( this.init ) this.init.initialise( scope ); + if ( this.test ) this.test.initialise( scope ); + if ( this.update ) this.update.initialise( scope ); this.body.initialise( scope ); } diff --git a/test/form/for-loop-with-empty-head/_config.js b/test/form/for-loop-with-empty-head/_config.js new file mode 100644 index 0000000..3fc4a67 --- /dev/null +++ b/test/form/for-loop-with-empty-head/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'handles for loop with empty head' +}; diff --git a/test/form/for-loop-with-empty-head/_expected/amd.js b/test/form/for-loop-with-empty-head/_expected/amd.js new file mode 100644 index 0000000..a07e3e0 --- /dev/null +++ b/test/form/for-loop-with-empty-head/_expected/amd.js @@ -0,0 +1,7 @@ +define(function () { 'use strict'; + + for ( ; ; ) { + console.log( 42 ); + } + +}); \ No newline at end of file diff --git a/test/form/for-loop-with-empty-head/_expected/cjs.js b/test/form/for-loop-with-empty-head/_expected/cjs.js new file mode 100644 index 0000000..f94dff7 --- /dev/null +++ b/test/form/for-loop-with-empty-head/_expected/cjs.js @@ -0,0 +1,5 @@ +'use strict'; + +for ( ; ; ) { + console.log( 42 ); +} \ No newline at end of file diff --git a/test/form/for-loop-with-empty-head/_expected/es.js b/test/form/for-loop-with-empty-head/_expected/es.js new file mode 100644 index 0000000..1690f25 --- /dev/null +++ b/test/form/for-loop-with-empty-head/_expected/es.js @@ -0,0 +1,3 @@ +for ( ; ; ) { + console.log( 42 ); +} \ No newline at end of file diff --git a/test/form/for-loop-with-empty-head/_expected/iife.js b/test/form/for-loop-with-empty-head/_expected/iife.js new file mode 100644 index 0000000..2217234 --- /dev/null +++ b/test/form/for-loop-with-empty-head/_expected/iife.js @@ -0,0 +1,8 @@ +(function () { + 'use strict'; + + for ( ; ; ) { + console.log( 42 ); + } + +}()); \ No newline at end of file diff --git a/test/form/for-loop-with-empty-head/_expected/umd.js b/test/form/for-loop-with-empty-head/_expected/umd.js new file mode 100644 index 0000000..3e90731 --- /dev/null +++ b/test/form/for-loop-with-empty-head/_expected/umd.js @@ -0,0 +1,11 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + for ( ; ; ) { + console.log( 42 ); + } + +}))); \ No newline at end of file diff --git a/test/form/for-loop-with-empty-head/main.js b/test/form/for-loop-with-empty-head/main.js new file mode 100644 index 0000000..5e492f8 --- /dev/null +++ b/test/form/for-loop-with-empty-head/main.js @@ -0,0 +1,3 @@ +for ( ; ; ) { + console.log( 42 ); +} From 7afadff2683b845fa90d0c6d302c834dbe361749 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 19:44:28 -0400 Subject: [PATCH 64/99] handle body-less for loops etc --- src/ast/Node.js | 8 +++-- src/ast/nodes/ArrowFunctionExpression.js | 4 +-- src/ast/nodes/ForInStatement.js | 17 +++++++++-- src/ast/nodes/ForOfStatement.js | 17 +++++++++-- src/ast/nodes/ForStatement.js | 29 ++++++++++--------- test/form/body-less-for-loops/_config.js | 3 ++ .../form/body-less-for-loops/_expected/amd.js | 16 ++++++++++ .../form/body-less-for-loops/_expected/cjs.js | 14 +++++++++ test/form/body-less-for-loops/_expected/es.js | 12 ++++++++ .../body-less-for-loops/_expected/iife.js | 17 +++++++++++ .../form/body-less-for-loops/_expected/umd.js | 20 +++++++++++++ test/form/body-less-for-loops/main.js | 12 ++++++++ 12 files changed, 144 insertions(+), 25 deletions(-) create mode 100644 test/form/body-less-for-loops/_config.js create mode 100644 test/form/body-less-for-loops/_expected/amd.js create mode 100644 test/form/body-less-for-loops/_expected/cjs.js create mode 100644 test/form/body-less-for-loops/_expected/es.js create mode 100644 test/form/body-less-for-loops/_expected/iife.js create mode 100644 test/form/body-less-for-loops/_expected/umd.js create mode 100644 test/form/body-less-for-loops/main.js diff --git a/src/ast/Node.js b/src/ast/Node.js index 0343f28..4f65871 100644 --- a/src/ast/Node.js +++ b/src/ast/Node.js @@ -3,7 +3,7 @@ import getLocation from '../utils/getLocation.js'; export default class Node { bind ( scope ) { - this.eachChild( child => child.bind( scope ) ); + this.eachChild( child => child.bind( this.scope || scope ) ); } eachChild ( callback ) { @@ -43,6 +43,8 @@ export default class Node { } hasEffects ( scope ) { + if ( this.scope ) scope = this.scope; + for ( const key of this.keys ) { const value = this[ key ]; @@ -61,7 +63,7 @@ export default class Node { } initialise ( scope ) { - this.eachChild( child => child.initialise( scope ) ); + this.eachChild( child => child.initialise( this.scope || scope ) ); } locate () { @@ -82,7 +84,7 @@ export default class Node { this.ran = true; this.eachChild( child => { - child.run( scope ); + child.run( this.scope || scope ); }); } diff --git a/src/ast/nodes/ArrowFunctionExpression.js b/src/ast/nodes/ArrowFunctionExpression.js index 257c7a3..8d9a3d2 100644 --- a/src/ast/nodes/ArrowFunctionExpression.js +++ b/src/ast/nodes/ArrowFunctionExpression.js @@ -18,6 +18,7 @@ export default class ArrowFunctionExpression extends Node { initialise ( scope ) { if ( this.body.type === 'BlockStatement' ) { this.body.createScope( scope ); + this.scope = this.body.scope; } else { this.scope = new Scope({ parent: scope, @@ -32,7 +33,6 @@ export default class ArrowFunctionExpression extends Node { } } - scope = this.scope || this.body.scope; - super.initialise( scope ); + super.initialise( this.scope ); } } diff --git a/src/ast/nodes/ForInStatement.js b/src/ast/nodes/ForInStatement.js index 0838aa1..1872b96 100644 --- a/src/ast/nodes/ForInStatement.js +++ b/src/ast/nodes/ForInStatement.js @@ -1,11 +1,22 @@ import Statement from './shared/Statement.js'; import assignTo from './shared/assignTo.js'; +import Scope from '../scopes/Scope.js'; import { STRING } from '../values.js'; export default class ForInStatement extends Statement { initialise ( scope ) { - this.body.createScope( scope ); - super.initialise( this.body.scope ); - assignTo( this.left, this.body.scope, STRING ); + if ( this.body.type === 'BlockStatement' ) { + this.body.createScope( scope ); + this.scope = this.body.scope; + } else { + this.scope = new Scope({ + parent: scope, + isBlockScope: true, + isLexicalBoundary: false + }); + } + + super.initialise( this.scope ); + assignTo( this.left, this.scope, STRING ); } } diff --git a/src/ast/nodes/ForOfStatement.js b/src/ast/nodes/ForOfStatement.js index e761433..a5225f8 100644 --- a/src/ast/nodes/ForOfStatement.js +++ b/src/ast/nodes/ForOfStatement.js @@ -1,11 +1,22 @@ import Statement from './shared/Statement.js'; import assignTo from './shared/assignTo.js'; +import Scope from '../scopes/Scope.js'; import { UNKNOWN } from '../values.js'; export default class ForOfStatement extends Statement { initialise ( scope ) { - this.body.createScope( scope ); - super.initialise( this.body.scope ); - assignTo( this.left, this.body.scope, UNKNOWN ); + if ( this.body.type === 'BlockStatement' ) { + this.body.createScope( scope ); + this.scope = this.body.scope; + } else { + this.scope = new Scope({ + parent: scope, + isBlockScope: true, + isLexicalBoundary: false + }); + } + + super.initialise( this.scope ); + assignTo( this.left, this.scope, UNKNOWN ); } } diff --git a/src/ast/nodes/ForStatement.js b/src/ast/nodes/ForStatement.js index 1d61b02..11e98e5 100644 --- a/src/ast/nodes/ForStatement.js +++ b/src/ast/nodes/ForStatement.js @@ -1,22 +1,23 @@ import Statement from './shared/Statement.js'; +import Scope from '../scopes/Scope.js'; export default class ForStatement extends Statement { - hasEffects () { - return super.hasEffects( this.body.scope ); - } - initialise ( scope ) { - this.body.createScope( scope ); - scope = this.body.scope; + if ( this.body.type === 'BlockStatement' ) { + this.body.createScope( scope ); + this.scope = this.body.scope; + } else { + this.scope = new Scope({ + parent: scope, + isBlockScope: true, + isLexicalBoundary: false + }); + } // can't use super, because we need to control the order - if ( this.init ) this.init.initialise( scope ); - if ( this.test ) this.test.initialise( scope ); - if ( this.update ) this.update.initialise( scope ); - this.body.initialise( scope ); - } - - run ( scope ) { - super.run( scope ); + if ( this.init ) this.init.initialise( this.scope ); + if ( this.test ) this.test.initialise( this.scope ); + if ( this.update ) this.update.initialise( this.scope ); + this.body.initialise( this.scope ); } } diff --git a/test/form/body-less-for-loops/_config.js b/test/form/body-less-for-loops/_config.js new file mode 100644 index 0000000..a748fa0 --- /dev/null +++ b/test/form/body-less-for-loops/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'supports body-less for loops' +}; diff --git a/test/form/body-less-for-loops/_expected/amd.js b/test/form/body-less-for-loops/_expected/amd.js new file mode 100644 index 0000000..464893d --- /dev/null +++ b/test/form/body-less-for-loops/_expected/amd.js @@ -0,0 +1,16 @@ +define(function () { 'use strict'; + + for ( let i = 0; i < 10; i += 1 ) console.log( i ); + for ( const letter of array ) console.log( letter ); + for ( const index in array ) console.log( index ); + + let i; + for ( i = 0; i < 10; i += 1 ) console.log( i ); + + let letter; + for ( letter of array ) console.log( letter ); + + let index; + for ( index in array ) console.log( index ); + +}); \ No newline at end of file diff --git a/test/form/body-less-for-loops/_expected/cjs.js b/test/form/body-less-for-loops/_expected/cjs.js new file mode 100644 index 0000000..f2e4fde --- /dev/null +++ b/test/form/body-less-for-loops/_expected/cjs.js @@ -0,0 +1,14 @@ +'use strict'; + +for ( let i = 0; i < 10; i += 1 ) console.log( i ); +for ( const letter of array ) console.log( letter ); +for ( const index in array ) console.log( index ); + +let i; +for ( i = 0; i < 10; i += 1 ) console.log( i ); + +let letter; +for ( letter of array ) console.log( letter ); + +let index; +for ( index in array ) console.log( index ); \ No newline at end of file diff --git a/test/form/body-less-for-loops/_expected/es.js b/test/form/body-less-for-loops/_expected/es.js new file mode 100644 index 0000000..63ee4e5 --- /dev/null +++ b/test/form/body-less-for-loops/_expected/es.js @@ -0,0 +1,12 @@ +for ( let i = 0; i < 10; i += 1 ) console.log( i ); +for ( const letter of array ) console.log( letter ); +for ( const index in array ) console.log( index ); + +let i; +for ( i = 0; i < 10; i += 1 ) console.log( i ); + +let letter; +for ( letter of array ) console.log( letter ); + +let index; +for ( index in array ) console.log( index ); \ No newline at end of file diff --git a/test/form/body-less-for-loops/_expected/iife.js b/test/form/body-less-for-loops/_expected/iife.js new file mode 100644 index 0000000..2214bd4 --- /dev/null +++ b/test/form/body-less-for-loops/_expected/iife.js @@ -0,0 +1,17 @@ +(function () { + 'use strict'; + + for ( let i = 0; i < 10; i += 1 ) console.log( i ); + for ( const letter of array ) console.log( letter ); + for ( const index in array ) console.log( index ); + + let i; + for ( i = 0; i < 10; i += 1 ) console.log( i ); + + let letter; + for ( letter of array ) console.log( letter ); + + let index; + for ( index in array ) console.log( index ); + +}()); \ No newline at end of file diff --git a/test/form/body-less-for-loops/_expected/umd.js b/test/form/body-less-for-loops/_expected/umd.js new file mode 100644 index 0000000..9840021 --- /dev/null +++ b/test/form/body-less-for-loops/_expected/umd.js @@ -0,0 +1,20 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + for ( let i = 0; i < 10; i += 1 ) console.log( i ); + for ( const letter of array ) console.log( letter ); + for ( const index in array ) console.log( index ); + + let i; + for ( i = 0; i < 10; i += 1 ) console.log( i ); + + let letter; + for ( letter of array ) console.log( letter ); + + let index; + for ( index in array ) console.log( index ); + +}))); \ No newline at end of file diff --git a/test/form/body-less-for-loops/main.js b/test/form/body-less-for-loops/main.js new file mode 100644 index 0000000..3aa7b7e --- /dev/null +++ b/test/form/body-less-for-loops/main.js @@ -0,0 +1,12 @@ +for ( let i = 0; i < 10; i += 1 ) console.log( i ); +for ( const letter of array ) console.log( letter ); +for ( const index in array ) console.log( index ); + +let i; +for ( i = 0; i < 10; i += 1 ) console.log( i ); + +let letter; +for ( letter of array ) console.log( letter ); + +let index; +for ( index in array ) console.log( index ); From e297468b6a68a17e7c60d25450a67fec9c5ef6ac Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 19:46:29 -0400 Subject: [PATCH 65/99] temporarily revert to 0.34 for build --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 097202f..928a68c 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "mocha": "^3.0.0", "remap-istanbul": "^0.6.4", "require-relative": "^0.8.7", - "rollup": "^0.35.3", + "rollup": "^0.34.0", "rollup-plugin-buble": "^0.12.1", "rollup-plugin-commonjs": "^3.0.0", "rollup-plugin-json": "^2.0.0", From 3ecde8cd093ace63cbecc477827de566d728bd19 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 19:51:27 -0400 Subject: [PATCH 66/99] -> v0.35.5 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2fcbc6..e46c0a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.35.5 + +* Allow empty for loop heads ([#919](https://github.com/rollup/rollup/issues/919)) + ## 0.35.4 * Preserve effects in for-of and for-in loops ([#870](https://github.com/rollup/rollup/issues/870)) diff --git a/package.json b/package.json index 928a68c..5a2c977 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.4", + "version": "0.35.5", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From 55c23a3f23a8047b1dfee8186373949fbcb9bde8 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 20:05:31 -0400 Subject: [PATCH 67/99] browser friendly flushTime --- src/utils/flushTime.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/utils/flushTime.js b/src/utils/flushTime.js index fb4d331..000fbb9 100644 --- a/src/utils/flushTime.js +++ b/src/utils/flushTime.js @@ -1,19 +1,35 @@ const DEBUG = false; const map = new Map; +let time; + +if ( typeof process === 'undefined' ) { + time = function time ( previous ) { + const now = window.performance.now(); + return previous ? previous - now : now; + }; +} else { + time = function time ( previous ) { + const hrtime = process.hrtime( previous ); + if ( previous ) { + return hrtime[0] * 1e3 + hrtime[1] / 1e6; + } + }; +} + export function timeStart ( label ) { if ( !map.has( label ) ) { map.set( label, { time: 0 }); } - map.get( label ).start = process.hrtime(); + map.get( label ).start = time(); } export function timeEnd ( label ) { if ( map.has( label ) ) { const item = map.get( label ); - item.time += toMilliseconds( process.hrtime( item.start ) ); + item.time += time( item.start ); } } @@ -24,10 +40,6 @@ export function flushTime ( log = defaultLog ) { map.clear(); } -function toMilliseconds ( time ) { - return time[0] * 1e+3 + Math.floor( time[1] * 1e-6 ); -} - function defaultLog ( label, time ) { if ( DEBUG ) { /* eslint-disable no-console */ From 17f52787cb03072b1eb926d57d7f4f8df5a469f5 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 20:07:00 -0400 Subject: [PATCH 68/99] -> v0.35.6 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e46c0a5..b82ff51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.35.6 + +* Fix browser build + ## 0.35.5 * Allow empty for loop heads ([#919](https://github.com/rollup/rollup/issues/919)) diff --git a/package.json b/package.json index 5a2c977..b852ddf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.5", + "version": "0.35.6", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From ae82c520e6e9a3eddb130ab876b400b6ecb697f6 Mon Sep 17 00:00:00 2001 From: Permutator Date: Sat, 10 Sep 2016 17:38:39 -0700 Subject: [PATCH 69/99] Fixed "process.hrtime() only accepts an Array tuple." error --- src/utils/flushTime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/flushTime.js b/src/utils/flushTime.js index 000fbb9..8261189 100644 --- a/src/utils/flushTime.js +++ b/src/utils/flushTime.js @@ -10,8 +10,8 @@ if ( typeof process === 'undefined' ) { }; } else { time = function time ( previous ) { - const hrtime = process.hrtime( previous ); if ( previous ) { + const hrtime = process.hrtime( previous ); return hrtime[0] * 1e3 + hrtime[1] / 1e6; } }; From 10808f733b5389ddeaa97e3b2ea3215cb7b8037e Mon Sep 17 00:00:00 2001 From: Permutator Date: Sat, 10 Sep 2016 17:59:17 -0700 Subject: [PATCH 70/99] Made flushTime actually work again --- src/utils/flushTime.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/utils/flushTime.js b/src/utils/flushTime.js index 8261189..c6c95e3 100644 --- a/src/utils/flushTime.js +++ b/src/utils/flushTime.js @@ -1,20 +1,25 @@ const DEBUG = false; const map = new Map; -let time; +let time, toMilliseconds; if ( typeof process === 'undefined' ) { time = function time ( previous ) { const now = window.performance.now(); return previous ? previous - now : now; }; + + toMilliseconds = function toMilliseconds ( time ) { + return time; + } } else { time = function time ( previous ) { - if ( previous ) { - const hrtime = process.hrtime( previous ); - return hrtime[0] * 1e3 + hrtime[1] / 1e6; - } + return previous === undefined ? process.hrtime() : process.hrtime( previous ); }; + + toMilliseconds = function toMilliseconds ( time ) { + return time[0] * 1e3 + time[1] / 1e6; + } } export function timeStart ( label ) { @@ -29,7 +34,7 @@ export function timeStart ( label ) { export function timeEnd ( label ) { if ( map.has( label ) ) { const item = map.get( label ); - item.time += time( item.start ); + item.time += toMilliseconds( time( item.start ) ); } } From d31c75bc70ddf134331411e7278aef55310464b0 Mon Sep 17 00:00:00 2001 From: Permutator Date: Sat, 10 Sep 2016 18:02:32 -0700 Subject: [PATCH 71/99] Pass linting --- src/utils/flushTime.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/utils/flushTime.js b/src/utils/flushTime.js index c6c95e3..c94ac73 100644 --- a/src/utils/flushTime.js +++ b/src/utils/flushTime.js @@ -1,7 +1,8 @@ const DEBUG = false; const map = new Map; -let time, toMilliseconds; +let time; +let toMilliseconds; if ( typeof process === 'undefined' ) { time = function time ( previous ) { @@ -11,7 +12,7 @@ if ( typeof process === 'undefined' ) { toMilliseconds = function toMilliseconds ( time ) { return time; - } + }; } else { time = function time ( previous ) { return previous === undefined ? process.hrtime() : process.hrtime( previous ); @@ -19,7 +20,7 @@ if ( typeof process === 'undefined' ) { toMilliseconds = function toMilliseconds ( time ) { return time[0] * 1e3 + time[1] / 1e6; - } + }; } export function timeStart ( label ) { From b09f0831675f6d4237e83ec6fc16e07afa517cba Mon Sep 17 00:00:00 2001 From: Permutator Date: Sat, 10 Sep 2016 18:17:30 -0700 Subject: [PATCH 72/99] Use timeStartHelper and timeEndHelper instead of time and toMilliseconds --- src/utils/flushTime.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/utils/flushTime.js b/src/utils/flushTime.js index c94ac73..683e28c 100644 --- a/src/utils/flushTime.js +++ b/src/utils/flushTime.js @@ -1,25 +1,25 @@ const DEBUG = false; const map = new Map; -let time; -let toMilliseconds; +let timeStartHelper; +let timeEndHelper; if ( typeof process === 'undefined' ) { - time = function time ( previous ) { - const now = window.performance.now(); - return previous ? previous - now : now; + timeStartHelper = function timeStartHelper () { + return window.performance.now(); }; - toMilliseconds = function toMilliseconds ( time ) { - return time; + timeEndHelper = function timeEndHelper ( previous ) { + return window.performance.now() - previous; }; } else { - time = function time ( previous ) { - return previous === undefined ? process.hrtime() : process.hrtime( previous ); + timeStartHelper = function timeStartHelper () { + return process.hrtime(); }; - toMilliseconds = function toMilliseconds ( time ) { - return time[0] * 1e3 + time[1] / 1e6; + timeEndHelper = function timeEndHelper ( previous ) { + const hrtime = process.hrtime( previous ); + return hrtime[0] * 1e3 + hrtime[1] / 1e6; }; } @@ -29,13 +29,13 @@ export function timeStart ( label ) { time: 0 }); } - map.get( label ).start = time(); + map.get( label ).start = timeStartHelper(); } export function timeEnd ( label ) { if ( map.has( label ) ) { const item = map.get( label ); - item.time += toMilliseconds( time( item.start ) ); + item.time += timeEndHelper( item.start ); } } From 1f63f405516a99082d428c75aaad94ee8960f33e Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Sep 2016 21:22:38 -0400 Subject: [PATCH 73/99] -> v0.35.7 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b82ff51..2680013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.35.7 + +* Refactor `flushTime.js` ([#922](https://github.com/rollup/rollup/pull/922)) + ## 0.35.6 * Fix browser build diff --git a/package.json b/package.json index b852ddf..c688e6b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.6", + "version": "0.35.7", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From 610ff331e4dc4ee569df164405d615531124d018 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Sun, 11 Sep 2016 11:18:57 +0300 Subject: [PATCH 74/99] Trim debug numbers --- src/utils/flushTime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/flushTime.js b/src/utils/flushTime.js index 683e28c..9f32e45 100644 --- a/src/utils/flushTime.js +++ b/src/utils/flushTime.js @@ -19,7 +19,7 @@ if ( typeof process === 'undefined' ) { timeEndHelper = function timeEndHelper ( previous ) { const hrtime = process.hrtime( previous ); - return hrtime[0] * 1e3 + hrtime[1] / 1e6; + return hrtime[0] * 1e3 + Math.floor( hrtime[1] / 1e6 ); }; } From a7161689e467452621b9b2fa453d87646a94da06 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Sun, 11 Sep 2016 11:05:27 -0700 Subject: [PATCH 75/99] Correctly deshadow re-assigned module functions Fixes #910. --- src/ast/scopes/ModuleScope.js | 13 +++++++------ .../namespacing-in-sub-functions/_config.js | 8 ++++++++ test/function/namespacing-in-sub-functions/main.js | 11 +++++++++++ .../namespacing-in-sub-functions/problematicFunc.js | 5 +++++ 4 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 test/function/namespacing-in-sub-functions/_config.js create mode 100644 test/function/namespacing-in-sub-functions/main.js create mode 100644 test/function/namespacing-in-sub-functions/problematicFunc.js diff --git a/src/ast/scopes/ModuleScope.js b/src/ast/scopes/ModuleScope.js index f451d20..cdc2c93 100644 --- a/src/ast/scopes/ModuleScope.js +++ b/src/ast/scopes/ModuleScope.js @@ -19,14 +19,15 @@ export default class ModuleScope extends Scope { forOwn( this.module.imports, specifier => { if ( specifier.module.isExternal ) return; - if ( specifier.name === '*' ) { - specifier.module.getExports().forEach( name => { - names.set( name, true ); - }); - } else { + specifier.module.getExports().forEach( name => { + names.set(name); + }); + if ( specifier.name !== '*' ) { const declaration = specifier.module.traceExport( specifier.name ); const name = declaration.getName( true ); - if ( name !== specifier.name ) names.set( declaration.getName( true ) ); + if ( name !== specifier.name ) { + names.set(declaration.getName( true )); + } } }); diff --git a/test/function/namespacing-in-sub-functions/_config.js b/test/function/namespacing-in-sub-functions/_config.js new file mode 100644 index 0000000..025b117 --- /dev/null +++ b/test/function/namespacing-in-sub-functions/_config.js @@ -0,0 +1,8 @@ +var assert = require( 'assert' ); + +module.exports = { + description: 'correctly namespaces sub-functions (#910)', + exports: function ( exports ) { + assert.equal( exports, 'foobar' ); + } +}; diff --git a/test/function/namespacing-in-sub-functions/main.js b/test/function/namespacing-in-sub-functions/main.js new file mode 100644 index 0000000..ac9d9da --- /dev/null +++ b/test/function/namespacing-in-sub-functions/main.js @@ -0,0 +1,11 @@ +import { problematicFunc as otherFunc } from './problematicFunc'; +function innerFunc() { + function problematicFunc () { + return otherFunc(); + } + return problematicFunc(); +} + +var res = innerFunc(); + +export default res; \ No newline at end of file diff --git a/test/function/namespacing-in-sub-functions/problematicFunc.js b/test/function/namespacing-in-sub-functions/problematicFunc.js new file mode 100644 index 0000000..1fbecc0 --- /dev/null +++ b/test/function/namespacing-in-sub-functions/problematicFunc.js @@ -0,0 +1,5 @@ +function problematicFunc() { + return 'foobar'; +} + +export { problematicFunc }; \ No newline at end of file From e7925e29d1f55f9bf09fe9c548f1bebc7ccfd52a Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 11 Sep 2016 14:35:04 -0400 Subject: [PATCH 76/99] -> v0.35.8 --- CHANGELOG.md | 4 ++++ package.json | 2 +- src/ast/scopes/ModuleScope.js | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2680013..a6f3f90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.35.8 + +* Correctly deshadow re-assigned module functions ([#910](https://github.com/rollup/rollup/issues/910)) + ## 0.35.7 * Refactor `flushTime.js` ([#922](https://github.com/rollup/rollup/pull/922)) diff --git a/package.json b/package.json index c688e6b..d49fd0a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.7", + "version": "0.35.8", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", diff --git a/src/ast/scopes/ModuleScope.js b/src/ast/scopes/ModuleScope.js index cdc2c93..051fcb4 100644 --- a/src/ast/scopes/ModuleScope.js +++ b/src/ast/scopes/ModuleScope.js @@ -26,7 +26,7 @@ export default class ModuleScope extends Scope { const declaration = specifier.module.traceExport( specifier.name ); const name = declaration.getName( true ); if ( name !== specifier.name ) { - names.set(declaration.getName( true )); + names.set( declaration.getName( true ) ); } } }); From 0727c3284f1419b3d65786f03b326bd1dffbfa7e Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 11 Sep 2016 14:43:30 -0400 Subject: [PATCH 77/99] support 0.12 --- rollup.config.js | 2 +- test/function/deconflicts-classes/_config.js | 3 ++- test/function/identifiers-in-template-literals/_config.js | 3 ++- .../function/rename-conditional-expression-children/_config.js | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index a8ac2c8..cc0b9ba 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -24,7 +24,7 @@ export default { buble({ include: [ 'src/**', 'node_modules/acorn/**' ], target: { - node: 4 + node: '0.12' } }), diff --git a/test/function/deconflicts-classes/_config.js b/test/function/deconflicts-classes/_config.js index ee66bd1..633aacc 100644 --- a/test/function/deconflicts-classes/_config.js +++ b/test/function/deconflicts-classes/_config.js @@ -1,3 +1,4 @@ module.exports = { - description: 'deconflicts top-level classes' + description: 'deconflicts top-level classes', + buble: true }; diff --git a/test/function/identifiers-in-template-literals/_config.js b/test/function/identifiers-in-template-literals/_config.js index 8b031cd..51b503d 100644 --- a/test/function/identifiers-in-template-literals/_config.js +++ b/test/function/identifiers-in-template-literals/_config.js @@ -1,3 +1,4 @@ module.exports = { - description: 'identifiers in template literals are rendered correctly' + description: 'identifiers in template literals are rendered correctly', + buble: true }; diff --git a/test/function/rename-conditional-expression-children/_config.js b/test/function/rename-conditional-expression-children/_config.js index 7c052c5..d159c95 100644 --- a/test/function/rename-conditional-expression-children/_config.js +++ b/test/function/rename-conditional-expression-children/_config.js @@ -1,3 +1,4 @@ module.exports = { - description: 'correctly renders children of ConditionalExpressions' + description: 'correctly renders children of ConditionalExpressions', + buble: true }; From b76f7b03ce98d0f9349d215b0adf27957f60185e Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 11 Sep 2016 14:44:00 -0400 Subject: [PATCH 78/99] put 0.12 back in the build matrix --- .travis.yml | 1 + appveyor.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 956a81b..83d1bca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ sudo: false language: node_js node_js: + - "0.12" - "4" - "6" env: diff --git a/appveyor.yml b/appveyor.yml index 8ab6c7d..a4389cb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,6 +10,7 @@ init: environment: matrix: # node.js + - nodejs_version: 0.12 - nodejs_version: 4 - nodejs_version: 6 From a1478d744577c4cf1d94ab7da4e120f147f3141c Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 11 Sep 2016 18:57:27 -0400 Subject: [PATCH 79/99] -> v0.35.9 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6f3f90..f097310 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.35.9 + +* Support Node 0.12 ([#909](https://github.com/rollup/rollup/issues/909)) + ## 0.35.8 * Correctly deshadow re-assigned module functions ([#910](https://github.com/rollup/rollup/issues/910)) diff --git a/package.json b/package.json index d49fd0a..6060ec6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.8", + "version": "0.35.9", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From d980cf37de5f58db13e50294b1e83b15dbf7ba56 Mon Sep 17 00:00:00 2001 From: Brian Donovan Date: Mon, 12 Sep 2016 21:46:54 -0700 Subject: [PATCH 80/99] Only remove EmptyStatement nodes directly inside blocks. When used as part of another statement, such as an `IfStatement`, they can be semantically meaningful. Fixes #931 --- src/ast/nodes/EmptyStatement.js | 4 +++- test/form/empty-statement-consequent/_config.js | 3 +++ test/form/empty-statement-consequent/_expected/amd.js | 6 ++++++ test/form/empty-statement-consequent/_expected/cjs.js | 4 ++++ test/form/empty-statement-consequent/_expected/es.js | 2 ++ test/form/empty-statement-consequent/_expected/iife.js | 7 +++++++ test/form/empty-statement-consequent/_expected/umd.js | 10 ++++++++++ test/form/empty-statement-consequent/main.js | 2 ++ 8 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 test/form/empty-statement-consequent/_config.js create mode 100644 test/form/empty-statement-consequent/_expected/amd.js create mode 100644 test/form/empty-statement-consequent/_expected/cjs.js create mode 100644 test/form/empty-statement-consequent/_expected/es.js create mode 100644 test/form/empty-statement-consequent/_expected/iife.js create mode 100644 test/form/empty-statement-consequent/_expected/umd.js create mode 100644 test/form/empty-statement-consequent/main.js diff --git a/src/ast/nodes/EmptyStatement.js b/src/ast/nodes/EmptyStatement.js index 27830c4..5f7309c 100644 --- a/src/ast/nodes/EmptyStatement.js +++ b/src/ast/nodes/EmptyStatement.js @@ -2,6 +2,8 @@ import Statement from './shared/Statement.js'; export default class EmptyStatement extends Statement { render ( code ) { - code.remove( this.start, this.end ); + if ( this.parent.type === 'BlockStatement' || this.parent.type === 'Program' ) { + code.remove( this.start, this.end ); + } } } diff --git a/test/form/empty-statement-consequent/_config.js b/test/form/empty-statement-consequent/_config.js new file mode 100644 index 0000000..dc10538 --- /dev/null +++ b/test/form/empty-statement-consequent/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'preserves empty statements used as the consequent of conditionals' +}; diff --git a/test/form/empty-statement-consequent/_expected/amd.js b/test/form/empty-statement-consequent/_expected/amd.js new file mode 100644 index 0000000..9114028 --- /dev/null +++ b/test/form/empty-statement-consequent/_expected/amd.js @@ -0,0 +1,6 @@ +define(function () { 'use strict'; + + if ( a === 0 ); + else a++; + +}); \ No newline at end of file diff --git a/test/form/empty-statement-consequent/_expected/cjs.js b/test/form/empty-statement-consequent/_expected/cjs.js new file mode 100644 index 0000000..9b5e04a --- /dev/null +++ b/test/form/empty-statement-consequent/_expected/cjs.js @@ -0,0 +1,4 @@ +'use strict'; + +if ( a === 0 ); +else a++; \ No newline at end of file diff --git a/test/form/empty-statement-consequent/_expected/es.js b/test/form/empty-statement-consequent/_expected/es.js new file mode 100644 index 0000000..04006b3 --- /dev/null +++ b/test/form/empty-statement-consequent/_expected/es.js @@ -0,0 +1,2 @@ +if ( a === 0 ); +else a++; \ No newline at end of file diff --git a/test/form/empty-statement-consequent/_expected/iife.js b/test/form/empty-statement-consequent/_expected/iife.js new file mode 100644 index 0000000..45b92d8 --- /dev/null +++ b/test/form/empty-statement-consequent/_expected/iife.js @@ -0,0 +1,7 @@ +(function () { + 'use strict'; + + if ( a === 0 ); + else a++; + +}()); \ No newline at end of file diff --git a/test/form/empty-statement-consequent/_expected/umd.js b/test/form/empty-statement-consequent/_expected/umd.js new file mode 100644 index 0000000..08026c7 --- /dev/null +++ b/test/form/empty-statement-consequent/_expected/umd.js @@ -0,0 +1,10 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + if ( a === 0 ); + else a++; + +}))); \ No newline at end of file diff --git a/test/form/empty-statement-consequent/main.js b/test/form/empty-statement-consequent/main.js new file mode 100644 index 0000000..b6b36cf --- /dev/null +++ b/test/form/empty-statement-consequent/main.js @@ -0,0 +1,2 @@ +if ( a === 0 ); +else a++; From 8545d85a9ee99b2a0651ce5997e647f998ec90cf Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Mon, 12 Sep 2016 22:50:31 -0700 Subject: [PATCH 81/99] Correctly deshadow star imports when conflicting --- src/Bundle.js | 9 +++++++++ test/form/namespace-optimization-b/_expected/amd.js | 6 +++--- test/form/namespace-optimization-b/_expected/cjs.js | 6 +++--- test/form/namespace-optimization-b/_expected/es.js | 6 +++--- test/form/namespace-optimization-b/_expected/iife.js | 6 +++--- test/form/namespace-optimization-b/_expected/umd.js | 6 +++--- test/function/namespacing-collisions-2/Material.js | 3 +++ test/function/namespacing-collisions-2/MaterialAgain.js | 3 +++ test/function/namespacing-collisions-2/Something.js | 6 ++++++ test/function/namespacing-collisions-2/SomethingAgain.js | 6 ++++++ test/function/namespacing-collisions-2/_config.js | 8 ++++++++ test/function/namespacing-collisions-2/main.js | 7 +++++++ test/function/namespacing-collisions/Material.js | 3 +++ test/function/namespacing-collisions/Something.js | 6 ++++++ test/function/namespacing-collisions/_config.js | 8 ++++++++ test/function/namespacing-collisions/main.js | 5 +++++ 16 files changed, 79 insertions(+), 15 deletions(-) create mode 100644 test/function/namespacing-collisions-2/Material.js create mode 100644 test/function/namespacing-collisions-2/MaterialAgain.js create mode 100644 test/function/namespacing-collisions-2/Something.js create mode 100644 test/function/namespacing-collisions-2/SomethingAgain.js create mode 100644 test/function/namespacing-collisions-2/_config.js create mode 100644 test/function/namespacing-collisions-2/main.js create mode 100644 test/function/namespacing-collisions/Material.js create mode 100644 test/function/namespacing-collisions/Something.js create mode 100644 test/function/namespacing-collisions/_config.js create mode 100644 test/function/namespacing-collisions/main.js diff --git a/src/Bundle.js b/src/Bundle.js index 40a9211..d3ec8b5 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -208,6 +208,15 @@ export default class Bundle { declaration.name = getSafeName( declaration.name ); }); + // special case - for `import * as Foo`, we need to make sure that Foo + // gets its own variable because it will eventually be rendered as + // `var Foo = Object.freeze(...)` + forOwn( module.imports, ( importee, importeeName ) => { + if ( importee.name === '*' ) { + delete module.imports[ importeeName ]; + module.imports[ getSafeName(importeeName) ] = importee; + } + }); }); this.scope.deshadow( toDeshadow ); diff --git a/test/form/namespace-optimization-b/_expected/amd.js b/test/form/namespace-optimization-b/_expected/amd.js index 30c5406..45db05f 100644 --- a/test/form/namespace-optimization-b/_expected/amd.js +++ b/test/form/namespace-optimization-b/_expected/amd.js @@ -1,12 +1,12 @@ define(function () { 'use strict'; - function foo () { + function foo$1 () { console.log( 'foo' ); } function a () { - foo(); - foo(); + foo$1(); + foo$1(); var a; if ( a.b ) { diff --git a/test/form/namespace-optimization-b/_expected/cjs.js b/test/form/namespace-optimization-b/_expected/cjs.js index 3ee6869..5891df9 100644 --- a/test/form/namespace-optimization-b/_expected/cjs.js +++ b/test/form/namespace-optimization-b/_expected/cjs.js @@ -1,12 +1,12 @@ 'use strict'; -function foo () { +function foo$1 () { console.log( 'foo' ); } function a () { - foo(); - foo(); + foo$1(); + foo$1(); var a; if ( a.b ) { diff --git a/test/form/namespace-optimization-b/_expected/es.js b/test/form/namespace-optimization-b/_expected/es.js index 8346d0d..c63d076 100644 --- a/test/form/namespace-optimization-b/_expected/es.js +++ b/test/form/namespace-optimization-b/_expected/es.js @@ -1,10 +1,10 @@ -function foo () { +function foo$1 () { console.log( 'foo' ); } function a () { - foo(); - foo(); + foo$1(); + foo$1(); var a; if ( a.b ) { diff --git a/test/form/namespace-optimization-b/_expected/iife.js b/test/form/namespace-optimization-b/_expected/iife.js index 6aeb1cd..bfc4c08 100644 --- a/test/form/namespace-optimization-b/_expected/iife.js +++ b/test/form/namespace-optimization-b/_expected/iife.js @@ -1,13 +1,13 @@ (function () { 'use strict'; - function foo () { + function foo$1 () { console.log( 'foo' ); } function a () { - foo(); - foo(); + foo$1(); + foo$1(); var a; if ( a.b ) { diff --git a/test/form/namespace-optimization-b/_expected/umd.js b/test/form/namespace-optimization-b/_expected/umd.js index f7fbbdf..a95eafd 100644 --- a/test/form/namespace-optimization-b/_expected/umd.js +++ b/test/form/namespace-optimization-b/_expected/umd.js @@ -4,13 +4,13 @@ (factory()); }(this, (function () { 'use strict'; - function foo () { + function foo$1 () { console.log( 'foo' ); } function a () { - foo(); - foo(); + foo$1(); + foo$1(); var a; if ( a.b ) { diff --git a/test/function/namespacing-collisions-2/Material.js b/test/function/namespacing-collisions-2/Material.js new file mode 100644 index 0000000..b6d1881 --- /dev/null +++ b/test/function/namespacing-collisions-2/Material.js @@ -0,0 +1,3 @@ +export function Material() { + return 'Material'; +} diff --git a/test/function/namespacing-collisions-2/MaterialAgain.js b/test/function/namespacing-collisions-2/MaterialAgain.js new file mode 100644 index 0000000..170cfff --- /dev/null +++ b/test/function/namespacing-collisions-2/MaterialAgain.js @@ -0,0 +1,3 @@ +export function MaterialAgain() { + return 'MaterialAgain'; +} diff --git a/test/function/namespacing-collisions-2/Something.js b/test/function/namespacing-collisions-2/Something.js new file mode 100644 index 0000000..64cbdca --- /dev/null +++ b/test/function/namespacing-collisions-2/Something.js @@ -0,0 +1,6 @@ +import * as Material from './Material'; + +export function Something() { + console.log(Material); + return 'Something'; +} \ No newline at end of file diff --git a/test/function/namespacing-collisions-2/SomethingAgain.js b/test/function/namespacing-collisions-2/SomethingAgain.js new file mode 100644 index 0000000..1efaca7 --- /dev/null +++ b/test/function/namespacing-collisions-2/SomethingAgain.js @@ -0,0 +1,6 @@ +import * as Material from './MaterialAgain'; + +export function SomethingAgain() { + console.log(Material); + return 'SomethingAgain'; +} \ No newline at end of file diff --git a/test/function/namespacing-collisions-2/_config.js b/test/function/namespacing-collisions-2/_config.js new file mode 100644 index 0000000..4684296 --- /dev/null +++ b/test/function/namespacing-collisions-2/_config.js @@ -0,0 +1,8 @@ +var assert = require( 'assert' ); + +module.exports = { + description: 'correctly namespaces when using * exports, take two (#910)', + exports: function ( exports ) { + assert.deepEqual( exports, ['Material', 'MaterialAgain', 'Something', 'SomethingAgain'] ); + } +}; diff --git a/test/function/namespacing-collisions-2/main.js b/test/function/namespacing-collisions-2/main.js new file mode 100644 index 0000000..c69a6e4 --- /dev/null +++ b/test/function/namespacing-collisions-2/main.js @@ -0,0 +1,7 @@ +import { Something } from './Something'; +import { SomethingAgain } from './SomethingAgain'; +import { Material } from './Material'; +import { MaterialAgain } from './MaterialAgain'; + +var result = [Material(), MaterialAgain(), Something(), SomethingAgain()] +export default result; diff --git a/test/function/namespacing-collisions/Material.js b/test/function/namespacing-collisions/Material.js new file mode 100644 index 0000000..b6d1881 --- /dev/null +++ b/test/function/namespacing-collisions/Material.js @@ -0,0 +1,3 @@ +export function Material() { + return 'Material'; +} diff --git a/test/function/namespacing-collisions/Something.js b/test/function/namespacing-collisions/Something.js new file mode 100644 index 0000000..64cbdca --- /dev/null +++ b/test/function/namespacing-collisions/Something.js @@ -0,0 +1,6 @@ +import * as Material from './Material'; + +export function Something() { + console.log(Material); + return 'Something'; +} \ No newline at end of file diff --git a/test/function/namespacing-collisions/_config.js b/test/function/namespacing-collisions/_config.js new file mode 100644 index 0000000..02c772f --- /dev/null +++ b/test/function/namespacing-collisions/_config.js @@ -0,0 +1,8 @@ +var assert = require( 'assert' ); + +module.exports = { + description: 'correctly namespaces when using * exports (#910)', + exports: function ( exports ) { + assert.deepEqual( exports, [ 'Material', 'Something' ] ); + } +}; diff --git a/test/function/namespacing-collisions/main.js b/test/function/namespacing-collisions/main.js new file mode 100644 index 0000000..8746123 --- /dev/null +++ b/test/function/namespacing-collisions/main.js @@ -0,0 +1,5 @@ +import { Something } from './Something'; +import { Material } from './Material'; + +var result = [Material(), Something()] +export default result; \ No newline at end of file From 5b68b14052806364878975dc976eda1b7ea35cab Mon Sep 17 00:00:00 2001 From: Brian Donovan Date: Tue, 13 Sep 2016 09:57:34 -0700 Subject: [PATCH 82/99] -> v0.35.10 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f097310..32f446e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.35.10 + +* Only remove EmptyStatement nodes directly inside blocks ([#913](https://github.com/rollup/rollup/issues/931)) + ## 0.35.9 * Support Node 0.12 ([#909](https://github.com/rollup/rollup/issues/909)) diff --git a/package.json b/package.json index 6060ec6..17351f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.9", + "version": "0.35.10", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From bed6f0650da4f54ab8b8f7b4e3d64d99a12c922c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 15 Sep 2016 12:06:03 -0400 Subject: [PATCH 83/99] only deconflict reified namespaces --- src/Bundle.js | 15 ++++++--------- .../namespace-optimization-b/_expected/amd.js | 8 ++++---- .../namespace-optimization-b/_expected/cjs.js | 8 ++++---- .../form/namespace-optimization-b/_expected/es.js | 8 ++++---- .../namespace-optimization-b/_expected/iife.js | 8 ++++---- .../namespace-optimization-b/_expected/umd.js | 8 ++++---- 6 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/Bundle.js b/src/Bundle.js index d3ec8b5..2716102 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -208,15 +208,12 @@ export default class Bundle { declaration.name = getSafeName( declaration.name ); }); - // special case - for `import * as Foo`, we need to make sure that Foo - // gets its own variable because it will eventually be rendered as - // `var Foo = Object.freeze(...)` - forOwn( module.imports, ( importee, importeeName ) => { - if ( importee.name === '*' ) { - delete module.imports[ importeeName ]; - module.imports[ getSafeName(importeeName) ] = importee; - } - }); + + // deconflict reified namespaces + const namespace = module.namespace(); + if ( namespace.needsNamespaceBlock ) { + namespace.name = getSafeName( namespace.name ); + } }); this.scope.deshadow( toDeshadow ); diff --git a/test/form/namespace-optimization-b/_expected/amd.js b/test/form/namespace-optimization-b/_expected/amd.js index 45db05f..e2582cc 100644 --- a/test/form/namespace-optimization-b/_expected/amd.js +++ b/test/form/namespace-optimization-b/_expected/amd.js @@ -1,12 +1,12 @@ define(function () { 'use strict'; - function foo$1 () { + function foo () { console.log( 'foo' ); } function a () { - foo$1(); - foo$1(); + foo(); + foo(); var a; if ( a.b ) { @@ -16,4 +16,4 @@ define(function () { 'use strict'; a(); -}); \ No newline at end of file +}); diff --git a/test/form/namespace-optimization-b/_expected/cjs.js b/test/form/namespace-optimization-b/_expected/cjs.js index 5891df9..a826df2 100644 --- a/test/form/namespace-optimization-b/_expected/cjs.js +++ b/test/form/namespace-optimization-b/_expected/cjs.js @@ -1,12 +1,12 @@ 'use strict'; -function foo$1 () { +function foo () { console.log( 'foo' ); } function a () { - foo$1(); - foo$1(); + foo(); + foo(); var a; if ( a.b ) { @@ -14,4 +14,4 @@ function a () { } } -a(); \ No newline at end of file +a(); diff --git a/test/form/namespace-optimization-b/_expected/es.js b/test/form/namespace-optimization-b/_expected/es.js index c63d076..85c15d8 100644 --- a/test/form/namespace-optimization-b/_expected/es.js +++ b/test/form/namespace-optimization-b/_expected/es.js @@ -1,10 +1,10 @@ -function foo$1 () { +function foo () { console.log( 'foo' ); } function a () { - foo$1(); - foo$1(); + foo(); + foo(); var a; if ( a.b ) { @@ -12,4 +12,4 @@ function a () { } } -a(); \ No newline at end of file +a(); diff --git a/test/form/namespace-optimization-b/_expected/iife.js b/test/form/namespace-optimization-b/_expected/iife.js index bfc4c08..6c497f5 100644 --- a/test/form/namespace-optimization-b/_expected/iife.js +++ b/test/form/namespace-optimization-b/_expected/iife.js @@ -1,13 +1,13 @@ (function () { 'use strict'; - function foo$1 () { + function foo () { console.log( 'foo' ); } function a () { - foo$1(); - foo$1(); + foo(); + foo(); var a; if ( a.b ) { @@ -17,4 +17,4 @@ a(); -}()); \ No newline at end of file +}()); diff --git a/test/form/namespace-optimization-b/_expected/umd.js b/test/form/namespace-optimization-b/_expected/umd.js index a95eafd..7a2c4b0 100644 --- a/test/form/namespace-optimization-b/_expected/umd.js +++ b/test/form/namespace-optimization-b/_expected/umd.js @@ -4,13 +4,13 @@ (factory()); }(this, (function () { 'use strict'; - function foo$1 () { + function foo () { console.log( 'foo' ); } function a () { - foo$1(); - foo$1(); + foo(); + foo(); var a; if ( a.b ) { @@ -20,4 +20,4 @@ a(); -}))); \ No newline at end of file +}))); From 5d5eb24b15562bb1a41a3e8c98e15ba02353896c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 15 Sep 2016 12:08:56 -0400 Subject: [PATCH 84/99] -> v0.35.11 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32f446e..05c057e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.35.11 + +* Deconflict reified namespaces with other declarations ([#910](https://github.com/rollup/rollup/issues/910)) + ## 0.35.10 * Only remove EmptyStatement nodes directly inside blocks ([#913](https://github.com/rollup/rollup/issues/931)) diff --git a/package.json b/package.json index 17351f7..af215c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.10", + "version": "0.35.11", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From 211fa097a929a049584a520196d182a3128316a6 Mon Sep 17 00:00:00 2001 From: Emil Ajdyna Date: Fri, 16 Sep 2016 11:41:02 +0200 Subject: [PATCH 85/99] add: interop option --- LICENSE.md | 2 +- src/finalisers/amd.js | 2 +- src/finalisers/iife.js | 2 +- src/finalisers/shared/getInteropBlock.js | 4 ++-- src/finalisers/umd.js | 2 +- src/rollup.js | 1 + test/form/interop-false/_config.js | 7 +++++++ test/form/interop-false/_expected/amd.js | 8 ++++++++ test/form/interop-false/_expected/cjs.js | 10 ++++++++++ test/form/interop-false/_expected/es.js | 6 ++++++ test/form/interop-false/_expected/iife.js | 9 +++++++++ test/form/interop-false/_expected/umd.js | 12 ++++++++++++ test/form/interop-false/main.js | 3 +++ test/test.js | 2 +- 14 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 test/form/interop-false/_config.js create mode 100644 test/form/interop-false/_expected/amd.js create mode 100644 test/form/interop-false/_expected/cjs.js create mode 100644 test/form/interop-false/_expected/es.js create mode 100644 test/form/interop-false/_expected/iife.js create mode 100644 test/form/interop-false/_expected/umd.js create mode 100644 test/form/interop-false/main.js diff --git a/LICENSE.md b/LICENSE.md index ae037ce..ec3180a 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 [these people](https://github.com/rollup/rollup/graphs/contributors) +Copyright (c) 2016 [these people](https://github.com/rollup/rollup/graphs/contributors) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/finalisers/amd.js b/src/finalisers/amd.js index 456dd86..96626e2 100644 --- a/src/finalisers/amd.js +++ b/src/finalisers/amd.js @@ -20,7 +20,7 @@ export default function amd ( bundle, magicString, { exportMode, indentString, i const wrapperStart = `define(${params}function (${args.join( ', ' )}) {${useStrict}\n\n`; // var foo__default = 'default' in foo ? foo['default'] : foo; - const interopBlock = getInteropBlock( bundle ); + const interopBlock = getInteropBlock( bundle, options ); if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' ); if ( intro ) magicString.prepend( intro ); diff --git a/src/finalisers/iife.js b/src/finalisers/iife.js index 612f70f..8120594 100644 --- a/src/finalisers/iife.js +++ b/src/finalisers/iife.js @@ -49,7 +49,7 @@ export default function iife ( bundle, magicString, { exportMode, indentString, } // var foo__default = 'default' in foo ? foo['default'] : foo; - const interopBlock = getInteropBlock( bundle ); + const interopBlock = getInteropBlock( bundle, options ); if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' ); if ( intro ) magicString.prepend( intro ); diff --git a/src/finalisers/shared/getInteropBlock.js b/src/finalisers/shared/getInteropBlock.js index 67a26d7..6ecdba0 100644 --- a/src/finalisers/shared/getInteropBlock.js +++ b/src/finalisers/shared/getInteropBlock.js @@ -1,7 +1,7 @@ -export default function getInteropBlock ( bundle ) { +export default function getInteropBlock ( bundle, options ) { return bundle.externalModules .map( module => { - if ( !module.declarations.default ) return null; + if ( !module.declarations.default || options.interop === false ) return null; if ( module.exportsNamespace ) { return `${bundle.varOrConst} ${module.name}__default = ${module.name}['default'];`; diff --git a/src/finalisers/umd.js b/src/finalisers/umd.js index a36db02..f2be2a2 100644 --- a/src/finalisers/umd.js +++ b/src/finalisers/umd.js @@ -66,7 +66,7 @@ export default function umd ( bundle, magicString, { exportMode, indentString, i `.replace( /^\t\t/gm, '' ).replace( /^\t/gm, magicString.getIndentString() ); // var foo__default = 'default' in foo ? foo['default'] : foo; - const interopBlock = getInteropBlock( bundle ); + const interopBlock = getInteropBlock( bundle, options ); if ( interopBlock ) magicString.prepend( interopBlock + '\n\n' ); if ( intro ) magicString.prepend( intro ); diff --git a/src/rollup.js b/src/rollup.js index 2ab5b51..be39d0b 100644 --- a/src/rollup.js +++ b/src/rollup.js @@ -22,6 +22,7 @@ const ALLOWED_KEYS = [ 'format', 'globals', 'indent', + 'interop', 'intro', 'moduleId', 'moduleName', diff --git a/test/form/interop-false/_config.js b/test/form/interop-false/_config.js new file mode 100644 index 0000000..0076e3a --- /dev/null +++ b/test/form/interop-false/_config.js @@ -0,0 +1,7 @@ +module.exports = { + description: 'getInterop with interop: false', + options: { + moduleName: 'foo', + interop: false + } +}; diff --git a/test/form/interop-false/_expected/amd.js b/test/form/interop-false/_expected/amd.js new file mode 100644 index 0000000..3ee81e0 --- /dev/null +++ b/test/form/interop-false/_expected/amd.js @@ -0,0 +1,8 @@ +define(['core/view'], function (View) { 'use strict'; + + /*eslint import/no-unresolved: 0*/ + var main = View.extend({}); + + return main; + +}); \ No newline at end of file diff --git a/test/form/interop-false/_expected/cjs.js b/test/form/interop-false/_expected/cjs.js new file mode 100644 index 0000000..4813a5d --- /dev/null +++ b/test/form/interop-false/_expected/cjs.js @@ -0,0 +1,10 @@ +'use strict'; + +function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } + +var View = _interopDefault(require('core/view')); + +/*eslint import/no-unresolved: 0*/ +var main = View.extend({}); + +module.exports = main; \ No newline at end of file diff --git a/test/form/interop-false/_expected/es.js b/test/form/interop-false/_expected/es.js new file mode 100644 index 0000000..d404c44 --- /dev/null +++ b/test/form/interop-false/_expected/es.js @@ -0,0 +1,6 @@ +import View from 'core/view'; + +/*eslint import/no-unresolved: 0*/ +var main = View.extend({}); + +export default main; \ No newline at end of file diff --git a/test/form/interop-false/_expected/iife.js b/test/form/interop-false/_expected/iife.js new file mode 100644 index 0000000..fe0202b --- /dev/null +++ b/test/form/interop-false/_expected/iife.js @@ -0,0 +1,9 @@ +var foo = (function (View) { + 'use strict'; + + /*eslint import/no-unresolved: 0*/ + var main = View.extend({}); + + return main; + +}(View)); \ No newline at end of file diff --git a/test/form/interop-false/_expected/umd.js b/test/form/interop-false/_expected/umd.js new file mode 100644 index 0000000..2f376e3 --- /dev/null +++ b/test/form/interop-false/_expected/umd.js @@ -0,0 +1,12 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('core/view')) : + typeof define === 'function' && define.amd ? define(['core/view'], factory) : + (global.foo = factory(global.View)); +}(this, (function (View) { 'use strict'; + + /*eslint import/no-unresolved: 0*/ + var main = View.extend({}); + + return main; + +}))); \ No newline at end of file diff --git a/test/form/interop-false/main.js b/test/form/interop-false/main.js new file mode 100644 index 0000000..8d67d61 --- /dev/null +++ b/test/form/interop-false/main.js @@ -0,0 +1,3 @@ +/*eslint import/no-unresolved: 0*/ +import View from 'core/view'; +export default View.extend({}); \ No newline at end of file diff --git a/test/test.js b/test/test.js index ca11679..5876435 100644 --- a/test/test.js +++ b/test/test.js @@ -90,7 +90,7 @@ describe( 'rollup', function () { return rollup.rollup({ entry: 'x', plUgins: [] }).then( () => { throw new Error( 'Missing expected error' ); }, err => { - assert.equal( err.message, 'Unexpected key \'plUgins\' found, expected one of: acorn, banner, cache, context, dest, entry, exports, external, footer, format, globals, indent, intro, moduleId, moduleName, noConflict, onwarn, outro, paths, plugins, preferConst, sourceMap, sourceMapFile, targets, treeshake, useStrict' ); + assert.equal( err.message, 'Unexpected key \'plUgins\' found, expected one of: acorn, banner, cache, context, dest, entry, exports, external, footer, format, globals, indent, interop, intro, moduleId, moduleName, noConflict, onwarn, outro, paths, plugins, preferConst, sourceMap, sourceMapFile, targets, treeshake, useStrict' ); }); }); From 303333b31019ad92d7bda54ad2a558e126ed9d6e Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 17 Sep 2016 22:59:14 -0400 Subject: [PATCH 86/99] enable interop option in CJS bundles --- src/finalisers/cjs.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/finalisers/cjs.js b/src/finalisers/cjs.js index 0ffbce8..5af4df7 100644 --- a/src/finalisers/cjs.js +++ b/src/finalisers/cjs.js @@ -8,11 +8,12 @@ export default function cjs ( bundle, magicString, { exportMode, intro }, option let needsInterop = false; const varOrConst = bundle.varOrConst; + const interop = options.interop !== false; // TODO handle empty imports, once they're supported const importBlock = bundle.externalModules .map( module => { - if ( module.declarations.default ) { + if ( interop && module.declarations.default ) { if ( module.exportsNamespace ) { return `${varOrConst} ${module.name} = require('${module.path}');` + `\n${varOrConst} ${module.name}__default = ${module.name}['default'];`; From a2487a455545fd08e80462899cdd511d03f8114a Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 17 Sep 2016 22:59:22 -0400 Subject: [PATCH 87/99] remove eslint comment --- test/form/interop-false/_expected/amd.js | 3 +-- test/form/interop-false/_expected/cjs.js | 7 ++----- test/form/interop-false/_expected/es.js | 3 +-- test/form/interop-false/_expected/iife.js | 3 +-- test/form/interop-false/_expected/umd.js | 3 +-- test/form/interop-false/main.js | 3 +-- 6 files changed, 7 insertions(+), 15 deletions(-) diff --git a/test/form/interop-false/_expected/amd.js b/test/form/interop-false/_expected/amd.js index 3ee81e0..9245883 100644 --- a/test/form/interop-false/_expected/amd.js +++ b/test/form/interop-false/_expected/amd.js @@ -1,8 +1,7 @@ define(['core/view'], function (View) { 'use strict'; - /*eslint import/no-unresolved: 0*/ var main = View.extend({}); return main; -}); \ No newline at end of file +}); diff --git a/test/form/interop-false/_expected/cjs.js b/test/form/interop-false/_expected/cjs.js index 4813a5d..8396bc7 100644 --- a/test/form/interop-false/_expected/cjs.js +++ b/test/form/interop-false/_expected/cjs.js @@ -1,10 +1,7 @@ 'use strict'; -function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } +var View = require('core/view'); -var View = _interopDefault(require('core/view')); - -/*eslint import/no-unresolved: 0*/ var main = View.extend({}); -module.exports = main; \ No newline at end of file +module.exports = main; diff --git a/test/form/interop-false/_expected/es.js b/test/form/interop-false/_expected/es.js index d404c44..d5e775e 100644 --- a/test/form/interop-false/_expected/es.js +++ b/test/form/interop-false/_expected/es.js @@ -1,6 +1,5 @@ import View from 'core/view'; -/*eslint import/no-unresolved: 0*/ var main = View.extend({}); -export default main; \ No newline at end of file +export default main; diff --git a/test/form/interop-false/_expected/iife.js b/test/form/interop-false/_expected/iife.js index fe0202b..d94c432 100644 --- a/test/form/interop-false/_expected/iife.js +++ b/test/form/interop-false/_expected/iife.js @@ -1,9 +1,8 @@ var foo = (function (View) { 'use strict'; - /*eslint import/no-unresolved: 0*/ var main = View.extend({}); return main; -}(View)); \ No newline at end of file +}(View)); diff --git a/test/form/interop-false/_expected/umd.js b/test/form/interop-false/_expected/umd.js index 2f376e3..5c9be13 100644 --- a/test/form/interop-false/_expected/umd.js +++ b/test/form/interop-false/_expected/umd.js @@ -4,9 +4,8 @@ (global.foo = factory(global.View)); }(this, (function (View) { 'use strict'; - /*eslint import/no-unresolved: 0*/ var main = View.extend({}); return main; -}))); \ No newline at end of file +}))); diff --git a/test/form/interop-false/main.js b/test/form/interop-false/main.js index 8d67d61..a6b1040 100644 --- a/test/form/interop-false/main.js +++ b/test/form/interop-false/main.js @@ -1,3 +1,2 @@ -/*eslint import/no-unresolved: 0*/ import View from 'core/view'; -export default View.extend({}); \ No newline at end of file +export default View.extend({}); From 05e12b7b3e27217cad810acf05e08de0e9d37dc6 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 17 Sep 2016 23:12:08 -0400 Subject: [PATCH 88/99] -> v0.35.12 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05c057e..0548e8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.35.12 + +* Add `interop: false` option to disable unwrapping of external imports ([#939](https://github.com/rollup/rollup/issues/939)) + ## 0.35.11 * Deconflict reified namespaces with other declarations ([#910](https://github.com/rollup/rollup/issues/910)) diff --git a/package.json b/package.json index af215c5..c78a456 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.11", + "version": "0.35.12", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From b26917bff6c808d020a87ebc9f3230af0c5f96ce Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 18 Sep 2016 09:53:00 -0400 Subject: [PATCH 89/99] include superclass when including a class declaration (#932) --- src/ast/nodes/ClassDeclaration.js | 3 +++ test/function/includes-superclass/_config.js | 3 +++ test/function/includes-superclass/base.js | 5 +++++ test/function/includes-superclass/main.js | 6 ++++++ test/function/includes-superclass/thing.js | 7 +++++++ 5 files changed, 24 insertions(+) create mode 100644 test/function/includes-superclass/_config.js create mode 100644 test/function/includes-superclass/base.js create mode 100644 test/function/includes-superclass/main.js create mode 100644 test/function/includes-superclass/thing.js diff --git a/src/ast/nodes/ClassDeclaration.js b/src/ast/nodes/ClassDeclaration.js index f67c87d..f1a1daa 100644 --- a/src/ast/nodes/ClassDeclaration.js +++ b/src/ast/nodes/ClassDeclaration.js @@ -6,6 +6,7 @@ export default class ClassDeclaration extends Node { if ( this.activated ) return; this.activated = true; + if ( this.superClass ) this.superClass.run( this.scope ); this.body.run(); } @@ -26,6 +27,8 @@ export default class ClassDeclaration extends Node { } initialise ( scope ) { + this.scope = scope; + this.name = this.id.name; scope.addDeclaration( this.name, this, false, false ); diff --git a/test/function/includes-superclass/_config.js b/test/function/includes-superclass/_config.js new file mode 100644 index 0000000..7baf385 --- /dev/null +++ b/test/function/includes-superclass/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'includes superclass (#932)' +}; diff --git a/test/function/includes-superclass/base.js b/test/function/includes-superclass/base.js new file mode 100644 index 0000000..c374794 --- /dev/null +++ b/test/function/includes-superclass/base.js @@ -0,0 +1,5 @@ +export class Base { + foo () { + return true; + } +} diff --git a/test/function/includes-superclass/main.js b/test/function/includes-superclass/main.js new file mode 100644 index 0000000..bfb78e8 --- /dev/null +++ b/test/function/includes-superclass/main.js @@ -0,0 +1,6 @@ +import { Thing } from './thing'; + +const thing = new Thing(); + +assert.ok( thing.foo() ); +assert.ok( thing.bar() ); diff --git a/test/function/includes-superclass/thing.js b/test/function/includes-superclass/thing.js new file mode 100644 index 0000000..97b3be8 --- /dev/null +++ b/test/function/includes-superclass/thing.js @@ -0,0 +1,7 @@ +import { Base } from './base.js'; + +export class Thing extends Base { + bar () { + return true; + } +} From 036eae5ab116d5aa343953b2db64d3572d1af317 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 18 Sep 2016 09:56:41 -0400 Subject: [PATCH 90/99] transpile test for 0.12 --- test/function/includes-superclass/_config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/function/includes-superclass/_config.js b/test/function/includes-superclass/_config.js index 7baf385..e226472 100644 --- a/test/function/includes-superclass/_config.js +++ b/test/function/includes-superclass/_config.js @@ -1,3 +1,4 @@ module.exports = { - description: 'includes superclass (#932)' + description: 'includes superclass (#932)', + buble: true }; From b5a34d60bac0e9a2d65df4d900b4d785b77db332 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 18 Sep 2016 10:02:35 -0400 Subject: [PATCH 91/99] -> v0.35.13 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0548e8f..c13bc94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.35.13 + +* Include superclasses when including their subclasses ([#932](https://github.com/rollup/rollup/issues/932)) + ## 0.35.12 * Add `interop: false` option to disable unwrapping of external imports ([#939](https://github.com/rollup/rollup/issues/939)) diff --git a/package.json b/package.json index c78a456..0bb95d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.12", + "version": "0.35.13", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From 819d619aa14d27e0d59e2bcd3ed93f2dccff8833 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 18 Sep 2016 10:50:08 -0400 Subject: [PATCH 92/99] include all ancestors of expression with effects, up to function boundary - fixes #930 --- src/Bundle.js | 4 +++- src/ast/nodes/UpdateExpression.js | 4 +++- test/function/if-statement-with-assignment/_config.js | 3 +++ test/function/if-statement-with-assignment/main.js | 4 ++++ test/function/if-statement-with-update/_config.js | 3 +++ test/function/if-statement-with-update/main.js | 4 ++++ 6 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 test/function/if-statement-with-assignment/_config.js create mode 100644 test/function/if-statement-with-assignment/main.js create mode 100644 test/function/if-statement-with-update/_config.js create mode 100644 test/function/if-statement-with-update/main.js diff --git a/src/Bundle.js b/src/Bundle.js index 2716102..d56f4a5 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -141,7 +141,9 @@ export default class Bundle { let i = this.dependentExpressions.length; while ( i-- ) { const expression = this.dependentExpressions[i]; - const statement = expression.findParent( /ExpressionStatement/ ); + + let statement = expression; + while ( statement.parent && !/Function/.test( statement.parent.type ) ) statement = statement.parent; if ( !statement || statement.ran ) { this.dependentExpressions.splice( i, 1 ); diff --git a/src/ast/nodes/UpdateExpression.js b/src/ast/nodes/UpdateExpression.js index c5b9263..656da69 100644 --- a/src/ast/nodes/UpdateExpression.js +++ b/src/ast/nodes/UpdateExpression.js @@ -28,11 +28,13 @@ export default class UpdateExpression extends Node { } initialise ( scope ) { + this.scope = scope; + this.module.bundle.dependentExpressions.push( this ); super.initialise( scope ); } isUsedByBundle () { - return isUsedByBundle( this.findScope(), this.subject ); + return isUsedByBundle( this.scope, this.subject ); } } diff --git a/test/function/if-statement-with-assignment/_config.js b/test/function/if-statement-with-assignment/_config.js new file mode 100644 index 0000000..6c573d5 --- /dev/null +++ b/test/function/if-statement-with-assignment/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'update assignments to names are preserved (#930)' +}; diff --git a/test/function/if-statement-with-assignment/main.js b/test/function/if-statement-with-assignment/main.js new file mode 100644 index 0000000..612436b --- /dev/null +++ b/test/function/if-statement-with-assignment/main.js @@ -0,0 +1,4 @@ +var result = 0; +if ( Math.random() <= 1 ) result += 1; + +assert.equal( result, 1 ); diff --git a/test/function/if-statement-with-update/_config.js b/test/function/if-statement-with-update/_config.js new file mode 100644 index 0000000..8601e12 --- /dev/null +++ b/test/function/if-statement-with-update/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'updates to names are preserved (#930)' +}; diff --git a/test/function/if-statement-with-update/main.js b/test/function/if-statement-with-update/main.js new file mode 100644 index 0000000..9239794 --- /dev/null +++ b/test/function/if-statement-with-update/main.js @@ -0,0 +1,4 @@ +var result = 0; +if ( Math.random() <= 1 ) ++result; + +assert.equal( result, 1 ); From f143b01c6206366a1a7a702d79b514ffcaedd696 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 18 Sep 2016 11:28:34 -0400 Subject: [PATCH 93/99] beef up test --- test/function/if-statement-with-assignment/main.js | 4 +++- test/function/if-statement-with-update/main.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test/function/if-statement-with-assignment/main.js b/test/function/if-statement-with-assignment/main.js index 612436b..e885c22 100644 --- a/test/function/if-statement-with-assignment/main.js +++ b/test/function/if-statement-with-assignment/main.js @@ -1,4 +1,6 @@ var result = 0; -if ( Math.random() <= 1 ) result += 1; +if ( Math.random() <= 1 ) { + if ( Math.random() <= 1 ) result += 1; +} assert.equal( result, 1 ); diff --git a/test/function/if-statement-with-update/main.js b/test/function/if-statement-with-update/main.js index 9239794..7ab3055 100644 --- a/test/function/if-statement-with-update/main.js +++ b/test/function/if-statement-with-update/main.js @@ -1,4 +1,6 @@ var result = 0; -if ( Math.random() <= 1 ) ++result; +if ( Math.random() <= 1 ) { + if ( Math.random() <= 1 ) ++result; +} assert.equal( result, 1 ); From 42ed5c24ba44f4f59c5da9c811f29d5692799880 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 18 Sep 2016 11:39:47 -0400 Subject: [PATCH 94/99] -> v0.35.14 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c13bc94..62d02b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # rollup changelog +## 0.35.14 + +* Include all parent statements of expression with effects, up to function boundary ([#930](https://github.com/rollup/rollup/issues/930)) + ## 0.35.13 * Include superclasses when including their subclasses ([#932](https://github.com/rollup/rollup/issues/932)) diff --git a/package.json b/package.json index 0bb95d3..ec1c88e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.13", + "version": "0.35.14", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From 3a7322499035b0d030e38f7a4fca8eb38cc3a94c Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 18 Sep 2016 12:24:17 -0400 Subject: [PATCH 95/99] =?UTF-8?q?ensure=20bundle=20ends=20with=20newline?= =?UTF-8?q?=20=E2=80=93=20fixes=20#958?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Bundle.js | 1 + src/rollup.js | 2 +- test/test.js | 10 ++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Bundle.js b/src/Bundle.js index d56f4a5..a4e8c53 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -431,6 +431,7 @@ export default class Bundle { timeEnd( 'sourceMap' ); } + if ( code[ code.length - 1 ] !== '\n' ) code += '\n'; return { code, map }; } diff --git a/src/rollup.js b/src/rollup.js index be39d0b..0868960 100644 --- a/src/rollup.js +++ b/src/rollup.js @@ -108,7 +108,7 @@ export function rollup ( options ) { promises.push( writeFile( dest + '.map', map.toString() ) ); } - code += `\n//# ${SOURCEMAPPING_URL}=${url}\n`; + code += `//# ${SOURCEMAPPING_URL}=${url}\n`; } promises.push( writeFile( dest, code ) ); diff --git a/test/test.js b/test/test.js index 5876435..52cc00e 100644 --- a/test/test.js +++ b/test/test.js @@ -102,6 +102,16 @@ describe( 'rollup', function () { plugins: [ loader({ x: `var a = null; a = 'a string';` }) ] }); }); + + it( 'includes a newline at the end of the bundle', () => { + return rollup.rollup({ + entry: 'x', + plugins: [ loader({ x: `console.log( 42 );` }) ] + }).then( bundle => { + const { code } = bundle.generate({ format: 'iife' }); + assert.ok( code[ code.length - 1 ] === '\n' ); + }); + }); }); describe( 'bundle.write()', () => { From e152bb95c588a13ce05b0434572bcad6c54728c9 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 18 Sep 2016 15:03:20 -0400 Subject: [PATCH 96/99] warn on missing unused imports in deshadowing phase, rather than throwing - fixes #928 --- src/ast/scopes/ModuleScope.js | 5 +++++ .../warn-on-unused-missing-imports/_config.js | 12 ++++++++++++ test/function/warn-on-unused-missing-imports/foo.js | 1 + test/function/warn-on-unused-missing-imports/main.js | 3 +++ 4 files changed, 21 insertions(+) create mode 100644 test/function/warn-on-unused-missing-imports/_config.js create mode 100644 test/function/warn-on-unused-missing-imports/foo.js create mode 100644 test/function/warn-on-unused-missing-imports/main.js diff --git a/src/ast/scopes/ModuleScope.js b/src/ast/scopes/ModuleScope.js index 051fcb4..5a18d6f 100644 --- a/src/ast/scopes/ModuleScope.js +++ b/src/ast/scopes/ModuleScope.js @@ -22,8 +22,13 @@ export default class ModuleScope extends Scope { specifier.module.getExports().forEach( name => { names.set(name); }); + if ( specifier.name !== '*' ) { const declaration = specifier.module.traceExport( specifier.name ); + if ( !declaration ) { + this.module.bundle.onwarn( `Non-existent export '${specifier.name}' is imported from ${specifier.module.id} by ${this.module.id}` ); + return; + } const name = declaration.getName( true ); if ( name !== specifier.name ) { names.set( declaration.getName( true ) ); diff --git a/test/function/warn-on-unused-missing-imports/_config.js b/test/function/warn-on-unused-missing-imports/_config.js new file mode 100644 index 0000000..16d69c1 --- /dev/null +++ b/test/function/warn-on-unused-missing-imports/_config.js @@ -0,0 +1,12 @@ +const path = require( 'path' ); +const assert = require( 'assert' ); + +module.exports = { + solo: true, + description: 'warns on missing (but unused) imports', + warnings: warnings => { + assert.deepEqual( warnings, [ + `Non-existent export 'b' is imported from ${path.resolve(__dirname, 'foo.js')} by ${path.resolve(__dirname, 'main.js')}` + ]); + } +}; diff --git a/test/function/warn-on-unused-missing-imports/foo.js b/test/function/warn-on-unused-missing-imports/foo.js new file mode 100644 index 0000000..71becd3 --- /dev/null +++ b/test/function/warn-on-unused-missing-imports/foo.js @@ -0,0 +1 @@ +export const a = 42; diff --git a/test/function/warn-on-unused-missing-imports/main.js b/test/function/warn-on-unused-missing-imports/main.js new file mode 100644 index 0000000..7eaceb9 --- /dev/null +++ b/test/function/warn-on-unused-missing-imports/main.js @@ -0,0 +1,3 @@ +import { a, b } from './foo.js'; + +assert.equal( a, 42 ); From fa08af7689c7688f859f17478966abb4315ec803 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 18 Sep 2016 15:24:15 -0400 Subject: [PATCH 97/99] argh 0.12 --- test/function/warn-on-unused-missing-imports/foo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/function/warn-on-unused-missing-imports/foo.js b/test/function/warn-on-unused-missing-imports/foo.js index 71becd3..18e60c8 100644 --- a/test/function/warn-on-unused-missing-imports/foo.js +++ b/test/function/warn-on-unused-missing-imports/foo.js @@ -1 +1 @@ -export const a = 42; +export var a = 42; From 5001d2b2cb38d689913a013739091618d0aebe37 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 18 Sep 2016 15:38:55 -0400 Subject: [PATCH 98/99] -> v0.35.15 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62d02b5..aa4f31b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # rollup changelog +## 0.35.15 + +* Warn on missing unused imports in deshadowing phase ([#928](https://github.com/rollup/rollup/issues/928)) +* Always add a newline to the end of bundles ([#958](https://github.com/rollup/rollup/issues/958)) + ## 0.35.14 * Include all parent statements of expression with effects, up to function boundary ([#930](https://github.com/rollup/rollup/issues/930)) diff --git a/package.json b/package.json index ec1c88e..84ae321 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "0.35.14", + "version": "0.35.15", "description": "Next-generation ES6 module bundler", "main": "dist/rollup.js", "module": "dist/rollup.es.js", From 609cf2b3c6ef3e788fb7c40e2671675b3513d5f1 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 18 Sep 2016 15:40:29 -0400 Subject: [PATCH 99/99] oops, reinstate all tests --- test/function/warn-on-unused-missing-imports/_config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/function/warn-on-unused-missing-imports/_config.js b/test/function/warn-on-unused-missing-imports/_config.js index 16d69c1..1fca512 100644 --- a/test/function/warn-on-unused-missing-imports/_config.js +++ b/test/function/warn-on-unused-missing-imports/_config.js @@ -2,7 +2,6 @@ const path = require( 'path' ); const assert = require( 'assert' ); module.exports = { - solo: true, description: 'warns on missing (but unused) imports', warnings: warnings => { assert.deepEqual( warnings, [