Comparing version 0.4.5 to 0.4.6
# butternut changelog | ||
## 0.4.6 | ||
* Too many fixes to list individually! | ||
## 0.4.5 | ||
@@ -4,0 +8,0 @@ |
{ | ||
"name": "butternut", | ||
"version": "0.4.5", | ||
"version": "0.4.6", | ||
"description": "Experimental ES2015-aware minifier", | ||
@@ -95,3 +95,3 @@ "main": "dist/butternut.cjs.js", | ||
"chalk": "^1.1.3", | ||
"magic-string": "^0.19.0", | ||
"magic-string": "^0.20.0", | ||
"minimist": "^1.2.0", | ||
@@ -98,0 +98,0 @@ "sourcemap-codec": "^1.3.1" |
@@ -53,3 +53,3 @@ import Node from './Node.js'; | ||
export default class BlockStatement extends Node { | ||
attachScope ( parent ) { | ||
attachScope ( program, parent ) { | ||
this.parentIsFunction = /Function/.test( this.parent.type ); | ||
@@ -67,3 +67,3 @@ | ||
for ( let i = 0; i < this.body.length; i += 1 ) { | ||
this.body[i].attachScope( this.scope ); | ||
this.body[i].attachScope( program, this.scope ); | ||
} | ||
@@ -115,3 +115,3 @@ } | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
let executionIsBroken = false; | ||
@@ -130,3 +130,3 @@ let maybeReturnNode; | ||
hasDeclarationsAfterBreak = true; | ||
node.initialise( this.scope || scope ); | ||
node.initialise( program, this.scope || scope ); | ||
} | ||
@@ -140,3 +140,3 @@ | ||
node.initialise( this.scope || scope ); | ||
node.initialise( program, this.scope || scope ); | ||
@@ -175,4 +175,6 @@ if ( canCollapseReturns ) { | ||
minify ( code ) { | ||
if ( this.scope ) this.scope.mangle( code ); | ||
minify ( code, chars ) { | ||
if ( this.scope ) { | ||
this.scope.mangle( code, chars ); | ||
} | ||
@@ -230,3 +232,3 @@ let insertedVarDeclaration = ''; | ||
statement.minify( code ); | ||
statement.minify( code, chars ); | ||
@@ -308,5 +310,5 @@ if ( !statement.collapsed ) { | ||
// statements.forEach( statement => { | ||
// statement.minify( code ); | ||
// statement.minify( code, chars ); | ||
// }); | ||
// } | ||
} |
@@ -19,3 +19,3 @@ import { UNKNOWN } from '../utils/sentinels.js'; | ||
attachScope ( scope ) { | ||
attachScope ( program, scope ) { | ||
for ( var key of this.keys ) { | ||
@@ -28,6 +28,6 @@ const value = this[ key ]; | ||
while ( i-- ) { | ||
if ( value[i] ) value[i].attachScope( scope ); | ||
if ( value[i] ) value[i].attachScope( program, scope ); | ||
} | ||
} else { | ||
value.attachScope( scope ); | ||
value.attachScope( program, scope ); | ||
} | ||
@@ -71,3 +71,3 @@ } | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
this.skip = false; | ||
@@ -82,6 +82,6 @@ | ||
while ( i-- ) { | ||
if ( value[i] ) value[i].initialise( scope ); | ||
if ( value[i] ) value[i].initialise( program, scope ); | ||
} | ||
} else { | ||
value.initialise( scope ); | ||
value.initialise( program, scope ); | ||
} | ||
@@ -117,3 +117,3 @@ } | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
for ( var key of this.keys ) { | ||
@@ -126,6 +126,6 @@ const value = this[ key ]; | ||
while ( i-- ) { | ||
if ( value[i] ) value[i].minify( code ); | ||
if ( value[i] ) value[i].minify( code, chars ); | ||
} | ||
} else { | ||
value.minify( code ); | ||
value.minify( code, chars ); | ||
} | ||
@@ -132,0 +132,0 @@ } |
@@ -7,2 +7,10 @@ import MagicString from 'magic-string'; | ||
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$0123456789'.split(''); | ||
const digit = /\d/; | ||
const naturalOrder = {}; | ||
chars.forEach( ( char, i ) => { | ||
naturalOrder[char] = i; | ||
}); | ||
export default function Program ( source, ast, options, stats ) { | ||
@@ -34,10 +42,21 @@ this.options = options; | ||
this.body.body.forEach( node => { | ||
node.attachScope( this.body.scope ); | ||
node.attachScope( this, this.body.scope ); | ||
}); | ||
this.body.initialise( this.body.scope ); | ||
this.charFrequency = {}; | ||
chars.forEach( char => { | ||
this.charFrequency[char] = 0; | ||
}); | ||
this.body.initialise( this, this.body.scope ); | ||
if ( DEBUG ) stats.timeEnd( 'init body' ); | ||
chars.sort( ( a, b ) => { | ||
if ( digit.test( a ) && !digit.test( b ) ) return 1; | ||
if ( digit.test( b ) && !digit.test( a ) ) return -1; | ||
return ( this.charFrequency[b] - this.charFrequency[a] ) || ( naturalOrder[a] - naturalOrder[b] ); | ||
}); | ||
if ( DEBUG ) stats.time( 'minify' ); | ||
this.body.minify( this.magicString ); | ||
this.body.minify( this.magicString, chars ); | ||
if ( DEBUG ) stats.timeEnd( 'minify' ); | ||
@@ -47,2 +66,8 @@ } | ||
Program.prototype = { | ||
addWord ( word ) { | ||
for ( let i = 0; i < word.length; i += 1 ) { | ||
this.charFrequency[word[i]] += 1; | ||
} | ||
}, | ||
export ( options ) { | ||
@@ -49,0 +74,0 @@ const stats = this.stats; |
@@ -1,2 +0,2 @@ | ||
import reserved from '../utils/reserved.js'; | ||
import { reserved } from '../utils/reserved.js'; | ||
import CompileError from '../utils/CompileError.js'; | ||
@@ -6,4 +6,2 @@ | ||
const validChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$'; | ||
export default function Scope ( options ) { | ||
@@ -40,7 +38,2 @@ options = options || {}; | ||
Scope.prototype = { | ||
addAlias ( alias ) { | ||
this.aliases[ alias ] = true; | ||
if ( this.parent ) this.parent.addAlias( alias ); | ||
}, | ||
addDeclaration ( identifier, kind ) { | ||
@@ -65,3 +58,3 @@ if ( kind === 'var' && this.isBlockScope ) { | ||
// to keep function names — https://github.com/Rich-Harris/butternut/issues/17) | ||
if ( existingDeclaration.node.parent.type === 'FunctionExpression' ) { | ||
if ( existingDeclaration.kind === 'FunctionExpression' ) { | ||
existingDeclaration.node.parent.shadowed = true; | ||
@@ -134,28 +127,2 @@ } | ||
containsAlias ( alias ) { | ||
return this.aliases[ alias ] || ( this.parent && this.parent.containsAlias( alias ) ); | ||
}, | ||
createIdentifier ( used ) { | ||
let alias; | ||
do { | ||
alias = this.idCounter.map( i => validChars[i] ).join( '' ); | ||
let i = this.idCounter.length; | ||
while ( i-- ) { | ||
this.idCounter[i] += 1; | ||
if ( this.idCounter[i] === validChars.length ) { | ||
this.idCounter[i] = 0; | ||
if ( i === 0 ) this.idCounter.push( 0 ); | ||
} else { | ||
break; | ||
} | ||
} | ||
} while ( used[ alias ] || reserved[ alias ] ); | ||
return alias; | ||
}, | ||
deopt () { | ||
@@ -179,6 +146,9 @@ if ( !this.deopted ) { | ||
mangle ( code ) { | ||
mangle ( code, chars ) { | ||
if ( !this.canMangle ) return; | ||
let used = Object.create( null ); | ||
reserved.forEach( word => { | ||
used[ word ] = true; | ||
}); | ||
@@ -190,2 +160,16 @@ Object.keys( this.references ).forEach( reference => { | ||
let i = -1; | ||
function getNextAlias () { | ||
let alias; | ||
do { | ||
i += 1; | ||
alias = getAlias( chars, i ); | ||
} while ( alias in used ); | ||
return alias; | ||
} | ||
// TODO sort declarations by number of instances? | ||
Object.keys( this.declarations ).forEach( name => { | ||
@@ -200,3 +184,3 @@ const declaration = this.declarations[ name ]; | ||
declaration.alias = this.createIdentifier( used ); | ||
declaration.alias = getNextAlias(); | ||
@@ -213,1 +197,17 @@ declaration.instances.forEach( instance => { | ||
}; | ||
// adapted from https://github.com/mishoo/UglifyJS2/blob/master/lib/scope.js | ||
function getAlias ( chars, i ) { | ||
let alias = ''; | ||
let base = 54; | ||
i++; | ||
do { | ||
i--; | ||
alias += chars[ i % base ]; | ||
i = Math.floor( i / base ); | ||
base = 64; | ||
} while ( i > 0 ); | ||
return alias; | ||
} |
@@ -6,3 +6,3 @@ import Node from '../Node.js'; | ||
export default class ArrowFunctionExpression extends Node { | ||
attachScope ( parent ) { | ||
attachScope ( program, parent ) { | ||
this.scope = new Scope({ | ||
@@ -14,3 +14,3 @@ block: false, | ||
this.params.forEach( param => { | ||
param.attachScope( this.scope ); | ||
param.attachScope( program, this.scope ); | ||
@@ -25,6 +25,6 @@ extractNames( param ).forEach( node => { | ||
this.body.body.forEach( node => { | ||
node.attachScope( this.scope ); | ||
node.attachScope( program, this.scope ); | ||
}); | ||
} else { | ||
this.body.attachScope( this.scope ); | ||
this.body.attachScope( program, this.scope ); | ||
} | ||
@@ -34,4 +34,4 @@ | ||
initialise () { | ||
super.initialise( this.scope ); | ||
initialise ( program ) { | ||
super.initialise( program, this.scope ); | ||
} | ||
@@ -47,4 +47,4 @@ | ||
minify ( code ) { | ||
this.scope.mangle( code ); | ||
minify ( code, chars ) { | ||
this.scope.mangle( code, chars ); | ||
@@ -61,3 +61,3 @@ let c = this.start; | ||
else if ( this.params.length === 1 ) { | ||
this.params[0].minify( code ); | ||
this.params[0].minify( code, chars ); | ||
@@ -88,3 +88,3 @@ if ( this.params[0].type === 'Identifier' ) { | ||
this.params.forEach( ( param, i ) => { | ||
param.minify( code ); | ||
param.minify( code, chars ); | ||
if ( param.start > c + 1 ) code.overwrite( c, param.start, i ? ',' : '(' ); | ||
@@ -99,4 +99,4 @@ c = param.end; | ||
this.body.minify( code ); | ||
this.body.minify( code, chars ); | ||
} | ||
} |
@@ -20,3 +20,3 @@ import Node from '../Node.js'; | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
if ( this.left.type === 'Identifier' ) { | ||
@@ -29,6 +29,6 @@ const declaration = scope.findDeclaration( this.left.name ); | ||
super.initialise( scope ); | ||
super.initialise( program, scope ); | ||
} | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
if ( this.right.start > this.left.end + this.operator.length ) { | ||
@@ -44,3 +44,3 @@ code.overwrite( this.left.end, this.right.start, this.operator ); | ||
this.right.right.minify( code ); | ||
this.right.right.minify( code, chars ); | ||
return; | ||
@@ -54,3 +54,3 @@ } | ||
this.right.left.minify( code ); | ||
this.right.left.minify( code, chars ); | ||
return; | ||
@@ -60,4 +60,4 @@ } | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
} |
@@ -59,2 +59,3 @@ import Node from '../Node.js'; | ||
// TODO `program.addWord( stringify( this.getValue() ) )`... | ||
getValue () { | ||
@@ -69,3 +70,3 @@ const left = this.left.getValue(); | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
const value = this.getValue(); | ||
@@ -88,9 +89,7 @@ | ||
if ( this.right.start > this.left.end + operator.length ) { | ||
code.overwrite( this.left.end, this.right.start, operator ); | ||
} | ||
code.overwrite( this.left.end, this.right.start, operator ); | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
} | ||
} |
@@ -92,3 +92,3 @@ import Node from '../Node.js'; | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
if ( this.callee.type === 'Identifier' && this.callee.name === 'eval' && !scope.contains( 'eval' ) ) { | ||
@@ -101,6 +101,6 @@ if ( this.program.options.allowDangerousEval ) { | ||
} | ||
super.initialise( scope ); | ||
super.initialise( program, scope ); | ||
} | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
const value = this.getValue(); | ||
@@ -134,4 +134,4 @@ | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
} |
@@ -6,3 +6,3 @@ import Node from '../Node.js'; | ||
export default class CatchClause extends Node { | ||
attachScope ( parent ) { | ||
attachScope ( program, parent ) { | ||
this.scope = new Scope({ | ||
@@ -18,16 +18,17 @@ block: true, | ||
for ( let i = 0; i < this.body.body.length; i += 1 ) { | ||
this.body.body[i].attachScope( this.scope ); | ||
this.body.body[i].attachScope( program, this.scope ); | ||
} | ||
if ( this.finalizer ) { | ||
this.finalizer.attachScope( this.scope ); | ||
this.finalizer.attachScope( program, this.scope ); | ||
} | ||
} | ||
initialise () { | ||
super.initialise( this.scope ); | ||
initialise ( program ) { | ||
program.addWord( 'catch' ); | ||
super.initialise( program, this.scope ); | ||
} | ||
minify ( code ) { | ||
this.scope.mangle( code ); | ||
minify ( code, chars ) { | ||
this.scope.mangle( code, chars ); | ||
@@ -42,4 +43,4 @@ if ( this.param.start > this.start + 6 ) { | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
} |
import Node from '../Node.js'; | ||
export default class ClassBody extends Node { | ||
attachScope ( parent ) { | ||
attachScope ( program, parent ) { | ||
for ( let i = 0; i < this.body.length; i += 1 ) { | ||
this.body[i].attachScope( parent ); | ||
this.body[i].attachScope( program, parent ); | ||
} | ||
} | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
let c = this.start + 1; | ||
@@ -17,3 +17,3 @@ | ||
method.minify( code ); | ||
method.minify( code, chars ); | ||
@@ -20,0 +20,0 @@ c = method.end; |
@@ -9,6 +9,7 @@ import Class from './shared/Class.js'; | ||
this.skip = false; | ||
super.initialise( this.scope ); | ||
super.initialise( this.program, this.scope ); | ||
} | ||
attachScope ( scope ) { | ||
attachScope ( program, scope ) { | ||
this.program = program; | ||
this.scope = scope; | ||
@@ -21,14 +22,14 @@ | ||
this.id.attachScope( this.scope ); | ||
if ( this.superClass ) this.superClass.attachScope( this.scope ); | ||
this.body.attachScope( scope ); | ||
this.id.attachScope( program, this.scope ); | ||
if ( this.superClass ) this.superClass.attachScope( program, this.scope ); | ||
this.body.attachScope( program, scope ); | ||
} | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
if ( scope.parent ) { | ||
// noop — we wait for this declaration to be activated | ||
} else { | ||
super.initialise( scope ); | ||
super.initialise( program, scope ); | ||
} | ||
} | ||
} |
@@ -5,3 +5,3 @@ import Class from './shared/Class.js'; | ||
export default class ClassExpression extends Class { | ||
attachScope ( parent ) { | ||
attachScope ( program, parent ) { | ||
this.scope = new Scope({ | ||
@@ -12,8 +12,8 @@ block: true, | ||
if ( this.id ) this.id.attachScope( this.scope ); | ||
if ( this.superClass ) this.superClass.attachScope( this.scope ); | ||
this.body.attachScope( this.scope ); | ||
if ( this.id ) this.id.attachScope( program, this.scope ); | ||
if ( this.superClass ) this.superClass.attachScope( program, this.scope ); | ||
this.body.attachScope( program, this.scope ); | ||
} | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
if ( this.id ) { | ||
@@ -27,9 +27,9 @@ this.id.declaration = this; | ||
super.initialise( scope ); | ||
super.initialise( program, scope ); | ||
} | ||
minify ( code ) { | ||
this.scope.mangle( code ); | ||
super.minify( code ); | ||
minify ( code, chars ) { | ||
this.scope.mangle( code, chars ); | ||
super.minify( code, chars ); | ||
} | ||
} |
@@ -34,15 +34,15 @@ import Node from '../Node.js'; | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
const testValue = this.test.getValue(); | ||
if ( testValue === UNKNOWN ) { | ||
super.initialise( scope ); | ||
super.initialise( program, scope ); | ||
} else if ( testValue ) { | ||
this.consequent.initialise( scope ); | ||
this.consequent.initialise( program, scope ); | ||
} else { | ||
this.alternate.initialise( scope ); | ||
this.alternate.initialise( program, scope ); | ||
} | ||
} | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
const testValue = this.test.getValue(); | ||
@@ -62,3 +62,3 @@ | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} else if ( testValue ) { | ||
@@ -69,3 +69,3 @@ // remove test and alternate | ||
this.consequent.minify( code ); | ||
this.consequent.minify( code, chars ); | ||
} else { | ||
@@ -75,5 +75,5 @@ // remove test and consequent | ||
this.alternate.minify( code ); | ||
this.alternate.minify( code, chars ); | ||
} | ||
} | ||
} |
import Node from '../Node.js'; | ||
export default class DoWhileStatement extends Node { | ||
minify ( code ) { | ||
initialise ( program, scope ) { | ||
program.addWord( 'dowhile' ); | ||
super.initialise( program, scope ); | ||
} | ||
minify ( code, chars ) { | ||
// special case | ||
@@ -11,2 +16,4 @@ if ( this.body.isEmpty() ) { | ||
else { | ||
this.body.minify( code, chars ); | ||
if ( this.body.type === 'BlockStatement' ) { | ||
@@ -23,4 +30,2 @@ code.remove( this.start + 2, this.body.start ); | ||
} | ||
this.body.minify( code ); | ||
} | ||
@@ -34,4 +39,4 @@ | ||
this.test.minify( code ); | ||
this.test.minify( code, chars ); | ||
} | ||
} |
@@ -21,3 +21,3 @@ import Node from '../Node.js'; | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
if ( this.expression.type === 'Literal' || this.expression.getValue() !== UNKNOWN ) { | ||
@@ -28,4 +28,4 @@ // remove side-effect-free statements (TODO others, not just literals)... | ||
super.initialise( scope ); | ||
super.initialise( program, scope ); | ||
} | ||
} |
@@ -12,3 +12,3 @@ import LoopStatement from './shared/LoopStatement.js'; | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
let c = this.start + 3; | ||
@@ -19,3 +19,3 @@ | ||
[ this.init, this.test, this.update ].forEach( ( statement, i ) => { | ||
if ( statement && !statement.skip ) { | ||
if ( statement && ( !statement.skip || statement === this.test ) ) { | ||
if ( statement.start > c + replacement.length ) { | ||
@@ -25,3 +25,3 @@ code.overwrite( c, statement.start, replacement ); | ||
statement.minify( code ); | ||
statement.minify( code, chars ); | ||
@@ -39,4 +39,4 @@ c = statement.end; | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
} |
@@ -10,16 +10,17 @@ import FunctionNode from './shared/FunctionNode.js'; | ||
if ( this.id ) this.id.initialise( this.scope.parent ); | ||
this.program.addWord( 'function' ); | ||
if ( this.id ) this.id.initialise( this.program, this.scope.parent ); | ||
this.params.forEach( param => { | ||
param.initialise( this.scope ); | ||
param.initialise( this.program, this.scope ); | ||
}); | ||
this.body.initialise( this.scope ); | ||
this.body.initialise( this.program, this.scope ); | ||
} | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
if ( scope.parent ) { | ||
// noop — we wait for this declaration to be activated | ||
} else { | ||
this.activate(); | ||
this.activate( program ); | ||
} | ||
} | ||
} |
@@ -8,5 +8,6 @@ import FunctionNode from './shared/FunctionNode.js'; | ||
initialise () { | ||
super.initialise( this.scope ); | ||
initialise ( program ) { | ||
program.addWord( 'function' ); // TODO only if has function keyword | ||
super.initialise( program, this.scope ); | ||
} | ||
} |
@@ -15,3 +15,3 @@ import Node from '../Node.js'; | ||
attachScope ( scope ) { | ||
attachScope ( program, scope ) { | ||
this.scope = scope; | ||
@@ -36,3 +36,3 @@ } | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
// special case | ||
@@ -43,2 +43,4 @@ if ( ( this.parent.type === 'FunctionExpression' || this.parent.type === 'ClassExpression' ) && this === this.parent.id ) { | ||
// TODO add global/top-level identifiers to frequency count | ||
if ( this.isReference() ) { | ||
@@ -45,0 +47,0 @@ scope.addReference( this ); |
@@ -52,7 +52,9 @@ import Node from '../Node.js'; | ||
if ( testValue ) return this.consequent.getRightHandSide(); | ||
if ( testValue || !this.alternate ) return this.consequent.getRightHandSide(); | ||
return this.alternate.getRightHandSide(); | ||
} | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
// TODO add 'if/else' to character frequency, but only if not rewriting as sequence | ||
this.skip = false; // TODO skip if known to be safe | ||
@@ -64,9 +66,9 @@ | ||
// initialise everything | ||
this.test.initialise( scope ); | ||
this.consequent.initialise( scope ); | ||
if ( this.alternate ) this.alternate.initialise( scope ); | ||
this.test.initialise( program, scope ); | ||
this.consequent.initialise( program, scope ); | ||
if ( this.alternate ) this.alternate.initialise( program, scope ); | ||
} | ||
else if ( testValue ) { // if ( true ) {...} | ||
this.consequent.initialise( scope ); | ||
this.consequent.initialise( program, scope ); | ||
@@ -82,3 +84,3 @@ if ( this.alternate && this.alternate.type === 'BlockStatement' ) { | ||
if ( this.alternate ) { | ||
this.alternate.initialise( scope ); | ||
this.alternate.initialise( program, scope ); | ||
} else { | ||
@@ -98,3 +100,3 @@ this.skip = true; | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
const testValue = this.test.getValue(); | ||
@@ -110,6 +112,6 @@ | ||
code.remove( this.start, this.consequent.start ); | ||
this.consequent.minify( code ); | ||
this.consequent.minify( code, chars ); | ||
} else { // if ( false ) {...} | ||
// we know there's an alternate, otherwise we wouldn't be here | ||
this.alternate.minify( code ); | ||
this.alternate.minify( code, chars ); | ||
code.remove( this.start, this.alternate.start ); | ||
@@ -121,3 +123,3 @@ } | ||
this.test.minify( code ); | ||
this.test.minify( code, chars ); | ||
@@ -145,3 +147,3 @@ // if we're rewriting as &&, test must be higher precedence than 6 | ||
if ( this.alternate && !this.alternate.isEmpty() ) { | ||
this.alternate.minify( code ); | ||
this.alternate.minify( code, chars ); | ||
@@ -212,3 +214,3 @@ if ( this.alternate.type === 'BlockStatement' && this.alternate.body.length === 0 ) { | ||
// don't minify alternate | ||
this.consequent.minify( code ); | ||
this.consequent.minify( code, chars ); | ||
code.remove( this.consequent.end, this.end ); | ||
@@ -235,4 +237,4 @@ | ||
this.consequent.minify( code ); | ||
if ( this.alternate ) this.alternate.minify( code ); | ||
this.consequent.minify( code, chars ); | ||
if ( this.alternate ) this.alternate.minify( code, chars ); | ||
@@ -239,0 +241,0 @@ if ( this.canSequentialise() ) { |
import Node from '../Node.js'; | ||
export default class ImportDefaultSpecifier extends Node { | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
this.local.declaration = this; | ||
scope.addDeclaration( this.local, 'import' ); | ||
super.initialise( scope ); | ||
super.initialise( program, scope ); | ||
} | ||
} |
import Node from '../Node.js'; | ||
export default class ImportSpecifier extends Node { | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
this.local.declaration = this; | ||
scope.addDeclaration( this.local, 'import' ); | ||
super.initialise( scope ); | ||
super.initialise( program, scope ); | ||
} | ||
} |
@@ -20,2 +20,3 @@ import Array from './shared/Array.js'; | ||
import IfStatement from './IfStatement.js'; | ||
import ImportDeclaration from './ImportDeclaration.js'; | ||
import ImportDefaultSpecifier from './ImportDefaultSpecifier.js'; | ||
@@ -68,2 +69,3 @@ import ImportSpecifier from './ImportSpecifier.js'; | ||
IfStatement, | ||
ImportDeclaration, | ||
ImportDefaultSpecifier, | ||
@@ -70,0 +72,0 @@ ImportSpecifier, |
import Node from '../Node.js'; | ||
export default class LabeledStatement extends Node { | ||
minify ( code ) { | ||
getRightHandSide () { | ||
return this.body.getRightHandSide(); | ||
} | ||
initialise ( program, scope ) { | ||
program.addWord( this.label.name ); | ||
super.initialise( program, scope ); | ||
} | ||
minify ( code, chars ) { | ||
// TODO can we mangle labels? | ||
if ( this.body.start > this.label.end + 1 ) { | ||
@@ -14,5 +25,5 @@ code.overwrite( this.label.end, this.body.start, ':' ); | ||
} else { | ||
this.body.minify( code ); | ||
this.body.minify( code, chars ); | ||
} | ||
} | ||
} |
@@ -5,3 +5,3 @@ import Node from '../Node.js'; | ||
export default class Literal extends Node { | ||
attachScope ( scope ) { | ||
attachScope ( program, scope ) { | ||
if ( this.value === 'use strict' ) { | ||
@@ -27,4 +27,4 @@ const block = this.parent.parent; | ||
initialise () { | ||
// noop | ||
initialise ( program ) { | ||
program.addWord( stringify( this.value ) ); | ||
} | ||
@@ -34,9 +34,13 @@ | ||
if ( this.value === true || this.value === false ) { | ||
code.overwrite( this.start, this.end, this.value ? '!0' : '!1' ); | ||
code.overwrite( this.start, this.end, this.value ? '!0' : '!1', { | ||
contentOnly: true | ||
}); | ||
} | ||
else if ( typeof this.value === 'number' ) { | ||
code.overwrite( this.start, this.end, stringify( this.value ) ); | ||
code.overwrite( this.start, this.end, stringify( this.value ), { | ||
contentOnly: true | ||
}); | ||
} | ||
} | ||
} |
@@ -50,3 +50,3 @@ import Node from '../Node.js'; | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
const leftValue = this.left.getValue(); | ||
@@ -59,3 +59,3 @@ | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
@@ -66,6 +66,6 @@ | ||
code.remove( this.start, this.right.start ); | ||
this.right.minify( code ); | ||
this.right.minify( code, chars ); | ||
} else { | ||
code.remove( this.left.end, this.end ); | ||
this.left.minify( code ); | ||
this.left.minify( code, chars ); | ||
} | ||
@@ -77,6 +77,6 @@ } | ||
code.remove( this.left.end, this.end ); | ||
this.left.minify( code ); | ||
this.left.minify( code, chars ); | ||
} else { | ||
code.remove( this.start, this.right.start ); | ||
this.right.minify( code ); | ||
this.right.minify( code, chars ); | ||
} | ||
@@ -83,0 +83,0 @@ } |
import Node from '../Node.js'; | ||
import reserved from '../../utils/reserved.js'; | ||
import { reservedLookup } from '../../utils/reserved.js'; | ||
import { UNKNOWN } from '../../utils/sentinels.js'; | ||
@@ -9,3 +9,3 @@ import stringify from '../../utils/stringify.js'; | ||
// TODO there's probably a bit more to it than this | ||
return !reserved[ str ] && /^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test( str ); | ||
return !reservedLookup[ str ] && /^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test( str ); | ||
} | ||
@@ -53,6 +53,11 @@ | ||
minify ( code ) { | ||
initialise ( program, scope ) { | ||
if ( !this.computed ) program.addWord( this.property.name ); | ||
super.initialise( program, scope ); | ||
} | ||
minify ( code, chars ) { | ||
const value = this.getValue(); | ||
if ( value !== UNKNOWN && canFold( this, this.parent ) ) { | ||
if ( value && value !== UNKNOWN && canFold( this, this.parent ) ) { | ||
const str = stringify( value ); | ||
@@ -66,2 +71,8 @@ | ||
// special case — numbers | ||
const objectValue = this.object.getValue(); | ||
if ( typeof objectValue === 'number' && objectValue === parseInt( objectValue, 10 ) ) { | ||
this.object.append( code, '.' ); | ||
} | ||
if ( this.computed ) { | ||
@@ -87,3 +98,3 @@ const value = this.property.getValue(); | ||
this.property.minify( code ); | ||
this.property.minify( code, chars ); | ||
} | ||
@@ -98,4 +109,4 @@ } | ||
this.object.minify( code ); | ||
this.object.minify( code, chars ); | ||
} | ||
} |
@@ -5,6 +5,11 @@ import Node from '../Node.js'; | ||
export default class MethodDefinition extends Node { | ||
minify ( code ) { | ||
minifyPropertyKey( code, this, false ); | ||
this.value.minify( code ); | ||
initialise ( program, scope ) { | ||
if ( !this.computed ) program.addWord( this.key.name ); | ||
super.initialise( program, scope ); | ||
} | ||
minify ( code, chars ) { | ||
minifyPropertyKey( code, chars, this, false ); | ||
this.value.minify( code, chars ); | ||
} | ||
} |
@@ -8,3 +8,8 @@ import Node from '../Node.js'; | ||
minify ( code ) { | ||
initialise ( program, scope ) { | ||
program.addWord( 'new' ); | ||
super.initialise( program, scope ); | ||
} | ||
minify ( code, chars ) { | ||
if ( this.arguments.length ) { | ||
@@ -27,4 +32,4 @@ let lastNode = this.callee; | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
} |
@@ -10,3 +10,3 @@ import Node from '../Node.js'; | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
let c = this.start; | ||
@@ -20,4 +20,4 @@ | ||
minifyPropertyKey( code, p, true ); | ||
p.value.minify( code ); | ||
minifyPropertyKey( code, chars, p, true ); | ||
p.value.minify( code, chars ); | ||
@@ -24,0 +24,0 @@ c = p.end; |
import Node from '../Node.js'; | ||
export default class ObjectPattern extends Node { | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
let c = this.start + 1; | ||
@@ -9,3 +9,3 @@ for ( let i = 0; i < this.properties.length; i += 1 ) { | ||
const property = this.properties[i]; | ||
property.minify( code ); | ||
property.minify( code, chars ); | ||
@@ -12,0 +12,0 @@ if ( property.start > c ) code.overwrite( c, property.start, i ? ',' : '' ); |
@@ -49,2 +49,13 @@ import Node from '../Node.js'; | ||
getRightHandSide () { | ||
let node = this; | ||
while ( node.type === 'ParenthesizedExpression' ) { | ||
node = node.expression; | ||
} | ||
if ( shouldRemoveParens( node, this.parent ) ) return node.getRightHandSide(); | ||
return node.parent; | ||
} | ||
getPrecedence () { | ||
@@ -58,3 +69,3 @@ return 20; | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
let start = this.start; | ||
@@ -91,4 +102,4 @@ let end = this.end; | ||
expression.minify( code ); | ||
expression.minify( code, chars ); | ||
} | ||
} |
@@ -8,3 +8,8 @@ import Node from '../Node.js'; | ||
export default class ReturnStatement extends Node { | ||
minify ( code ) { | ||
initialise ( program, scope ) { | ||
program.addWord( 'return' ); | ||
super.initialise( program, scope ); | ||
} | ||
minify ( code, chars ) { | ||
if ( !this.argument ) return; | ||
@@ -28,4 +33,4 @@ | ||
this.argument.minify( code ); | ||
this.argument.minify( code, chars ); | ||
} | ||
} |
@@ -22,3 +22,3 @@ import Node from '../../Node.js'; | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
let c = this.start; | ||
@@ -47,4 +47,4 @@ | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
} |
@@ -15,3 +15,8 @@ import Node from '../../Node.js'; | ||
export default class Class extends Node { | ||
minify ( code ) { | ||
initialise ( program, scope ) { | ||
program.addWord( 'class' ); | ||
super.initialise( program, scope ); | ||
} | ||
minify ( code, chars ) { | ||
let c = this.start + 5; | ||
@@ -40,4 +45,4 @@ | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
} |
@@ -12,3 +12,3 @@ import LoopStatement from './LoopStatement.js'; | ||
minify ( code, transforms ) { | ||
minify ( code, chars ) { | ||
if ( this.left.start > this.start + 4 ) { | ||
@@ -26,6 +26,6 @@ code.overwrite( this.start + 3, this.left.start, '(' ); | ||
this.left.minify( code ); | ||
this.right.minify( code ); | ||
super.minify( code, transforms ); | ||
this.left.minify( code, chars ); | ||
this.right.minify( code, chars ); | ||
super.minify( code, chars ); | ||
} | ||
} |
@@ -28,3 +28,4 @@ import Node from '../../Node.js'; | ||
export default class FunctionNode extends Node { | ||
attachScope ( parent ) { | ||
attachScope ( program, parent ) { | ||
this.program = program; | ||
this.scope = new Scope({ | ||
@@ -40,6 +41,6 @@ block: false, | ||
if ( this.type === 'FunctionExpression' ) { | ||
this.scope.addDeclaration( this.id, 'function' ); | ||
this.scope.addDeclaration( this.id, this.type ); | ||
this.scope.addReference( this.id ); | ||
} else { | ||
parent.addDeclaration( this.id, 'function' ); | ||
parent.addDeclaration( this.id, this.type ); | ||
} | ||
@@ -49,3 +50,3 @@ } | ||
this.params.forEach( param => { | ||
param.attachScope( this.scope ); | ||
param.attachScope( program, this.scope ); | ||
@@ -58,3 +59,3 @@ extractNames( param ).forEach( node => { | ||
this.body.attachScope( this.scope ); | ||
this.body.attachScope( program, this.scope ); | ||
} | ||
@@ -66,3 +67,5 @@ | ||
minify ( code ) { | ||
// TODO `program.addWord('async')` if necessary | ||
minify ( code, chars ) { | ||
let c = this.start; | ||
@@ -99,3 +102,3 @@ | ||
const param = this.params[i]; | ||
param.minify( code ); | ||
param.minify( code, chars ); | ||
@@ -111,4 +114,4 @@ if ( param.start > c + 1 ) code.overwrite( c, param.start, i ? ',' : '(' ); | ||
this.body.minify( code ); | ||
this.body.minify( code, chars ); | ||
} | ||
} |
@@ -5,3 +5,3 @@ import Node from '../../Node.js'; | ||
export default class LoopStatement extends Node { | ||
attachScope ( parent ) { | ||
attachScope ( program, parent ) { | ||
if ( this.hasVariableDeclaration() ) { | ||
@@ -13,14 +13,18 @@ this.scope = new Scope({ | ||
super.attachScope( this.scope ); | ||
super.attachScope( program, this.scope ); | ||
} else { | ||
super.attachScope( parent ); | ||
super.attachScope( program, parent ); | ||
} | ||
} | ||
initialise ( scope ) { | ||
super.initialise( this.scope || scope ); | ||
initialise ( program, scope ) { | ||
program.addWord( 'for' ); | ||
if ( this.type === 'ForInStatement' ) program.addWord( 'in' ); | ||
else if ( this.type === 'ForOfStatement' ) program.addWord( 'of' ); | ||
super.initialise( program, this.scope || scope ); | ||
} | ||
minify ( code ) { | ||
if ( this.scope ) this.scope.mangle( code ); | ||
minify ( code, chars ) { | ||
if ( this.scope ) this.scope.mangle( code, chars ); | ||
@@ -32,5 +36,5 @@ // special case — empty body | ||
} else { | ||
this.body.minify( code ); | ||
this.body.minify( code, chars ); | ||
} | ||
} | ||
} |
@@ -5,3 +5,3 @@ function isAccessor ( property ) { | ||
export default function minifyPropertyKey ( code, property, isObject ) { | ||
export default function minifyPropertyKey ( code, chars, property, isObject ) { | ||
if ( property.shorthand ) return; | ||
@@ -43,3 +43,3 @@ | ||
property.key.minify( code ); | ||
property.key.minify( code, chars ); | ||
} |
import Node from '../Node.js'; | ||
export default class SwitchCase extends Node { | ||
minify ( code ) { | ||
getRightHandSide () { | ||
if ( this.consequent.length > 0 ) { | ||
return this.consequent[ this.consequent.length - 1 ].getRightHandSide(); | ||
} | ||
return this; | ||
} | ||
initialise ( program, scope ) { | ||
program.addWord( this.test ? 'case' : 'default' ); | ||
super.initialise( program, scope ); | ||
} | ||
minify ( code, chars ) { | ||
let c; | ||
if ( this.test ) { | ||
this.test.minify( code ); | ||
this.test.minify( code, chars ); | ||
@@ -21,3 +34,3 @@ if ( this.test.start > this.start + 5 ) { | ||
this.consequent.forEach( ( statement, i ) => { | ||
statement.minify( code ); | ||
statement.minify( code, chars ); | ||
@@ -24,0 +37,0 @@ const separator = i ? ';' : ':'; // TODO can consequents be written as sequences? |
@@ -5,4 +5,4 @@ import Node from '../Node.js'; | ||
export default class SwitchStatement extends Node { | ||
initialise ( scope ) { | ||
super.initialise( scope ); | ||
initialise ( program, scope ) { | ||
super.initialise( program, scope ); | ||
@@ -13,8 +13,12 @@ if ( this.cases.length === 0 ) { | ||
} | ||
if ( !this.skip ) { | ||
program.addWord( 'switch' ); | ||
} | ||
} | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
// special (and unlikely!) case — no cases, but a non-removable discriminant | ||
if ( this.cases.length === 0 ) { | ||
this.discriminant.minify( code ); | ||
this.discriminant.minify( code, chars ); | ||
code.remove( this.start, this.discriminant.start ); | ||
@@ -32,5 +36,4 @@ code.remove( this.discriminant.end, this.end ); | ||
this.cases.forEach( ( switchCase, i ) => { | ||
if ( switchCase.start > c + ( i ? 1 : 2 ) ) { | ||
code.overwrite( c, switchCase.start, i ? ';' : '){' ); | ||
} | ||
code.remove( c, switchCase.start ); | ||
switchCase.prepend( code, i > 0 ? ';' : '){' ); | ||
@@ -43,5 +46,5 @@ c = switchCase.end; | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
} | ||
} |
import Node from '../Node.js'; | ||
export default class TaggedTemplateExpression extends Node { | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
if ( this.quasi.start > this.tag.end ) code.remove( this.tag.end, this.quasi.start ); | ||
this.quasi.minify( code ); | ||
this.quasi.minify( code, chars ); | ||
} | ||
} |
@@ -33,3 +33,3 @@ import Node from '../Node.js'; | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
if ( this.parent.type !== 'TaggedTemplateExpression' ) { | ||
@@ -53,3 +53,3 @@ const value = this.getValue(); | ||
if ( typeof value === 'object' ) { // includes both UNKNOWN and known non-primitives | ||
expression.minify( code ); | ||
expression.minify( code, chars ); | ||
@@ -56,0 +56,0 @@ if ( expression.start > quasi.end + 2 ) { |
import Node from '../Node.js'; | ||
export default class TryStatement extends Node { | ||
minify ( code ) { | ||
initialise ( program, scope ) { | ||
program.addWord( 'try' ); | ||
if ( this.finalizer ) program.addWord( 'finally' ); | ||
super.initialise( program, scope ); | ||
} | ||
minify ( code, chars ) { | ||
if ( this.block.start > this.start + 3 ) code.remove( this.start + 3, this.block.start ); | ||
@@ -21,4 +28,4 @@ | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
} |
@@ -29,3 +29,3 @@ import Node from '../Node.js'; | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
const value = this.getValue(); | ||
@@ -45,5 +45,5 @@ if ( value !== UNKNOWN ) { | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
} | ||
} |
@@ -9,3 +9,3 @@ import Node from '../Node.js'; | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
if ( this.argument.type === 'Identifier' ) { | ||
@@ -18,4 +18,4 @@ const declaration = scope.findDeclaration( this.argument.name ); | ||
super.initialise( scope ); | ||
super.initialise( program, scope ); | ||
} | ||
} |
@@ -11,5 +11,5 @@ import Node from '../Node.js'; | ||
export default class VariableDeclaration extends Node { | ||
attachScope ( scope ) { | ||
attachScope ( program, scope ) { | ||
this.declarations.forEach( declarator => { | ||
declarator.attachScope( scope ); | ||
declarator.attachScope( program, scope ); | ||
}); | ||
@@ -20,3 +20,5 @@ | ||
initialise ( scope ) { | ||
initialise ( program, scope ) { | ||
// TODO `program.addWord(kind)`, but only if this declaration is included... | ||
let _scope = scope; | ||
@@ -32,5 +34,5 @@ if ( this.kind === 'var' ) while ( _scope.isBlockScope ) _scope = _scope.parent; | ||
// only initialise top-level variables. TODO unless we're in e.g. module mode | ||
declarator.initialise( scope ); | ||
declarator.initialise( program, scope ); | ||
} else { | ||
if ( declarator.init ) declarator.init.initialise( scope ); | ||
if ( declarator.init ) declarator.init.initialise( program, scope ); | ||
} | ||
@@ -40,3 +42,3 @@ }); | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
if ( this.collapsed ) return; | ||
@@ -82,3 +84,3 @@ | ||
declarator.init.minify( code ); | ||
declarator.init.minify( code, chars ); | ||
@@ -89,3 +91,3 @@ // we have a situation like `var unused = x()` — need to preserve `x()` | ||
} else { | ||
declarator.minify( code ); | ||
declarator.minify( code, chars ); | ||
@@ -92,0 +94,0 @@ let separator = needsKeyword ? |
@@ -6,3 +6,3 @@ import Node from '../Node.js'; | ||
// TODO this can get way more sophisticated | ||
if ( node.type === 'Identifier' || node.type === 'Literal' ) return false; | ||
if ( node.type === 'Identifier' || node.type === 'Literal' || /FunctionExpression/.test( node.type ) ) return false; | ||
return true; | ||
@@ -17,14 +17,16 @@ } | ||
this.skip = this.parent.skip = false; | ||
this.id.initialise( this.scope ); | ||
if ( this.init ) this.init.initialise( this.scope ); | ||
this.id.initialise( this.program, this.scope ); | ||
if ( this.init ) this.init.initialise( this.program, this.scope ); | ||
} | ||
attachScope ( scope ) { | ||
attachScope ( program, scope ) { | ||
this.program = program; | ||
this.scope = scope; | ||
const kind = this.parent.kind; | ||
this.id.attachScope( scope ); | ||
this.id.attachScope( program, scope ); | ||
if ( this.init ) { | ||
this.init.attachScope( scope ); | ||
this.init.attachScope( program, scope ); | ||
@@ -42,3 +44,3 @@ if ( mightHaveSideEffects( this.init ) ) { | ||
minify ( code ) { | ||
minify ( code, chars ) { | ||
if ( this.init ) { | ||
@@ -48,4 +50,4 @@ if ( this.init.start > this.id.end + 1 ) code.overwrite( this.id.end, this.init.start, '=' ); | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
} |
@@ -8,3 +8,8 @@ import Node from '../Node.js'; | ||
minify ( code ) { | ||
initialise ( program, scope ) { | ||
program.addWord( 'while' ); | ||
super.initialise( program, scope ); | ||
} | ||
minify ( code, chars ) { | ||
if ( this.test.start > this.start + 6 ) { | ||
@@ -24,4 +29,4 @@ code.overwrite( this.start + 5, this.test.start, '(' ); | ||
super.minify( code ); | ||
super.minify( code, chars ); | ||
} | ||
} |
@@ -7,2 +7,7 @@ import Node from '../Node.js'; | ||
} | ||
initialise ( program, scope ) { | ||
program.addWord( 'yield' ); | ||
super.initialise( program, scope ); | ||
} | ||
} |
@@ -1,5 +0,6 @@ | ||
let reserved = Object.create( null ); | ||
'do if in for let new try var case else enum eval null this true void with await break catch class const false super throw while yield delete export import public return static switch typeof default extends finally package private continue debugger function arguments interface protected implements instanceof'.split( ' ' ) | ||
.forEach( word => reserved[ word ] = true ); | ||
export const reserved = 'do if in for let new try var case else enum eval null this true void with await break catch class const false super throw while yield delete export import public return static switch typeof default extends finally package private continue debugger function arguments interface protected implements instanceof'.split( ' ' ); | ||
export default reserved; | ||
export const reservedLookup = Object.create( null ); | ||
reserved.forEach( word => { | ||
reservedLookup[ word ] = true; | ||
}); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2377280
87
17214
3
+ Addedmagic-string@0.20.0(transitive)
- Removedmagic-string@0.19.1(transitive)
Updatedmagic-string@^0.20.0