|
@ -1,4 +1,5 @@ |
|
|
import Statement from './shared/Statement.js'; |
|
|
import Statement from './shared/Statement.js'; |
|
|
|
|
|
import extractNames from '../utils/extractNames.js'; |
|
|
import { UNKNOWN } from '../values.js'; |
|
|
import { UNKNOWN } from '../values.js'; |
|
|
|
|
|
|
|
|
// Statement types which may contain if-statements as direct children.
|
|
|
// Statement types which may contain if-statements as direct children.
|
|
@ -11,9 +12,34 @@ const statementsWithIfStatements = new Set([ |
|
|
'WhileStatement' |
|
|
'WhileStatement' |
|
|
]); |
|
|
]); |
|
|
|
|
|
|
|
|
|
|
|
function handleVarDeclarations ( node, scope ) { |
|
|
|
|
|
const hoistedVars = []; |
|
|
|
|
|
|
|
|
|
|
|
function visit ( node ) { |
|
|
|
|
|
if ( node.type === 'VariableDeclaration' && node.kind === 'var' ) { |
|
|
|
|
|
node.initialise( scope ); |
|
|
|
|
|
|
|
|
|
|
|
node.declarations.forEach( declarator => { |
|
|
|
|
|
extractNames( declarator.id ).forEach( name => { |
|
|
|
|
|
if ( !~hoistedVars.indexOf( name ) ) hoistedVars.push( name ); |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
else if ( !/Function/.test( node.type ) ) { |
|
|
|
|
|
node.eachChild( visit ); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
visit( node ); |
|
|
|
|
|
|
|
|
|
|
|
return hoistedVars; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// TODO DRY this out
|
|
|
// TODO DRY this out
|
|
|
export default class IfStatement extends Statement { |
|
|
export default class IfStatement extends Statement { |
|
|
initialise ( scope ) { |
|
|
initialise ( scope ) { |
|
|
|
|
|
this.scope = scope; |
|
|
this.testValue = this.test.getValue(); |
|
|
this.testValue = this.test.getValue(); |
|
|
|
|
|
|
|
|
if ( this.module.bundle.treeshake ) { |
|
|
if ( this.module.bundle.treeshake ) { |
|
@ -23,11 +49,15 @@ export default class IfStatement extends Statement { |
|
|
|
|
|
|
|
|
else if ( this.testValue ) { |
|
|
else if ( this.testValue ) { |
|
|
this.consequent.initialise( scope ); |
|
|
this.consequent.initialise( scope ); |
|
|
|
|
|
|
|
|
|
|
|
if ( this.alternate ) this.hoistedVars = handleVarDeclarations( this.alternate, scope ); |
|
|
this.alternate = null; |
|
|
this.alternate = null; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
else { |
|
|
else { |
|
|
if ( this.alternate ) this.alternate.initialise( scope ); |
|
|
if ( this.alternate ) this.alternate.initialise( scope ); |
|
|
|
|
|
|
|
|
|
|
|
this.hoistedVars = handleVarDeclarations( this.consequent, scope ); |
|
|
this.consequent = null; |
|
|
this.consequent = null; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -49,6 +79,19 @@ export default class IfStatement extends Statement { |
|
|
// TODO if no block-scoped declarations, remove enclosing
|
|
|
// TODO if no block-scoped declarations, remove enclosing
|
|
|
// curlies and dedent block (if there is a block)
|
|
|
// curlies and dedent block (if there is a block)
|
|
|
|
|
|
|
|
|
|
|
|
if ( this.hoistedVars ) { |
|
|
|
|
|
const names = this.hoistedVars |
|
|
|
|
|
.map( name => { |
|
|
|
|
|
const declaration = this.scope.findDeclaration( name ); |
|
|
|
|
|
return declaration.activated ? declaration.getName() : null; |
|
|
|
|
|
}) |
|
|
|
|
|
.filter( Boolean ); |
|
|
|
|
|
|
|
|
|
|
|
if ( names.length > 0 ) { |
|
|
|
|
|
code.insertLeft( this.start, `var ${names.join( ', ' )};\n\n` ); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if ( this.testValue ) { |
|
|
if ( this.testValue ) { |
|
|
code.remove( this.start, this.consequent.start ); |
|
|
code.remove( this.start, this.consequent.start ); |
|
|
code.remove( this.consequent.end, this.end ); |
|
|
code.remove( this.consequent.end, this.end ); |
|
|