From fa497ad771351e66f9b2350ee8d26065e8fb97d4 Mon Sep 17 00:00:00 2001 From: Lukas Taegert Date: Wed, 2 Aug 2017 06:02:04 +0200 Subject: [PATCH 1/2] Always store scope in nodes and clean up scope handling --- src/Bundle.js | 150 +++++++++++----------- src/Module.js | 86 ++++++------- src/ast/Node.js | 44 ++++--- src/ast/nodes/ArrowFunctionExpression.js | 42 ++---- src/ast/nodes/AssignmentExpression.js | 18 +-- src/ast/nodes/BinaryExpression.js | 2 +- src/ast/nodes/BlockStatement.js | 49 +++---- src/ast/nodes/CallExpression.js | 19 ++- src/ast/nodes/CatchClause.js | 21 ++- src/ast/nodes/ClassDeclaration.js | 35 ++--- src/ast/nodes/ClassExpression.js | 28 +--- src/ast/nodes/ConditionalExpression.js | 20 ++- src/ast/nodes/ExportAllDeclaration.js | 2 +- src/ast/nodes/ExportDefaultDeclaration.js | 34 +++-- src/ast/nodes/ExportNamedDeclaration.js | 11 +- src/ast/nodes/ForInStatement.js | 27 ++-- src/ast/nodes/ForOfStatement.js | 27 ++-- src/ast/nodes/ForStatement.js | 26 ++-- src/ast/nodes/FunctionDeclaration.js | 37 ++---- src/ast/nodes/FunctionExpression.js | 35 ++--- src/ast/nodes/Identifier.js | 40 +++--- src/ast/nodes/IfStatement.js | 49 ++++--- src/ast/nodes/ImportDeclaration.js | 2 +- src/ast/nodes/Literal.js | 2 +- src/ast/nodes/MemberExpression.js | 18 +-- src/ast/nodes/NewExpression.js | 4 +- src/ast/nodes/ReturnStatement.js | 7 - src/ast/nodes/TemplateLiteral.js | 2 +- src/ast/nodes/ThisExpression.js | 6 +- src/ast/nodes/UnaryExpression.js | 19 ++- src/ast/nodes/UpdateExpression.js | 17 +-- src/ast/nodes/VariableDeclaration.js | 79 ++++++------ src/ast/nodes/VariableDeclarator.js | 23 ++-- src/ast/nodes/index.js | 3 +- src/ast/nodes/shared/Class.js | 32 +++++ src/ast/nodes/shared/Function.js | 33 +++++ src/ast/nodes/shared/Statement.js | 4 +- 37 files changed, 489 insertions(+), 564 deletions(-) delete mode 100644 src/ast/nodes/ReturnStatement.js create mode 100644 src/ast/nodes/shared/Class.js create mode 100644 src/ast/nodes/shared/Function.js diff --git a/src/Bundle.js b/src/Bundle.js index 884eae76eac..216afad745a 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -27,7 +27,7 @@ export default class Bundle { if ( options.cache ) { options.cache.modules.forEach( module => { this.cachedModules.set( module.id, module ); - }); + } ); } this.plugins = ensureArray( options.plugins ); @@ -35,7 +35,7 @@ export default class Bundle { options = this.plugins.reduce( ( acc, plugin ) => { if ( plugin.options ) return plugin.options( acc ) || acc; return acc; - }, options); + }, options ); if ( !options.entry ) { throw new Error( 'You must supply options.entry to rollup' ); @@ -47,13 +47,13 @@ export default class Bundle { this.treeshake = options.treeshake !== false; - if (options.pureExternalModules === true) { + if ( options.pureExternalModules === true ) { this.isPureExternalModule = () => true; - } else if (typeof options.pureExternalModules === 'function') { + } else if ( typeof options.pureExternalModules === 'function' ) { this.isPureExternalModule = options.pureExternalModules; - } else if (Array.isArray(options.pureExternalModules)) { - const pureExternalModules = new Set(options.pureExternalModules); - this.isPureExternalModule = id => pureExternalModules.has(id); + } else if ( Array.isArray( options.pureExternalModules ) ) { + const pureExternalModules = new Set( options.pureExternalModules ); + this.isPureExternalModule = id => pureExternalModules.has( id ); } else { this.isPureExternalModule = () => false; } @@ -81,7 +81,7 @@ export default class Bundle { // 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 = []; @@ -96,7 +96,7 @@ export default class Bundle { const moduleContext = new Map(); Object.keys( optionsModuleContext ).forEach( key => { moduleContext.set( resolve( key ), optionsModuleContext[ key ] ); - }); + } ); this.getModuleContext = id => moduleContext.get( id ) || this.context; } else { this.getModuleContext = () => this.context; @@ -125,22 +125,22 @@ export default class Bundle { return this.resolveId( this.entry, undefined ) .then( id => { if ( id === false ) { - error({ + error( { code: 'UNRESOLVED_ENTRY', message: `Entry module cannot be external` - }); + } ); } if ( id == null ) { - error({ + error( { code: 'UNRESOLVED_ENTRY', message: `Could not resolve entry (${this.entry})` - }); + } ); } this.entryId = id; return this.fetchModule( id, undefined ); - }) + } ) .then( entryModule => { this.entryModule = entryModule; @@ -169,13 +169,13 @@ export default class Bundle { if ( declaration.isNamespace ) { declaration.needsNamespaceBlock = true; } - }); + } ); // mark statements that should appear in the bundle if ( this.treeshake ) { this.modules.forEach( module => { module.run(); - }); + } ); let settled = false; while ( !settled ) { @@ -183,7 +183,7 @@ export default class Bundle { let i = this.dependentExpressions.length; while ( i-- ) { - const expression = this.dependentExpressions[i]; + const expression = this.dependentExpressions[ i ]; let statement = expression; while ( statement.parent && !/Function/.test( statement.parent.type ) ) statement = statement.parent; @@ -192,7 +192,7 @@ export default class Bundle { this.dependentExpressions.splice( i, 1 ); } else if ( expression.isUsedByBundle() ) { settled = false; - statement.run( statement.findScope() ); + statement.run(); this.dependentExpressions.splice( i, 1 ); } } @@ -216,25 +216,25 @@ export default class Bundle { if ( unused.length === 0 ) return; const names = unused.length === 1 ? - `'${unused[0]}' is` : + `'${unused[ 0 ]}' is` : `${unused.slice( 0, -1 ).map( name => `'${name}'` ).join( ', ' )} and '${unused.pop()}' are`; - this.warn({ + this.warn( { code: 'UNUSED_EXTERNAL_IMPORT', message: `${names} imported from external module '${module.id}' but never used` - }); - }); + } ); + } ); // prune unused external imports - this.externalModules = this.externalModules.filter(module => { - return module.used || !this.isPureExternalModule(module.id); - }); + this.externalModules = this.externalModules.filter( module => { + return module.used || !this.isPureExternalModule( module.id ); + } ); this.orderedModules = this.sort(); this.deconflict(); timeEnd( 'phase 4' ); - }); + } ); } deconflict () { @@ -245,7 +245,7 @@ export default class Bundle { function getSafeName ( name ) { while ( used[ name ] ) { - name += `$${used[name]++}`; + name += `$${used[ name ]++}`; } used[ name ] = 1; @@ -265,8 +265,8 @@ export default class Bundle { const safeName = getSafeName( name ); toDeshadow.add( safeName ); declaration.setSafeName( safeName ); - }); - }); + } ); + } ); this.modules.forEach( module => { forOwn( module.scope.declarations, ( declaration ) => { @@ -275,14 +275,14 @@ export default class Bundle { } declaration.name = getSafeName( declaration.name ); - }); + } ); // deconflict reified namespaces const namespace = module.namespace(); if ( namespace.needsNamespaceBlock ) { namespace.name = getSafeName( namespace.name ); } - }); + } ); this.scope.deshadow( toDeshadow ); } @@ -299,17 +299,17 @@ export default class Bundle { msg += `: ${err.message}`; throw new Error( msg ); - }) + } ) .then( source => { if ( typeof source === 'string' ) return source; if ( source && typeof source === 'object' && source.code ) return source; // TODO report which plugin failed - error({ + error( { code: 'BAD_LOADER', message: `Error loading ${relativeId( id )}: plugin load hook should return a string, a { code, map } object, or nothing/null` - }); - }) + } ); + } ) .then( source => { if ( typeof source === 'string' ) { source = { @@ -323,11 +323,11 @@ export default class Bundle { } return transform( this, source, id, this.plugins ); - }) + } ) .then( source => { const { code, originalCode, originalSourceMap, ast, sourceMapChain, resolvedIds } = source; - const module = new Module({ + const module = new Module( { id, code, originalCode, @@ -336,7 +336,7 @@ export default class Bundle { sourceMapChain, resolvedIds, bundle: this - }); + } ); this.modules.push( module ); this.moduleById.set( id, module ); @@ -344,9 +344,9 @@ export default class Bundle { return this.fetchAllDependencies( module ).then( () => { keys( module.exports ).forEach( name => { if ( name !== 'default' ) { - module.exportsAll[name] = module.id; + module.exportsAll[ name ] = module.id; } - }); + } ); module.exportAllSources.forEach( source => { const id = module.resolvedIds[ source ] || module.resolvedExternalIds[ source ]; const exportAllModule = this.moduleById.get( id ); @@ -354,18 +354,18 @@ export default class Bundle { keys( exportAllModule.exportsAll ).forEach( name => { if ( name in module.exportsAll ) { - this.warn({ + this.warn( { code: 'NAMESPACE_CONFLICT', message: `Conflicting namespaces: ${relativeId( module.id )} re-exports '${name}' from both ${relativeId( module.exportsAll[ name ] )} and ${relativeId( exportAllModule.exportsAll[ name ] )} (will be ignored)` - }); + } ); } else { - module.exportsAll[ name ] = exportAllModule.exportsAll[ name ]; + module.exportsAll[ name ] = exportAllModule.exportsAll[ name ]; } - }); - }); + } ); + } ); return module; - }); - }); + } ); + } ); } fetchAllDependencies ( module ) { @@ -374,24 +374,24 @@ export default class Bundle { return ( resolvedId ? Promise.resolve( resolvedId ) : this.resolveId( source, module.id ) ) .then( resolvedId => { const externalId = resolvedId || ( - isRelative( source ) ? resolve( module.id, '..', source ) : source - ); + isRelative( source ) ? resolve( module.id, '..', source ) : source + ); let isExternal = this.isExternal( externalId ); if ( !resolvedId && !isExternal ) { if ( isRelative( source ) ) { - error({ + error( { code: 'UNRESOLVED_IMPORT', message: `Could not resolve '${source}' from ${relativeId( module.id )}` - }); + } ); } - this.warn({ + this.warn( { code: 'UNRESOLVED_IMPORT', message: `'${source}' is imported by ${relativeId( module.id )}, but could not be resolved – treating it as an external dependency`, url: 'https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency' - }); + } ); isExternal = true; } @@ -412,16 +412,16 @@ export default class Bundle { if ( importDeclaration.source !== source ) return; externalModule.traceExport( importDeclaration.name ); - }); + } ); } else { if ( resolvedId === module.id ) { // need to find the actual import declaration, so we can provide // a useful error message. Bit hoop-jumpy but what can you do const declaration = module.ast.body.find( node => { return node.isImportDeclaration && node.source.value === source; - }); + } ); - module.error({ + module.error( { code: 'CANNOT_IMPORT_SELF', message: `A module cannot import itself` }, declaration.start ); @@ -430,8 +430,8 @@ export default class Bundle { module.resolvedIds[ source ] = resolvedId; return this.fetchModule( resolvedId, module.id ); } - }); - }); + } ); + } ); } getPathRelativeToEntryDirname ( resolvedId ) { @@ -448,10 +448,10 @@ export default class Bundle { render ( options = {} ) { return Promise.resolve().then( () => { if ( options.format === 'es6' ) { - this.warn({ + this.warn( { code: 'DEPRECATED_ES6', message: 'The es6 format is deprecated – use `es` instead' - }); + } ); options.format = 'es'; } @@ -459,7 +459,7 @@ export default class Bundle { // Determine export mode - 'default', 'named', 'none' const exportMode = getExportMode( this, options ); - let magicString = new MagicStringBundle({ separator: '\n\n' }); + let magicString = new MagicStringBundle( { separator: '\n\n' } ); const usedModules = []; timeStart( 'render modules' ); @@ -471,13 +471,13 @@ export default class Bundle { magicString.addSource( source ); usedModules.push( module ); } - }); + } ); if ( !magicString.toString().trim() && this.entryModule.getExports().length === 0 ) { - this.warn({ + this.warn( { code: 'EMPTY_BUNDLE', message: 'Generated an empty bundle' - }); + } ); } timeEnd( 'render modules' ); @@ -504,10 +504,10 @@ export default class Bundle { const finalise = finalisers[ options.format ]; if ( !finalise ) { - error({ + error( { code: 'INVALID_OPTION', message: `You must specify an output type - valid options are ${keys( finalisers ).join( ', ' )}` - }); + } ); } timeStart( 'render format' ); @@ -543,13 +543,13 @@ export default class Bundle { if ( file ) file = resolve( typeof process !== 'undefined' ? process.cwd() : '', file ); if ( this.hasLoaders || find( this.plugins, plugin => plugin.transform || plugin.transformBundle ) ) { - map = magicString.generateMap({}); + map = magicString.generateMap( {} ); if ( typeof map.mappings === 'string' ) { map.mappings = decode( map.mappings ); } map = collapseSourcemaps( this, file, map, usedModules, bundleSourcemapChain ); } else { - map = magicString.generateMap({ file, includeContent: true }); + map = magicString.generateMap( { file, includeContent: true } ); } map.sources = map.sources.map( normalize ); @@ -559,8 +559,8 @@ export default class Bundle { if ( code[ code.length - 1 ] !== '\n' ) code += '\n'; return { code, map }; - }); - }); + } ); + } ); } sort () { @@ -574,7 +574,7 @@ export default class Bundle { this.modules.forEach( module => { stronglyDependsOn[ module.id ] = blank(); dependsOn[ module.id ] = blank(); - }); + } ); this.modules.forEach( module => { function processStrongDependency ( dependency ) { @@ -593,7 +593,7 @@ export default class Bundle { module.strongDependencies.forEach( processStrongDependency ); module.dependencies.forEach( processDependency ); - }); + } ); const visit = module => { if ( seen[ module.id ] ) { @@ -612,7 +612,7 @@ export default class Bundle { if ( hasCycles ) { ordered.forEach( ( a, i ) => { for ( i += 1; i < ordered.length; i += 1 ) { - const b = ordered[i]; + const b = ordered[ i ]; // TODO reinstate this! it no longer works if ( stronglyDependsOn[ a.id ][ b.id ] ) { @@ -629,7 +629,7 @@ export default class Bundle { } visited[ module.id ] = true; for ( let i = 0; i < module.dependencies.length; i += 1 ) { - const dependency = module.dependencies[i]; + const dependency = module.dependencies[ i ]; if ( !visited[ dependency.id ] && findParent( dependency ) ) return true; } }; @@ -641,7 +641,7 @@ export default class Bundle { ); } } - }); + } ); } return ordered; diff --git a/src/Module.js b/src/Module.js index bb868548d59..cdd8526d575 100644 --- a/src/Module.js +++ b/src/Module.js @@ -17,14 +17,14 @@ import ModuleScope from './ast/scopes/ModuleScope.js'; function tryParse ( module, acornOptions ) { try { - return parse( module.code, assign({ + return parse( module.code, assign( { ecmaVersion: 8, sourceType: 'module', - onComment: ( block, text, start, end ) => module.comments.push({ block, text, start, end }), + onComment: ( block, text, start, end ) => module.comments.push( { block, text, start, end } ), preserveParens: false - }, acornOptions )); + }, acornOptions ) ); } catch ( err ) { - module.error({ + module.error( { code: 'PARSE_ERROR', message: err.message.replace( / \(\d+:\d+\)$/, '' ) }, err.pos ); @@ -32,7 +32,7 @@ function tryParse ( module, acornOptions ) { } export default class Module { - constructor ({ id, code, originalCode, originalSourceMap, ast, sourceMapChain, resolvedIds, resolvedExternalIds, bundle }) { + constructor ( { id, code, originalCode, originalSourceMap, ast, sourceMapChain, resolvedIds, resolvedExternalIds, bundle } ) { this.code = code; this.id = id; this.bundle = bundle; @@ -79,17 +79,17 @@ export default class Module { this.magicString = new MagicString( code, { filename: this.excludeFromSourcemap ? null : id, // don't include plugin helpers in sourcemap indentExclusionRanges: [] - }); + } ); // remove existing sourceMappingURL comments - this.comments = this.comments.filter(comment => { + this.comments = this.comments.filter( comment => { //only one line comment can contain source maps - const isSourceMapComment = !comment.block && SOURCEMAPPING_URL_RE.test(comment.text); - if (isSourceMapComment) { - this.magicString.remove(comment.start, comment.end ); + const isSourceMapComment = !comment.block && SOURCEMAPPING_URL_RE.test( comment.text ); + if ( isSourceMapComment ) { + this.magicString.remove( comment.start, comment.end ); } return !isSourceMapComment; - }); + } ); this.declarations = blank(); this.type = 'Module'; // TODO only necessary so that Scope knows this should be treated as a function scope... messy @@ -122,7 +122,7 @@ export default class Module { const name = specifier.exported.name; if ( this.exports[ name ] || this.reexports[ name ] ) { - this.error({ + this.error( { code: 'DUPLICATE_EXPORT', message: `A module cannot have multiple exports with the same name ('${name}')` }, specifier.start ); @@ -134,7 +134,7 @@ export default class Module { localName: specifier.local.name, module: null // filled in later }; - }); + } ); } } @@ -145,7 +145,7 @@ export default class Module { const identifier = ( node.declaration.id && node.declaration.id.name ) || node.declaration.name; if ( this.exports.default ) { - this.error({ + this.error( { code: 'DUPLICATE_EXPORT', message: `A module can only have one default export` }, node.start ); @@ -171,8 +171,8 @@ export default class Module { declaration.declarations.forEach( decl => { extractNames( decl.id ).forEach( localName => { this.exports[ localName ] = { localName }; - }); - }); + } ); + } ); } else { // export function foo () {} const localName = declaration.id.name; @@ -188,18 +188,18 @@ export default class Module { const exportedName = specifier.exported.name; if ( this.exports[ exportedName ] || this.reexports[ exportedName ] ) { - this.error({ + this.error( { code: 'DUPLICATE_EXPORT', message: `A module cannot have multiple exports with the same name ('${exportedName}')` }, specifier.start ); } this.exports[ exportedName ] = { localName }; - }); + } ); } else { // TODO is this really necessary? `export {}` is valid JS, and // might be used as a hint that this is indeed a module - this.warn({ + this.warn( { code: 'EMPTY_EXPORT', message: `Empty export declaration` }, node.start ); @@ -216,7 +216,7 @@ export default class Module { const localName = specifier.local.name; if ( this.imports[ localName ] ) { - this.error({ + this.error( { code: 'DUPLICATE_IMPORT', message: `Duplicated import '${localName}'` }, specifier.start ); @@ -227,7 +227,7 @@ export default class Module { const name = isDefault ? 'default' : isNamespace ? '*' : specifier.imported.name; this.imports[ localName ] = { source, specifier, name, module: null }; - }); + } ); } analyse () { @@ -262,13 +262,13 @@ export default class Module { const id = this.resolvedIds[ specifier.source ] || this.resolvedExternalIds[ specifier.source ]; specifier.module = this.bundle.moduleById.get( id ); - }); - }); + } ); + } ); this.exportAllModules = this.exportAllSources.map( source => { const id = this.resolvedIds[ source ] || this.resolvedExternalIds[ source ]; return this.bundle.moduleById.get( id ); - }); + } ); this.sources.forEach( source => { const id = this.resolvedIds[ source ]; @@ -277,12 +277,12 @@ export default class Module { const module = this.bundle.moduleById.get( id ); this.dependencies.push( module ); } - }); + } ); } bindReferences () { for ( const node of this.ast.body ) { - node.bind( this.scope ); + node.bind(); } // if ( this.declarations.default ) { @@ -297,7 +297,7 @@ export default class Module { if ( pos !== undefined ) { props.pos = pos; - const { line, column } = locate( this.code, pos, { offsetLine: 1 }); // TODO trace sourcemaps + const { line, column } = locate( this.code, pos, { offsetLine: 1 } ); // TODO trace sourcemaps props.loc = { file: this.id, line, column }; props.frame = getCodeFrame( this.code, line, column ); @@ -311,20 +311,16 @@ export default class Module { return null; } - findScope () { - return this.scope; - } - getExports () { const exports = blank(); keys( this.exports ).forEach( name => { exports[ name ] = true; - }); + } ); keys( this.reexports ).forEach( name => { exports[ name ] = true; - }); + } ); this.exportAllModules.forEach( module => { if ( module.isExternal ) { @@ -334,18 +330,18 @@ export default class Module { module.getExports().forEach( name => { if ( name !== 'default' ) exports[ name ] = true; - }); - }); + } ); + } ); return keys( exports ); } namespace () { - if ( !this.declarations['*'] ) { - this.declarations['*'] = new SyntheticNamespaceDeclaration( this ); + if ( !this.declarations[ '*' ] ) { + this.declarations[ '*' ] = new SyntheticNamespaceDeclaration( this ); } - return this.declarations['*']; + return this.declarations[ '*' ]; } render ( es, legacy ) { @@ -364,8 +360,8 @@ export default class Module { run () { for ( const node of this.ast.body ) { - if ( node.hasEffects( this.scope ) ) { - node.run( this.scope ); + if ( node.hasEffects() ) { + node.run(); } } } @@ -401,7 +397,7 @@ export default class Module { const declaration = otherModule.traceExport( importDeclaration.name ); if ( !declaration ) { - this.error({ + this.error( { code: 'MISSING_EXPORT', message: `'${importDeclaration.name}' is not exported by ${relativeId( otherModule.id )}`, url: `https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module` @@ -416,7 +412,7 @@ export default class Module { traceExport ( name ) { // export * from 'external' - if ( name[0] === '*' ) { + if ( name[ 0 ] === '*' ) { const module = this.bundle.moduleById.get( name.slice( 1 ) ); return module.traceExport( '*' ); } @@ -427,7 +423,7 @@ export default class Module { const declaration = reexportDeclaration.module.traceExport( reexportDeclaration.localName ); if ( !declaration ) { - this.error({ + this.error( { code: 'MISSING_EXPORT', message: `'${reexportDeclaration.localName}' is not exported by ${relativeId( reexportDeclaration.module.id )}`, url: `https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module` @@ -448,7 +444,7 @@ export default class Module { if ( name === 'default' ) return; for ( let i = 0; i < this.exportAllModules.length; i += 1 ) { - const module = this.exportAllModules[i]; + const module = this.exportAllModules[ i ]; const declaration = module.traceExport( name ); if ( declaration ) return declaration; @@ -459,7 +455,7 @@ export default class Module { if ( pos !== undefined ) { warning.pos = pos; - const { line, column } = locate( this.code, pos, { offsetLine: 1 }); // TODO trace sourcemaps + const { line, column } = locate( this.code, pos, { offsetLine: 1 } ); // TODO trace sourcemaps warning.loc = { file: this.id, line, column }; warning.frame = getCodeFrame( this.code, line, column ); diff --git a/src/ast/Node.js b/src/ast/Node.js index 1a9cd8fd6d2..9ce677d6a39 100644 --- a/src/ast/Node.js +++ b/src/ast/Node.js @@ -2,8 +2,8 @@ import { locate } from 'locate-character'; import { UNKNOWN } from './values.js'; export default class Node { - bind ( scope ) { - this.eachChild( child => child.bind( this.scope || scope ) ); + bind () { + this.eachChild( child => child.bind() ); } eachChild ( callback ) { @@ -28,11 +28,6 @@ export default class Node { 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 ); @@ -42,28 +37,41 @@ export default class Node { return UNKNOWN; } - hasEffects ( scope ) { - if ( this.scope ) scope = this.scope; - + hasEffects () { 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 ) ) { + if ( child && child.hasEffects( this.scope ) ) { return true; } } - } else if ( value && value.hasEffects( scope ) ) { + } else if ( value.hasEffects( this.scope ) ) { return true; } } } } - initialise ( scope ) { - this.eachChild( child => child.initialise( this.scope || scope ) ); + initialise ( parentScope ) { + this.initialiseScope( parentScope ); + this.initialiseNode( parentScope ); + this.initialiseChildren( parentScope ); + } + + // Override if e.g. some children need to be initialised with the parent scope + initialiseChildren () { + this.eachChild( child => child.initialise( this.scope ) ); + } + + // Override to perform special initialisation steps after the scope is initialised + initialiseNode () {} + + // Overwrite to create a new scope + initialiseScope ( parentScope ) { + this.scope = parentScope; } insertSemicolon ( code ) { @@ -74,7 +82,7 @@ export default class Node { locate () { // useful for debugging - const location = locate( this.module.code, this.start, { offsetLine: 1 }); + const location = locate( this.module.code, this.start, { offsetLine: 1 } ); location.file = this.module.id; location.toString = () => JSON.stringify( location ); @@ -85,13 +93,11 @@ export default class Node { this.eachChild( child => child.render( code, es ) ); } - run ( scope ) { + run () { if ( this.ran ) return; this.ran = true; - this.eachChild( child => { - child.run( this.scope || scope ); - }); + this.eachChild( child => child.run() ); } toString () { diff --git a/src/ast/nodes/ArrowFunctionExpression.js b/src/ast/nodes/ArrowFunctionExpression.js index 8d9a3d2e878..1cd0307e2a1 100644 --- a/src/ast/nodes/ArrowFunctionExpression.js +++ b/src/ast/nodes/ArrowFunctionExpression.js @@ -1,38 +1,12 @@ -import Node from '../Node.js'; +import Function from './shared/Function.js'; 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' ) { - this.body.createScope( scope ); - this.scope = this.body.scope; - } else { - 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( this.scope ); +export default class ArrowFunctionExpression extends Function { + initialiseScope ( parentScope ) { + this.scope = new Scope( { + parent: parentScope, + isBlockScope: false, + isLexicalBoundary: false + } ); } } diff --git a/src/ast/nodes/AssignmentExpression.js b/src/ast/nodes/AssignmentExpression.js index bbbb7d0ab96..ba846a3cbef 100644 --- a/src/ast/nodes/AssignmentExpression.js +++ b/src/ast/nodes/AssignmentExpression.js @@ -5,14 +5,14 @@ import isProgramLevel from '../utils/isProgramLevel.js'; import { NUMBER, STRING } from '../values.js'; export default class AssignmentExpression extends Node { - bind ( scope ) { + bind () { const subject = this.left; this.subject = subject; - disallowIllegalReassignment( scope, subject ); + disallowIllegalReassignment( this.scope, subject ); if ( subject.type === 'Identifier' ) { - const declaration = scope.findDeclaration( subject.name ); + const declaration = this.scope.findDeclaration( subject.name ); declaration.isReassigned = true; if ( declaration.possibleValues ) { // TODO this feels hacky @@ -26,22 +26,18 @@ export default class AssignmentExpression extends Node { } } - super.bind( scope ); + super.bind(); } - hasEffects ( scope ) { - const hasEffects = this.isUsedByBundle() || this.right.hasEffects( scope ); + hasEffects () { + const hasEffects = this.isUsedByBundle() || this.right.hasEffects( this.scope ); return hasEffects; } - initialise ( scope ) { - this.scope = scope; - + initialiseNode () { if ( isProgramLevel( this ) ) { this.module.bundle.dependentExpressions.push( this ); } - - super.initialise( scope ); } isUsedByBundle () { diff --git a/src/ast/nodes/BinaryExpression.js b/src/ast/nodes/BinaryExpression.js index f4bc5d72aa9..81c0fa5c0f0 100644 --- a/src/ast/nodes/BinaryExpression.js +++ b/src/ast/nodes/BinaryExpression.js @@ -34,7 +34,7 @@ export default class BinaryExpression extends Node { const rightValue = this.right.getValue(); if ( rightValue === UNKNOWN ) return UNKNOWN; - if (!operators[ this.operator ]) return UNKNOWN; + if ( !operators[ this.operator ] ) return UNKNOWN; return operators[ this.operator ]( leftValue, rightValue ); } diff --git a/src/ast/nodes/BlockStatement.js b/src/ast/nodes/BlockStatement.js index d83d321ef65..1c83e4eb9b1 100644 --- a/src/ast/nodes/BlockStatement.js +++ b/src/ast/nodes/BlockStatement.js @@ -1,43 +1,18 @@ import Statement from './shared/Statement.js'; import Scope from '../scopes/Scope.js'; -import extractNames from '../utils/extractNames.js'; export default class BlockStatement extends Statement { bind () { - for ( const node of this.body ) { - node.bind( this.scope ); - } + this.body.forEach( node => node.bind() ); } - createScope ( parent ) { - this.parentIsFunction = /Function/.test( this.parent.type ); - this.isFunctionBlock = this.parentIsFunction || this.parent.type === 'Module'; - - this.scope = new Scope({ - parent, - isBlockScope: !this.isFunctionBlock, - isLexicalBoundary: this.isFunctionBlock && this.parent.type !== 'ArrowFunctionExpression', - 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 ); - }); - }); - } + initialiseAndReplaceScope ( scope ) { + this.scope = scope; + this.initialiseNode(); + this.initialiseChildren( scope ); } - findScope ( functionScope ) { - return functionScope && !this.isFunctionBlock ? this.parent.findScope( functionScope ) : this.scope; - } - - initialise ( scope ) { - if ( !this.scope ) this.createScope( scope ); // scope can be created early in some cases, e.g for (let i... ) - + initialiseChildren () { let lastNode; for ( const node of this.body ) { node.initialise( this.scope ); @@ -47,13 +22,21 @@ export default class BlockStatement extends Statement { } } + initialiseScope ( parentScope ) { + this.scope = new Scope( { + parent: parentScope, + isBlockScope: true, + isLexicalBoundary: false + } ); + } + render ( code, es ) { - if (this.body.length) { + if ( this.body.length ) { for ( const node of this.body ) { node.render( code, es ); } } else { - Statement.prototype.render.call(this, code, es); + Statement.prototype.render.call( this, code, es ); } } } diff --git a/src/ast/nodes/CallExpression.js b/src/ast/nodes/CallExpression.js index d743838d29d..feb736a00a5 100644 --- a/src/ast/nodes/CallExpression.js +++ b/src/ast/nodes/CallExpression.js @@ -3,19 +3,19 @@ import isProgramLevel from '../utils/isProgramLevel.js'; import callHasEffects from './shared/callHasEffects.js'; export default class CallExpression extends Node { - bind ( scope ) { + bind () { if ( this.callee.type === 'Identifier' ) { - const declaration = scope.findDeclaration( this.callee.name ); + const declaration = this.scope.findDeclaration( this.callee.name ); if ( declaration.isNamespace ) { - this.module.error({ + this.module.error( { code: 'CANNOT_CALL_NAMESPACE', message: `Cannot call a namespace ('${this.callee.name}')` }, this.start ); } if ( this.callee.name === 'eval' && declaration.isGlobal ) { - this.module.warn({ + this.module.warn( { code: 'EVAL', message: `Use of eval is strongly discouraged, as it poses security risks and may cause issues with minification`, url: 'https://github.com/rollup/rollup/wiki/Troubleshooting#avoiding-eval' @@ -23,21 +23,20 @@ export default class CallExpression extends Node { } } - super.bind( scope ); + super.bind(); } - hasEffects ( scope ) { - return callHasEffects( scope, this.callee, false ); + hasEffects () { + return callHasEffects( this.scope, this.callee, false ); } - initialise ( scope ) { + initialiseNode () { if ( isProgramLevel( this ) ) { this.module.bundle.dependentExpressions.push( this ); } - super.initialise( scope ); } isUsedByBundle () { - return this.hasEffects( this.findScope() ); + return this.hasEffects(); } } diff --git a/src/ast/nodes/CatchClause.js b/src/ast/nodes/CatchClause.js index e4697ecf635..037e736a920 100644 --- a/src/ast/nodes/CatchClause.js +++ b/src/ast/nodes/CatchClause.js @@ -1,10 +1,21 @@ import Node from '../Node.js'; +import Scope from '../scopes/Scope.js'; +import extractNames from '../utils/extractNames.js'; export default class CatchClause extends Node { - initialise ( scope ) { - this.body.createScope( scope ); - this.scope = this.body.scope; + initialiseChildren () { + if ( this.param ) { + this.param.initialise( this.scope ); + extractNames( this.param ).forEach( name => this.scope.addDeclaration( name, null, false, true ) ); + } + this.body.initialiseAndReplaceScope( this.scope ); + } - this.body.initialise( this.scope ); + initialiseScope ( parentScope ) { + this.scope = new Scope( { + parent: parentScope, + isBlockScope: true, + isLexicalBoundary: false + } ); } -} \ No newline at end of file +} diff --git a/src/ast/nodes/ClassDeclaration.js b/src/ast/nodes/ClassDeclaration.js index 594ee62b17f..2d1b98ffae4 100644 --- a/src/ast/nodes/ClassDeclaration.js +++ b/src/ast/nodes/ClassDeclaration.js @@ -1,40 +1,21 @@ -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; - - if ( this.superClass ) this.superClass.run( this.scope ); - this.body.run(); - } - - addReference () { - /* noop? */ - } +import Class from './shared/Class.js'; +export default class ClassDeclaration extends Class { gatherPossibleValues ( values ) { values.add( this ); } - getName () { - return this.name; - } - hasEffects () { return false; } - initialise ( scope ) { - this.scope = scope; - + initialiseChildren ( parentScope ) { if ( this.id ) { this.name = this.id.name; - scope.addDeclaration( this.name, this, false, false ); + parentScope.addDeclaration( this.name, this, false, false ); + this.id.initialise( parentScope ); } - - super.initialise( scope ); + super.initialiseChildren(parentScope); } render ( code, es ) { @@ -45,9 +26,9 @@ export default class ClassDeclaration extends Node { } } - run ( scope ) { + run () { if ( this.parent.type === 'ExportDefaultDeclaration' ) { - super.run( scope ); + super.run(); } } } diff --git a/src/ast/nodes/ClassExpression.js b/src/ast/nodes/ClassExpression.js index 63f1399663b..e3bd24bdc25 100644 --- a/src/ast/nodes/ClassExpression.js +++ b/src/ast/nodes/ClassExpression.js @@ -1,26 +1,12 @@ -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 ) - }); +import Class from './shared/Class.js'; +export default class ClassExpression extends Class { + initialiseChildren (parentScope) { if ( this.id ) { - // function expression IDs belong to the child scope... - this.scope.addDeclaration( this.id.name, this, false, true ); + this.name = this.id.name; + this.scope.addDeclaration( this.name, this, false, false ); + this.id.initialise( this.scope ); } - - super.initialise( this.scope ); + super.initialiseChildren(parentScope); } } diff --git a/src/ast/nodes/ConditionalExpression.js b/src/ast/nodes/ConditionalExpression.js index b8bb8c9864b..3350736974b 100644 --- a/src/ast/nodes/ConditionalExpression.js +++ b/src/ast/nodes/ConditionalExpression.js @@ -2,25 +2,21 @@ import Node from '../Node.js'; import { UNKNOWN } from '../values.js'; export default class ConditionalExpression extends Node { - initialise ( scope ) { + initialiseChildren ( parentScope ) { if ( this.module.bundle.treeshake ) { this.testValue = this.test.getValue(); - if ( this.testValue === UNKNOWN ) { - super.initialise( scope ); - } - - else if ( this.testValue ) { - this.consequent.initialise( scope ); + if ( this.testValue === UNKNOWN ) { + super.initialiseChildren( parentScope ); + } else if ( this.testValue ) { + this.consequent.initialise( this.scope ); this.alternate = null; } else if ( this.alternate ) { - this.alternate.initialise( scope ); + this.alternate.initialise( this.scope ); this.consequent = null; } - } - - else { - super.initialise( scope ); + } else { + super.initialiseChildren( parentScope ); } } diff --git a/src/ast/nodes/ExportAllDeclaration.js b/src/ast/nodes/ExportAllDeclaration.js index a0f53085dc7..26f3101d4fb 100644 --- a/src/ast/nodes/ExportAllDeclaration.js +++ b/src/ast/nodes/ExportAllDeclaration.js @@ -1,7 +1,7 @@ import Node from '../Node.js'; export default class ExportAllDeclaration extends Node { - initialise () { + initialiseNode () { this.isExportDeclaration = true; } diff --git a/src/ast/nodes/ExportDefaultDeclaration.js b/src/ast/nodes/ExportDefaultDeclaration.js index 945f774bcfa..efe17298911 100644 --- a/src/ast/nodes/ExportDefaultDeclaration.js +++ b/src/ast/nodes/ExportDefaultDeclaration.js @@ -3,16 +3,6 @@ import Node from '../Node.js'; const functionOrClassDeclaration = /^(?:Function|Class)Declaration/; 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; @@ -25,11 +15,11 @@ export default class ExportDefaultDeclaration extends Node { if ( this.original ) this.original.addReference( reference ); } - bind ( scope ) { + bind () { const name = ( this.declaration.id && this.declaration.id.name ) || this.declaration.name; - if ( name ) this.original = scope.findDeclaration( name ); + if ( name ) this.original = this.scope.findDeclaration( name ); - this.declaration.bind( scope ); + this.declaration.bind(); } gatherPossibleValues ( values ) { @@ -44,6 +34,14 @@ export default class ExportDefaultDeclaration extends Node { return this.name; } + initialiseNode () { + this.isExportDeclaration = true; + this.isDefault = true; + + this.name = ( this.declaration.id && this.declaration.id.name ) || this.declaration.name || this.module.basename(); + this.scope.declarations.default = this; + } + // TODO this is total chaos, tidy it up render ( code, es ) { const treeshake = this.module.bundle.treeshake; @@ -53,7 +51,7 @@ export default class ExportDefaultDeclaration extends Node { let declaration_start; if ( this.declaration ) { const statementStr = code.original.slice( this.start, this.end ); - declaration_start = this.start + statementStr.match(/^\s*export\s+default\s*/)[0].length; + declaration_start = this.start + statementStr.match( /^\s*export\s+default\s*/ )[ 0 ].length; } if ( this.shouldInclude || this.declaration.activated ) { @@ -63,7 +61,7 @@ export default class ExportDefaultDeclaration extends Node { code.remove( this.start, declaration_start ); } else { code.overwrite( this.start, declaration_start, `var ${this.name} = ` ); - if ( code.original[this.end - 1] !== ';' ) code.appendLeft( this.end, ';' ); + if ( code.original[ this.end - 1 ] !== ';' ) code.appendLeft( this.end, ';' ); } } @@ -92,7 +90,7 @@ export default class ExportDefaultDeclaration extends Node { const hasEffects = this.declaration.hasEffects( this.module.scope ); code.remove( this.start, hasEffects ? declaration_start : this.next || this.end ); } - } else if (name === this.declaration.name) { + } else if ( name === this.declaration.name ) { code.remove( this.start, this.next || this.end ); } else { code.overwrite( this.start, declaration_start, `${this.module.bundle.varOrConst} ${name} = ` ); @@ -101,9 +99,9 @@ export default class ExportDefaultDeclaration extends Node { } } - run ( scope ) { + run () { this.shouldInclude = true; - super.run( scope ); + super.run(); // special case (TODO is this correct?) if ( functionOrClassDeclaration.test( this.declaration.type ) && !this.declaration.id ) { diff --git a/src/ast/nodes/ExportNamedDeclaration.js b/src/ast/nodes/ExportNamedDeclaration.js index c9e4af11bc7..74d0a104056 100644 --- a/src/ast/nodes/ExportNamedDeclaration.js +++ b/src/ast/nodes/ExportNamedDeclaration.js @@ -1,15 +1,12 @@ import Node from '../Node.js'; export default class ExportNamedDeclaration extends Node { - initialise ( scope ) { - this.scope = scope; - this.isExportDeclaration = true; - - if ( this.declaration ) this.declaration.initialise( scope ); + bind () { + if ( this.declaration ) this.declaration.bind(); } - bind ( scope ) { - if ( this.declaration ) this.declaration.bind( scope ); + initialiseNode () { + this.isExportDeclaration = true; } render ( code, es ) { diff --git a/src/ast/nodes/ForInStatement.js b/src/ast/nodes/ForInStatement.js index 1872b963b21..3f0954b8f5c 100644 --- a/src/ast/nodes/ForInStatement.js +++ b/src/ast/nodes/ForInStatement.js @@ -4,19 +4,20 @@ import Scope from '../scopes/Scope.js'; import { STRING } from '../values.js'; export default class ForInStatement extends Statement { - initialise ( 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 - }); - } - - super.initialise( this.scope ); + initialiseChildren () { + this.left.initialise( this.scope ); + this.right.initialise( this.scope ); + this.body.initialiseAndReplaceScope ? + this.body.initialiseAndReplaceScope( this.scope ) : + this.body.initialise( this.scope ); assignTo( this.left, this.scope, STRING ); } + + initialiseScope ( parentScope ) { + this.scope = new Scope( { + parent: parentScope, + isBlockScope: true, + isLexicalBoundary: false + } ); + } } diff --git a/src/ast/nodes/ForOfStatement.js b/src/ast/nodes/ForOfStatement.js index a5225f847b7..8c745b7103a 100644 --- a/src/ast/nodes/ForOfStatement.js +++ b/src/ast/nodes/ForOfStatement.js @@ -4,19 +4,20 @@ import Scope from '../scopes/Scope.js'; import { UNKNOWN } from '../values.js'; export default class ForOfStatement extends Statement { - initialise ( 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 - }); - } - - super.initialise( this.scope ); + initialiseChildren () { + this.left.initialise( this.scope ); + this.right.initialise( this.scope ); + this.body.initialiseAndReplaceScope ? + this.body.initialiseAndReplaceScope( this.scope ) : + this.body.initialise( this.scope ); assignTo( this.left, this.scope, UNKNOWN ); } + + initialiseScope ( parentScope ) { + this.scope = new Scope( { + parent: parentScope, + isBlockScope: true, + isLexicalBoundary: false + } ); + } } diff --git a/src/ast/nodes/ForStatement.js b/src/ast/nodes/ForStatement.js index 11e98e5c18c..94132dfa65d 100644 --- a/src/ast/nodes/ForStatement.js +++ b/src/ast/nodes/ForStatement.js @@ -2,22 +2,20 @@ import Statement from './shared/Statement.js'; import Scope from '../scopes/Scope.js'; export default class ForStatement extends Statement { - initialise ( 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 + initialiseChildren () { 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 ); + this.body.initialiseAndReplaceScope ? + this.body.initialiseAndReplaceScope( this.scope ) : + this.body.initialise( this.scope ); + } + + initialiseScope ( parentScope ) { + this.scope = new Scope( { + parent: parentScope, + isBlockScope: true, + isLexicalBoundary: false + } ); } } diff --git a/src/ast/nodes/FunctionDeclaration.js b/src/ast/nodes/FunctionDeclaration.js index 660be951a11..2ce79eda407 100644 --- a/src/ast/nodes/FunctionDeclaration.js +++ b/src/ast/nodes/FunctionDeclaration.js @@ -1,24 +1,15 @@ -import Node from '../Node.js'; +import Function from './shared/Function.js'; -export default class FunctionDeclaration extends Node { +export default class FunctionDeclaration extends Function { 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.params.forEach( param => param.run() ); // in case of assignment patterns this.body.run(); } - addReference () { - /* noop? */ - } - - bind ( scope ) { - if ( this.id ) this.id.bind( scope ); - this.params.forEach( param => param.bind( this.body.scope ) ); - this.body.bind( scope ); - } + addReference () {} gatherPossibleValues ( values ) { values.add( this ); @@ -28,21 +19,13 @@ export default class FunctionDeclaration extends Node { return this.name; } - hasEffects () { - return false; - } - - initialise ( scope ) { + initialiseChildren ( parentScope ) { if ( this.id ) { this.name = this.id.name; // may be overridden by bundle.deconflict - scope.addDeclaration( this.name, this, false, false ); - this.id.initialise( scope ); + parentScope.addDeclaration( this.name, this, false, false ); + this.id.initialise( parentScope ); } - - this.body.createScope( scope ); - - this.params.forEach( param => param.initialise( this.body.scope ) ); - this.body.initialise(); + super.initialiseChildren( parentScope ); } render ( code, es ) { @@ -53,9 +36,9 @@ export default class FunctionDeclaration extends Node { } } - run ( scope ) { + run () { if ( this.parent.type === 'ExportDefaultDeclaration' ) { - super.run( scope ); + super.run(); } } } diff --git a/src/ast/nodes/FunctionExpression.js b/src/ast/nodes/FunctionExpression.js index 60f89f3f69a..ea0f32ab12d 100644 --- a/src/ast/nodes/FunctionExpression.js +++ b/src/ast/nodes/FunctionExpression.js @@ -1,43 +1,26 @@ -import Node from '../Node.js'; +import Function from './shared/Function.js'; -export default class FunctionExpression extends Node { +export default class FunctionExpression extends Function { 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.params.forEach( param => param.run() ); // in case of assignment patterns this.body.run(); } - addReference () { - /* noop? */ - } - - bind () { - if ( this.id ) this.id.bind( this.body.scope ); - this.params.forEach( param => param.bind( this.body.scope ) ); - this.body.bind(); - } + addReference () {} getName () { return this.name; } - hasEffects () { - return false; - } - - initialise ( scope ) { - this.name = this.id && this.id.name; // may be overridden by bundle.deconflict - this.body.createScope( scope ); - + initialiseChildren ( parentScope ) { if ( this.id ) { - this.id.initialise( this.body.scope ); - this.body.scope.addDeclaration( this.id.name, this, false, false ); + this.name = this.id.name; // may be overridden by bundle.deconflict + this.scope.addDeclaration( this.name, this, false, false ); + this.id.initialise( this.scope ); } - - this.params.forEach( param => param.initialise( this.body.scope ) ); - this.body.initialise(); + super.initialiseChildren( parentScope ); } } diff --git a/src/ast/nodes/Identifier.js b/src/ast/nodes/Identifier.js index d458f5d5615..b3857b58e37 100644 --- a/src/ast/nodes/Identifier.js +++ b/src/ast/nodes/Identifier.js @@ -1,7 +1,7 @@ -import Node from '../Node.js'; -import isReference from 'is-reference'; +import Node from "../Node.js"; +import isReference from "is-reference"; -function isAssignmentPatternLhs ( node, parent ) { +function isAssignmentPatternLhs (node, parent) { // special case: `({ foo = 42 }) => {...}` // `foo` actually has two different parents, the Property of the // ObjectPattern, and the AssignmentPattern. In one case it's a @@ -9,42 +9,42 @@ function isAssignmentPatternLhs ( node, parent ) { // `({ foo: foo = 42 }) => {...}`. But unlike a regular shorthand // property, the `foo` node appears at different levels of the tree return ( - parent.type === 'Property' && + parent.type === "Property" && parent.shorthand && - parent.value.type === 'AssignmentPattern' && + parent.value.type === "AssignmentPattern" && parent.value.left === node ); } export default class Identifier extends Node { - bind ( scope ) { - if ( isReference( this, this.parent ) || isAssignmentPatternLhs( this, this.parent ) ) { - this.declaration = scope.findDeclaration( this.name ); - this.declaration.addReference( this ); // TODO necessary? + bind () { + if (isReference(this, this.parent) || isAssignmentPatternLhs(this, this.parent)) { + this.declaration = this.scope.findDeclaration(this.name); + this.declaration.addReference(this); // TODO necessary? } } - gatherPossibleValues ( values ) { - if ( isReference( this, this.parent ) ) { - values.add( this ); + 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, { storeName: true, contentOnly: false } ); + render (code, es) { + if (this.declaration) { + const name = this.declaration.getName(es); + if (name !== this.name) { + code.overwrite(this.start, this.end, name, { storeName: true, contentOnly: false }); // special case - if ( this.parent.type === 'Property' && this.parent.shorthand ) { - code.appendLeft( this.start, `${this.name}: ` ); + if (this.parent.type === "Property" && this.parent.shorthand) { + code.appendLeft(this.start, `${this.name}: `); } } } } run () { - if ( this.declaration ) this.declaration.activate(); + if (this.declaration) this.declaration.activate(); } } diff --git a/src/ast/nodes/IfStatement.js b/src/ast/nodes/IfStatement.js index 5bf8c0f6c63..3feedf97ede 100644 --- a/src/ast/nodes/IfStatement.js +++ b/src/ast/nodes/IfStatement.js @@ -3,14 +3,14 @@ import extractNames from '../utils/extractNames.js'; import { UNKNOWN } from '../values.js'; // Statement types which may contain if-statements as direct children. -const statementsWithIfStatements = new Set([ +const statementsWithIfStatements = new Set( [ 'DoWhileStatement', 'ForInStatement', 'ForOfStatement', 'ForStatement', 'IfStatement', 'WhileStatement' -]); +] ); function handleVarDeclarations ( node, scope ) { const hoistedVars = []; @@ -23,8 +23,8 @@ function handleVarDeclarations ( node, scope ) { extractNames( declarator.id ).forEach( name => { if ( !~hoistedVars.indexOf( name ) ) hoistedVars.push( name ); - }); - }); + } ); + } ); } else if ( !/Function/.test( node.type ) ) { @@ -39,32 +39,27 @@ function handleVarDeclarations ( node, scope ) { // TODO DRY this out export default class IfStatement extends Statement { - initialise ( scope ) { - this.scope = scope; - this.testValue = this.test.getValue(); - + initialiseChildren ( parentScope ) { if ( this.module.bundle.treeshake ) { - if ( this.testValue === UNKNOWN ) { - super.initialise( scope ); - } - - else if ( this.testValue ) { - this.consequent.initialise( scope ); - - if ( this.alternate ) this.hoistedVars = handleVarDeclarations( this.alternate, scope ); - this.alternate = null; - } - - else { - if ( this.alternate ) this.alternate.initialise( scope ); + this.testValue = this.test.getValue(); - this.hoistedVars = handleVarDeclarations( this.consequent, scope ); + if ( this.testValue === UNKNOWN ) { + super.initialiseChildren( parentScope ); + } else if ( this.testValue ) { + this.consequent.initialise( this.scope ); + if ( this.alternate ) { + this.hoistedVars = handleVarDeclarations( this.alternate, this.scope ); + this.alternate = null; + } + } else { + if ( this.alternate ) { + this.alternate.initialise( this.scope ); + } + this.hoistedVars = handleVarDeclarations( this.consequent, this.scope ); this.consequent = null; } - } - - else { - super.initialise( scope ); + } else { + super.initialiseChildren( parentScope ); } } @@ -85,7 +80,7 @@ export default class IfStatement extends Statement { .map( name => { const declaration = this.scope.findDeclaration( name ); return declaration.activated ? declaration.getName() : null; - }) + } ) .filter( Boolean ); if ( names.length > 0 ) { diff --git a/src/ast/nodes/ImportDeclaration.js b/src/ast/nodes/ImportDeclaration.js index 0edc68b2111..261aeed2d12 100644 --- a/src/ast/nodes/ImportDeclaration.js +++ b/src/ast/nodes/ImportDeclaration.js @@ -6,7 +6,7 @@ export default class ImportDeclaration extends Node { // TODO do the inter-module binding setup here? } - initialise () { + initialiseNode () { this.isImportDeclaration = true; } diff --git a/src/ast/nodes/Literal.js b/src/ast/nodes/Literal.js index 3b54f14294f..3ba7962a62f 100644 --- a/src/ast/nodes/Literal.js +++ b/src/ast/nodes/Literal.js @@ -11,7 +11,7 @@ export default class Literal extends Node { render ( code ) { if ( typeof this.value === 'string' ) { - code.indentExclusionRanges.push([ this.start + 1, this.end - 1 ]); + code.indentExclusionRanges.push( [ this.start + 1, this.end - 1 ] ); } } } diff --git a/src/ast/nodes/MemberExpression.js b/src/ast/nodes/MemberExpression.js index 5ab2954444d..3716cdc11f3 100644 --- a/src/ast/nodes/MemberExpression.js +++ b/src/ast/nodes/MemberExpression.js @@ -11,7 +11,7 @@ class Keypath { while ( node.type === 'MemberExpression' ) { const prop = node.property; - if ( node.computed ) { + if ( node.computed ) { if ( prop.type !== 'Literal' || typeof prop.value !== 'string' || !validProp.test( prop.value ) ) { this.computed = true; return; @@ -27,23 +27,23 @@ class Keypath { } export default class MemberExpression extends Node { - bind ( scope ) { + bind () { // if this resolves to a namespaced declaration, prepare // to replace it // TODO this code is a bit inefficient const keypath = new Keypath( this ); if ( !keypath.computed && keypath.root.type === 'Identifier' ) { - let declaration = scope.findDeclaration( keypath.root.name ); + let declaration = this.scope.findDeclaration( keypath.root.name ); while ( declaration.isNamespace && keypath.parts.length ) { const exporterId = declaration.module.id; - const part = keypath.parts[0]; + const part = keypath.parts[ 0 ]; declaration = declaration.module.traceExport( part.name || part.value ); if ( !declaration ) { - this.module.warn({ + this.module.warn( { code: 'MISSING_EXPORT', message: `'${part.name || part.value}' is not exported by '${relativeId( exporterId )}'`, url: `https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module` @@ -56,7 +56,7 @@ export default class MemberExpression extends Node { } if ( keypath.parts.length ) { - super.bind( scope ); + super.bind(); return; // not a namespaced declaration } @@ -68,7 +68,7 @@ export default class MemberExpression extends Node { } else { - super.bind( scope ); + super.bind(); } } @@ -89,8 +89,8 @@ export default class MemberExpression extends Node { super.render( code, es ); } - run ( scope ) { + run () { if ( this.declaration ) this.declaration.activate(); - super.run( scope ); + super.run(); } } diff --git a/src/ast/nodes/NewExpression.js b/src/ast/nodes/NewExpression.js index d8fc3b159f4..699a393dddf 100644 --- a/src/ast/nodes/NewExpression.js +++ b/src/ast/nodes/NewExpression.js @@ -2,7 +2,7 @@ import Node from '../Node.js'; import callHasEffects from './shared/callHasEffects.js'; export default class NewExpression extends Node { - hasEffects ( scope ) { - return callHasEffects( scope, this.callee, true ); + hasEffects () { + return callHasEffects( this.scope, this.callee, true ); } } diff --git a/src/ast/nodes/ReturnStatement.js b/src/ast/nodes/ReturnStatement.js deleted file mode 100644 index bff5ae126d6..00000000000 --- a/src/ast/nodes/ReturnStatement.js +++ /dev/null @@ -1,7 +0,0 @@ -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 index b25ef48c86a..0b24cdecc65 100644 --- a/src/ast/nodes/TemplateLiteral.js +++ b/src/ast/nodes/TemplateLiteral.js @@ -2,7 +2,7 @@ import Node from '../Node.js'; export default class TemplateLiteral extends Node { render ( code, es ) { - code.indentExclusionRanges.push([ this.start, this.end ]); + code.indentExclusionRanges.push( [ this.start, this.end ] ); super.render( code, es ); } } diff --git a/src/ast/nodes/ThisExpression.js b/src/ast/nodes/ThisExpression.js index 235a372008b..ece9a32f853 100644 --- a/src/ast/nodes/ThisExpression.js +++ b/src/ast/nodes/ThisExpression.js @@ -1,13 +1,13 @@ import Node from '../Node.js'; export default class ThisExpression extends Node { - initialise ( scope ) { - const lexicalBoundary = scope.findLexicalBoundary(); + initialiseNode () { + const lexicalBoundary = this.scope.findLexicalBoundary(); if ( lexicalBoundary.isModuleScope ) { this.alias = this.module.context; if ( this.alias === 'undefined' ) { - this.module.warn({ + this.module.warn( { code: 'THIS_IS_UNDEFINED', message: `The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten`, url: `https://github.com/rollup/rollup/wiki/Troubleshooting#this-is-undefined` diff --git a/src/ast/nodes/UnaryExpression.js b/src/ast/nodes/UnaryExpression.js index 9be1c2c3689..9193157e796 100644 --- a/src/ast/nodes/UnaryExpression.js +++ b/src/ast/nodes/UnaryExpression.js @@ -2,18 +2,18 @@ import Node from '../Node.js'; import { UNKNOWN } from '../values.js'; const operators = { - "-": value => -value, - "+": value => +value, - "!": value => !value, - "~": value => ~value, + '-': 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 ); + bind () { + if ( this.value === UNKNOWN ) super.bind(); } getValue () { @@ -23,12 +23,11 @@ export default class UnaryExpression extends Node { return operators[ this.operator ]( argumentValue ); } - hasEffects ( scope ) { - return this.operator === 'delete' || this.argument.hasEffects( scope ); + hasEffects () { + return this.operator === 'delete' || this.argument.hasEffects(); } - initialise ( scope ) { + initialiseNode () { 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 index 9fcea9dc50f..2b77db9a417 100644 --- a/src/ast/nodes/UpdateExpression.js +++ b/src/ast/nodes/UpdateExpression.js @@ -4,14 +4,14 @@ import isUsedByBundle from './shared/isUsedByBundle.js'; import { NUMBER } from '../values.js'; export default class UpdateExpression extends Node { - bind ( scope ) { + bind () { const subject = this.argument; this.subject = subject; - disallowIllegalReassignment( scope, this.argument ); + disallowIllegalReassignment( this.scope, this.argument ); if ( subject.type === 'Identifier' ) { - const declaration = scope.findDeclaration( subject.name ); + const declaration = this.scope.findDeclaration( subject.name ); declaration.isReassigned = true; if ( declaration.possibleValues ) { @@ -19,18 +19,15 @@ export default class UpdateExpression extends Node { } } - super.bind( scope ); + super.bind(); } - hasEffects ( scope ) { - return isUsedByBundle( scope, this.subject ); + hasEffects () { + return isUsedByBundle( this.scope, this.subject ); } - initialise ( scope ) { - this.scope = scope; - + initialiseNode () { this.module.bundle.dependentExpressions.push( this ); - super.initialise( scope ); } isUsedByBundle () { diff --git a/src/ast/nodes/VariableDeclaration.js b/src/ast/nodes/VariableDeclaration.js index 77f4433d67d..3726705a43b 100644 --- a/src/ast/nodes/VariableDeclaration.js +++ b/src/ast/nodes/VariableDeclaration.js @@ -1,15 +1,15 @@ -import Node from '../Node.js'; -import extractNames from '../utils/extractNames.js'; +import Node from "../Node.js"; +import extractNames from "../utils/extractNames.js"; -function getSeparator ( code, start ) { +function getSeparator (code, start) { let c = start; - while ( c > 0 && code[ c - 1 ] !== '\n' ) { + while (c > 0 && code[c - 1] !== "\n") { c -= 1; - if ( code[c] === ';' || code[c] === '{' ) return '; '; + if (code[c] === ";" || code[c] === "{") return "; "; } - const lineStart = code.slice( c, start ).match( /^\s*/ )[0]; + const lineStart = code.slice(c, start).match(/^\s*/)[0]; return `;\n${lineStart}`; } @@ -17,90 +17,83 @@ function getSeparator ( code, start ) { const forStatement = /^For(?:Of|In)?Statement/; export default class VariableDeclaration extends Node { - initialise ( scope ) { - this.scope = scope; - super.initialise( scope ); - } - - render ( code, es ) { + render (code, es) { const treeshake = this.module.bundle.treeshake; 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 ); + separator = getSeparator(this.module.code, this.start); } let c = this.start; let empty = true; - for ( let i = 0; i < this.declarations.length; i += 1 ) { + for (let i = 0; i < this.declarations.length; i += 1) { const declarator = this.declarations[i]; - const prefix = empty ? '' : separator; // TODO indentation + const prefix = empty ? "" : separator; // TODO indentation - if ( declarator.id.type === 'Identifier' ) { - const proxy = declarator.proxies.get( declarator.id.name ); + 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 ) { - if ( shouldSeparate ) code.overwrite( c, declarator.start, prefix ); + if (isExportedAndReassigned) { + if (declarator.init) { + if (shouldSeparate) code.overwrite(c, declarator.start, prefix); c = declarator.end; empty = false; } - } else if ( !treeshake || proxy.activated ) { - if ( shouldSeparate ) code.overwrite( c, declarator.start, `${prefix}${this.kind} ` ); // TODO indentation + } else if (!treeshake || proxy.activated) { + if (shouldSeparate) code.overwrite(c, declarator.start, `${prefix}${this.kind} `); // TODO indentation c = declarator.end; empty = false; } - } - - else { + } else { const exportAssignments = []; let activated = false; - extractNames( declarator.id ).forEach( name => { - const proxy = declarator.proxies.get( name ); + extractNames(declarator.id).forEach(name => { + const proxy = declarator.proxies.get(name); const isExportedAndReassigned = !es && proxy.exportName && proxy.isReassigned; - if ( isExportedAndReassigned ) { + if (isExportedAndReassigned) { // code.overwrite( c, declarator.start, prefix ); // c = declarator.end; // empty = false; - exportAssignments.push( 'TODO' ); - } else if ( declarator.activated ) { + exportAssignments.push("TODO"); + } else if (declarator.activated) { activated = true; } }); - if ( !treeshake || activated ) { - if ( shouldSeparate ) code.overwrite( c, declarator.start, `${prefix}${this.kind} ` ); // TODO indentation + if (!treeshake || activated) { + if (shouldSeparate) code.overwrite(c, declarator.start, `${prefix}${this.kind} `); // TODO indentation c = declarator.end; empty = false; } - if ( exportAssignments.length ) { - throw new Error( 'TODO' ); + if (exportAssignments.length) { + throw new Error("TODO"); } } - declarator.render( code, es ); + declarator.render(code, es); } - if ( treeshake && empty ) { - code.remove( this.leadingCommentStart || this.start, this.next || this.end ); + if (treeshake && empty) { + code.remove(this.leadingCommentStart || this.start, this.next || this.end); } else { // always include a semi-colon (https://github.com/rollup/rollup/pull/1013), // unless it's a var declaration in a loop head - const needsSemicolon = !forStatement.test( this.parent.type ); + const needsSemicolon = !forStatement.test(this.parent.type); - if ( this.end > c ) { - code.overwrite( c, this.end, needsSemicolon ? ';' : '' ); - } else if ( needsSemicolon ) { - this.insertSemicolon( code ); + if (this.end > c) { + code.overwrite(c, this.end, needsSemicolon ? ";" : ""); + } else if (needsSemicolon) { + this.insertSemicolon(code); } } } diff --git a/src/ast/nodes/VariableDeclarator.js b/src/ast/nodes/VariableDeclarator.js index 753d38402b2..f3225ccb257 100644 --- a/src/ast/nodes/VariableDeclarator.js +++ b/src/ast/nodes/VariableDeclarator.js @@ -47,7 +47,7 @@ export default class VariableDeclarator extends Node { if ( this.activated ) return; this.activated = true; - this.run( this.findScope() ); + this.run(); // if declaration is inside a block, ensure that the block // is marked for inclusion @@ -60,27 +60,22 @@ export default class VariableDeclarator extends Node { } } - hasEffects ( scope ) { - return this.init && this.init.hasEffects( scope ); + hasEffects () { + return this.init && this.init.hasEffects(); } - initialise ( scope ) { + initialiseNode () { this.proxies = new Map(); - - const lexicalBoundary = scope.findLexicalBoundary(); - - const init = this.init ? - ( this.id.type === 'Identifier' ? this.init : UNKNOWN ) : // TODO maybe UNKNOWN is unnecessary + const lexicalBoundary = this.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 ); + this.scope.addDeclaration( name, proxy, this.parent.kind === 'var' ); + } ); } render ( code, es ) { @@ -94,7 +89,7 @@ export default class VariableDeclarator extends Node { code.remove( this.start, this.end ); } } - }); + } ); super.render( code, es ); } diff --git a/src/ast/nodes/index.js b/src/ast/nodes/index.js index bfda415448a..6cfc0b43c13 100644 --- a/src/ast/nodes/index.js +++ b/src/ast/nodes/index.js @@ -26,7 +26,6 @@ import LogicalExpression from './LogicalExpression.js'; import MemberExpression from './MemberExpression.js'; import NewExpression from './NewExpression.js'; import ObjectExpression from './ObjectExpression.js'; -import ReturnStatement from './ReturnStatement.js'; import Statement from './shared/Statement.js'; import TemplateLiteral from './TemplateLiteral.js'; import ThisExpression from './ThisExpression.js'; @@ -66,7 +65,7 @@ export default { MemberExpression, NewExpression, ObjectExpression, - ReturnStatement, + ReturnStatement: Statement, SwitchStatement: Statement, TemplateLiteral, ThisExpression, diff --git a/src/ast/nodes/shared/Class.js b/src/ast/nodes/shared/Class.js new file mode 100644 index 00000000000..fa550396b5c --- /dev/null +++ b/src/ast/nodes/shared/Class.js @@ -0,0 +1,32 @@ +import Node from '../../Node.js'; +import Scope from '../../scopes/Scope.js'; + +export default class ClassExpression extends Node { + activate () { + if ( this.activated ) return; + this.activated = true; + + if ( this.superClass ) this.superClass.run(); + this.body.run(); + } + + addReference () {} + + getName () { + return this.name; + } + + initialiseChildren () { + if ( this.superClass ) { + this.superClass.initialise( this.scope ); + } + this.body.initialise( this.scope ); + } + + initialiseScope ( parentScope ) { + this.scope = new Scope( { + parent: parentScope, + isBlockScope: true + } ); + } +} diff --git a/src/ast/nodes/shared/Function.js b/src/ast/nodes/shared/Function.js new file mode 100644 index 00000000000..21dae785507 --- /dev/null +++ b/src/ast/nodes/shared/Function.js @@ -0,0 +1,33 @@ +import Node from '../../Node.js'; +import Scope from '../../scopes/Scope.js'; +import extractNames from '../../utils/extractNames.js'; + +export default class Function extends Node { + bind () { + if ( this.id ) this.id.bind(); + this.params.forEach( param => param.bind() ); + this.body.bind(); + } + + hasEffects () { + return false; + } + + initialiseChildren () { + this.params.forEach( param => { + param.initialise( this.scope ); + extractNames( param ).forEach( name => this.scope.addDeclaration( name, null, false, true ) ); + } ); + this.body.initialiseAndReplaceScope ? + this.body.initialiseAndReplaceScope( this.scope ) : + this.body.initialise( this.scope ); + } + + initialiseScope ( parentScope ) { + this.scope = new Scope( { + parent: parentScope, + isBlockScope: false, + isLexicalBoundary: true + } ); + } +} diff --git a/src/ast/nodes/shared/Statement.js b/src/ast/nodes/shared/Statement.js index 5667652071c..5a18476ec2b 100644 --- a/src/ast/nodes/shared/Statement.js +++ b/src/ast/nodes/shared/Statement.js @@ -9,8 +9,8 @@ export default class Statement extends Node { } } - run ( scope ) { + run () { this.shouldInclude = true; - super.run( scope ); + super.run(); } } From 6c80cbb538a689219d062986bd0f43fbcd24a8ff Mon Sep 17 00:00:00 2001 From: Lukas Taegert Date: Mon, 7 Aug 2017 07:52:02 +0200 Subject: [PATCH 2/2] Remove a few scope parameters previously missed --- src/ast/Node.js | 4 ++-- src/ast/nodes/AssignmentExpression.js | 2 +- src/ast/nodes/ClassDeclaration.js | 2 +- src/ast/nodes/ExportDefaultDeclaration.js | 2 +- src/ast/nodes/shared/callHasEffects.js | 13 ++++++------- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/ast/Node.js b/src/ast/Node.js index 9ce677d6a39..b9e1f405c2a 100644 --- a/src/ast/Node.js +++ b/src/ast/Node.js @@ -44,11 +44,11 @@ export default class Node { if ( value ) { if ( 'length' in value ) { for ( const child of value ) { - if ( child && child.hasEffects( this.scope ) ) { + if ( child && child.hasEffects() ) { return true; } } - } else if ( value.hasEffects( this.scope ) ) { + } else if ( value.hasEffects() ) { return true; } } diff --git a/src/ast/nodes/AssignmentExpression.js b/src/ast/nodes/AssignmentExpression.js index ba846a3cbef..6a9ef91d7a2 100644 --- a/src/ast/nodes/AssignmentExpression.js +++ b/src/ast/nodes/AssignmentExpression.js @@ -30,7 +30,7 @@ export default class AssignmentExpression extends Node { } hasEffects () { - const hasEffects = this.isUsedByBundle() || this.right.hasEffects( this.scope ); + const hasEffects = this.isUsedByBundle() || this.right.hasEffects(); return hasEffects; } diff --git a/src/ast/nodes/ClassDeclaration.js b/src/ast/nodes/ClassDeclaration.js index 2d1b98ffae4..6d9863273df 100644 --- a/src/ast/nodes/ClassDeclaration.js +++ b/src/ast/nodes/ClassDeclaration.js @@ -15,7 +15,7 @@ export default class ClassDeclaration extends Class { parentScope.addDeclaration( this.name, this, false, false ); this.id.initialise( parentScope ); } - super.initialiseChildren(parentScope); + super.initialiseChildren( parentScope ); } render ( code, es ) { diff --git a/src/ast/nodes/ExportDefaultDeclaration.js b/src/ast/nodes/ExportDefaultDeclaration.js index efe17298911..433b9b25139 100644 --- a/src/ast/nodes/ExportDefaultDeclaration.js +++ b/src/ast/nodes/ExportDefaultDeclaration.js @@ -87,7 +87,7 @@ export default class ExportDefaultDeclaration extends Node { if ( functionOrClassDeclaration.test( this.declaration.type ) ) { code.remove( this.leadingCommentStart || this.start, this.next || this.end ); } else { - const hasEffects = this.declaration.hasEffects( this.module.scope ); + const hasEffects = this.declaration.hasEffects(); code.remove( this.start, hasEffects ? declaration_start : this.next || this.end ); } } else if ( name === this.declaration.name ) { diff --git a/src/ast/nodes/shared/callHasEffects.js b/src/ast/nodes/shared/callHasEffects.js index 09d58ccfce9..3fc7e6bbccd 100644 --- a/src/ast/nodes/shared/callHasEffects.js +++ b/src/ast/nodes/shared/callHasEffects.js @@ -9,21 +9,21 @@ function isES5Function ( node ) { return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration'; } -function hasEffectsNew ( node, scope ) { +function hasEffectsNew ( node ) { let inner = node; if ( inner.type === 'ExpressionStatement' ) { inner = inner.expression; if ( inner.type === 'AssignmentExpression' ) { - if ( inner.right.hasEffects( scope ) ) { + if ( inner.right.hasEffects() ) { return true; } else { inner = inner.left; if ( inner.type === 'MemberExpression' ) { - if ( inner.computed && inner.property.hasEffects( scope ) ) { + if ( inner.computed && inner.property.hasEffects() ) { return true; } else { @@ -38,7 +38,7 @@ function hasEffectsNew ( node, scope ) { } } - return node.hasEffects( scope ); + return node.hasEffects(); } function fnHasEffects ( fn, isNew ) { @@ -46,11 +46,10 @@ function fnHasEffects ( fn, isNew ) { currentlyCalling.add( fn ); // handle body-less arrow functions - const scope = fn.body.scope || fn.scope; const body = fn.body.type === 'BlockStatement' ? fn.body.body : [ fn.body ]; for ( const node of body ) { - if ( isNew ? hasEffectsNew( node, scope ) : node.hasEffects( scope ) ) { + if ( isNew ? hasEffectsNew( node ) : node.hasEffects() ) { currentlyCalling.delete( fn ); return true; } @@ -61,7 +60,7 @@ function fnHasEffects ( fn, isNew ) { } export default function callHasEffects ( scope, callee, isNew ) { - const values = new Set([ callee ]); + const values = new Set( [ callee ] ); for ( const node of values ) { if ( node === UNKNOWN ) return true; // err on side of caution