UNPKG

butternut

Version:
1,762 lines (1,358 loc) 120 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var acorn = require('acorn'); var MagicString = _interopDefault(require('magic-string')); var sourcemapCodec = require('sourcemap-codec'); function parse$1 ( source ) { return acorn.parse( source, { ecmaVersion: 8, preserveParens: true, sourceType: 'module', allowReserved: true, allowReturnOutsideFunction: true }); } var UNKNOWN = {}; function locate ( source, index ) { var lines = source.split( '\n' ); var len = lines.length; var lineStart = 0; var i; for ( i = 0; i < len; i += 1 ) { var line = lines[i]; var lineEnd = lineStart + line.length + 1; // +1 for newline if ( lineEnd > index ) { return { line: i + 1, column: index - lineStart, char: i }; } lineStart = lineEnd; } throw new Error( 'Could not determine location of character' ); } function pad ( num, len ) { var result = String( num ); return result + repeat( ' ', len - result.length ); } function repeat ( str, times ) { var result = ''; while ( times-- ) { result += str; } return result; } function getSnippet ( source, loc, length ) { if ( length === void 0 ) length = 1; var first = Math.max( loc.line - 5, 0 ); var last = loc.line; var numDigits = String( last ).length; var lines = source.split( '\n' ).slice( first, last ); var lastLine = lines[ lines.length - 1 ]; var offset = lastLine.slice( 0, loc.column ).replace( /\t/g, ' ' ).length; var snippet = lines .map( function ( line, i ) { return ((pad( i + first + 1, numDigits )) + " : " + (line.replace( /\t/g, ' '))); } ) .join( '\n' ); snippet += '\n' + repeat( ' ', numDigits + 3 + offset ) + repeat( '^', length ); return snippet; } var CompileError = (function (Error) { function CompileError ( node, message ) { Error.call(this); var source = node.program.magicString.original; var loc = locate( source, node.start ); this.name = 'CompileError'; this.message = message + " (" + (loc.line) + ":" + (loc.column) + ")"; this.stack = new Error().stack.replace( new RegExp( (".+new " + (this.name) + ".+\\n"), 'm' ), '' ); this.loc = loc; this.pos = loc.char; this.snippet = getSnippet( source, loc, node.end - node.start ); } if ( Error ) CompileError.__proto__ = Error; CompileError.prototype = Object.create( Error && Error.prototype ); CompileError.prototype.constructor = CompileError; return CompileError; }(Error)); var Node = function Node () {}; Node.prototype.ancestor = function ancestor ( level ) { var node = this; while ( level-- ) { node = node.parent; if ( !node ) { return null; } } return node; }; Node.prototype.append = function append ( code, content ) { code.appendLeft( this.getRightHandSide().end, content ); }; Node.prototype.attachScope = function attachScope ( program, scope ) { var this$1 = this; for ( var i$1 = 0, list = this$1.keys; i$1 < list.length; i$1 += 1 ) { var key = list[i$1]; var value = this$1[ key ]; if ( value ) { if ( 'length' in value ) { var i = value.length; while ( i-- ) { if ( value[i] ) { value[i].attachScope( program, scope ); } } } else { value.attachScope( program, scope ); } } } }; Node.prototype.canSequentialise = function canSequentialise () { return false; }; Node.prototype.contains = function contains ( node ) { var this$1 = this; while ( node ) { if ( node === this$1 ) { return true; } node = node.parent; } return false; }; Node.prototype.error = function error ( message ) { throw new CompileError( this, message ); }; Node.prototype.getLeftHandSide = function getLeftHandSide () { return this; }; Node.prototype.getPrecedence = function getPrecedence () { return 0; }; Node.prototype.getRightHandSide = function getRightHandSide () { return this; }; Node.prototype.getValue = function getValue () { return UNKNOWN; }; Node.prototype.initialise = function initialise ( program, scope ) { var this$1 = this; this.skip = false; for ( var i$1 = 0, list = this$1.keys; i$1 < list.length; i$1 += 1 ) { var key = list[i$1]; var value = this$1[ key ]; if ( value ) { if ( 'length' in value ) { var i = value.length; while ( i-- ) { if ( value[i] ) { value[i].initialise( program, scope ); } } } else { value.initialise( program, scope ); } } } }; Node.prototype.isEmpty = function isEmpty () { return this.skip; }; Node.prototype.findVarDeclarations = function findVarDeclarations ( varsToHoist ) { var this$1 = this; for ( var i$1 = 0, list = this$1.keys; i$1 < list.length; i$1 += 1 ) { var key = list[i$1]; var value = this$1[ key ]; if ( value ) { if ( 'length' in value ) { var i = value.length; while ( i-- ) { if ( value[i] ) { value[i].findVarDeclarations( varsToHoist ); } } } else { value.findVarDeclarations( varsToHoist ); } } } }; Node.prototype.move = function move ( code, position ) { code.move( this.getLeftHandSide().start, this.getRightHandSide().end, position ); }; Node.prototype.minify = function minify ( code, chars ) { var this$1 = this; for ( var i$1 = 0, list = this$1.keys; i$1 < list.length; i$1 += 1 ) { var key = list[i$1]; var value = this$1[ key ]; if ( value ) { if ( 'length' in value ) { var i = value.length; while ( i-- ) { if ( value[i] ) { value[i].minify( code, chars ); } } } else { value.minify( code, chars ); } } } }; Node.prototype.parenthesize = function parenthesize ( code ) { this.prepend( code, '(' ); this.append( code, ')' ); }; Node.prototype.prepend = function prepend ( code, content ) { code.prependRight( this.getLeftHandSide().start, content ); }; Node.prototype.preventsCollapsedReturns = function preventsCollapsedReturns ( returnStatements ) { if ( this.type === 'ExpressionStatement' ) { return false; } if ( this.type === 'ReturnStatement' ) { return returnStatements.push( this ), false; } if ( this.type === 'IfStatement' ) { return !this.preventsCollapsedReturns( returnStatements ); } return true; }; Node.prototype.source = function source () { return this.program.magicString.original.slice( this.start, this.end ); }; Node.prototype.toString = function toString () { return this.program.magicString.slice( this.start, this.end ); }; var ArrayExpression = (function (Node$$1) { function ArrayExpression () { Node$$1.apply(this, arguments); } if ( Node$$1 ) ArrayExpression.__proto__ = Node$$1; ArrayExpression.prototype = Object.create( Node$$1 && Node$$1.prototype ); ArrayExpression.prototype.constructor = ArrayExpression; ArrayExpression.prototype.getValue = function getValue () { var this$1 = this; var values = new Array( this.elements.length ); for ( var i = 0; i < this.elements.length; i += 1 ) { var element = this$1.elements[i]; if ( element ) { var value = element.getValue(); if ( value === UNKNOWN ) { return UNKNOWN; } values[i] = value; } } return values; }; ArrayExpression.prototype.minify = function minify ( code, chars ) { var this$1 = this; var c = this.start; if ( this.elements.length ) { var insert = '['; this.elements.forEach( function ( element, i ) { if ( !element ) { insert += i === this$1.elements.length - 1 ? ',]' : ','; return; } if ( element.start > c + 1 ) { code.overwrite( c, element.start, insert ); } c = element.end; insert = i === this$1.elements.length - 1 ? ']' : ','; }); if ( this.end > insert.length ) { code.overwrite( c, this.end, insert ); } } else { if ( this.end > c + 2 ) { code.overwrite( c, this.end, '[]' ); } } Node$$1.prototype.minify.call( this, code, chars ); }; return ArrayExpression; }(Node)); var 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( ' ' ); var reservedLookup = Object.create( null ); reserved.forEach( function (word) { reservedLookup[ word ] = true; }); var letConst = /^(?:let|const)$/; function Scope ( options ) { options = options || {}; this.parent = options.parent; this.canMangle = !!this.parent; this.isBlockScope = !!options.block; this.useStrict = this.parent && this.parent.useStrict; // vars declared in blocks are stored here, so that we // can hoist them if those blocks are removed but the // declarations are used. TODO an alternative approach // would be to replace instances of the hoisted var // with `void 0` this.varDeclarations = new Set(); this.hoistedVars = new Set(); this.varDeclarationNodes = []; var scope = this; while ( scope.isBlockScope ) { scope = scope.parent; } this.functionScope = scope; this.identifiers = []; this.declarations = Object.create( null ); this.references = Object.create( null ); this.blockScopedDeclarations = this.isBlockScope ? null : Object.create( null ); this.aliases = Object.create( null ); this.idCounter = [ 0 ]; } Scope.prototype = { addDeclaration: function addDeclaration ( identifier, kind ) { if ( kind === 'var' && this.isBlockScope ) { this.varDeclarations.add( identifier.name ); this.parent.addDeclaration( identifier, kind ); return; } var name = identifier.name; var existingDeclaration = this.declarations[ name ]; if ( existingDeclaration ) { if ( letConst.test( kind ) || letConst.test( existingDeclaration.kind ) ) { // TODO warn about double var declarations? throw new CompileError( identifier, (name + " is already declared") ); } // special case — function expression IDs that are shadowed by // declarations should just be removed (TODO unless the user wishes // to keep function names — https://github.com/Rich-Harris/butternut/issues/17) if ( existingDeclaration.kind === 'FunctionExpression' ) { existingDeclaration.node.parent.shadowed = true; } else { identifier.isDuplicate = true; if ( existingDeclaration.activated ) { identifier.activate(); } else { existingDeclaration.duplicates.push( identifier ); } return; } } var declaration = { activated: !this.parent, // TODO is this necessary? name: name, node: identifier, kind: kind, instances: [], duplicates: [] }; this.declarations[ name ] = declaration; if ( this.isBlockScope ) { if ( !this.functionScope.blockScopedDeclarations[ name ] ) { this.functionScope.blockScopedDeclarations[ name ] = []; } this.functionScope.blockScopedDeclarations[ name ].push( declaration ); } if ( kind === 'param' ) { declaration.instances.push( identifier ); } }, addReference: function addReference ( identifier ) { var declaration = this.declarations[ identifier.name ]; if ( declaration ) { declaration.instances.push( identifier ); if ( !declaration.activated ) { declaration.activated = true; // const parent = declaration.node.parent; declaration.node.activate(); declaration.duplicates.forEach( function (dupe) { dupe.activate(); }); // if ( declaration.kind === 'param' ) { // // TODO is there anything to do here? // } else if ( parent.activate ) { // parent.activate(); // } } } else { this.references[ identifier.name ] = true; if ( this.parent ) { this.parent.addReference( identifier ); } } }, contains: function contains ( name ) { return this.declarations[ name ] || ( this.parent ? this.parent.contains( name ) : false ); }, deopt: function deopt () { var this$1 = this; if ( !this.deopted ) { this.deopted = true; this.canMangle = false; if ( this.parent ) { this.parent.deopt(); } Object.keys( this.declarations ).forEach( function (name) { this$1.declarations[name].node.activate(); }); } }, findDeclaration: function findDeclaration ( name ) { return this.declarations[ name ] || ( this.parent && this.parent.findDeclaration( name ) ); }, mangle: function mangle ( code, chars ) { var this$1 = this; if ( !this.canMangle ) { return; } var used = Object.create( null ); reserved.forEach( function (word) { used[ word ] = true; }); Object.keys( this.references ).forEach( function (reference) { var declaration = this$1.parent && this$1.parent.findDeclaration( reference ); used[ declaration && declaration.alias || reference ] = true; }); var i = -1; function getNextAlias () { var 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( function (name) { var declaration = this$1.declarations[ name ]; if ( declaration.instances.length === 0 ) { return; } // special case — function expression IDs may be removed outright if ( declaration.node.parent.type === 'FunctionExpression' && declaration.node === declaration.node.parent.id ) { if ( declaration.node.shadowed || declaration.instances.length === 1 ) { return; } } declaration.alias = getNextAlias(); declaration.instances.forEach( function (instance) { var replacement = instance.parent.type === 'Property' && instance.parent.shorthand ? ((instance.name) + ":" + (declaration.alias)) : declaration.alias; code.overwrite( instance.start, instance.end, replacement, true ); }); }); } }; // adapted from https://github.com/mishoo/UglifyJS2/blob/master/lib/scope.js function getAlias ( chars, i ) { var alias = ''; var base = 54; i++; do { i--; alias += chars[ i % base ]; i = Math.floor( i / base ); base = 64; } while ( i > 0 ); return alias; } function extractNames ( node ) { var names = []; extractors[ node.type ]( names, node ); return names; } var extractors = { Identifier: function Identifier ( names, node ) { names.push( node ); }, ObjectPattern: function ObjectPattern ( names, node ) { for ( var i = 0, list = node.properties; i < list.length; i += 1 ) { var prop = list[i]; extractors[ prop.value.type ]( names, prop.value ); } }, ArrayPattern: function ArrayPattern ( names, node ) { for ( var i = 0, list = node.elements; i < list.length; i += 1 ) { var element = list[i]; if ( element ) { extractors[ element.type ]( names, element ); } } }, RestElement: function RestElement ( names, node ) { extractors[ node.argument.type ]( names, node.argument ); }, AssignmentPattern: function AssignmentPattern ( names, node ) { extractors[ node.left.type ]( names, node.left ); } }; var ArrowFunctionExpression = (function (Node$$1) { function ArrowFunctionExpression () { Node$$1.apply(this, arguments); } if ( Node$$1 ) ArrowFunctionExpression.__proto__ = Node$$1; ArrowFunctionExpression.prototype = Object.create( Node$$1 && Node$$1.prototype ); ArrowFunctionExpression.prototype.constructor = ArrowFunctionExpression; ArrowFunctionExpression.prototype.attachScope = function attachScope ( program, parent ) { var this$1 = this; this.scope = new Scope({ block: false, parent: parent }); this.params.forEach( function (param) { param.attachScope( program, this$1.scope ); extractNames( param ).forEach( function (node) { node.declaration = this$1; this$1.scope.addDeclaration( node, 'param' ); }); }); if ( this.body.type === 'BlockStatement' ) { this.body.body.forEach( function (node) { node.attachScope( program, this$1.scope ); }); } else { this.body.attachScope( program, this.scope ); } }; ArrowFunctionExpression.prototype.initialise = function initialise ( program ) { Node$$1.prototype.initialise.call( this, program, this.scope ); }; ArrowFunctionExpression.prototype.findVarDeclarations = function findVarDeclarations () { // noop }; ArrowFunctionExpression.prototype.getLeftHandSide = function getLeftHandSide () { return this.params.length === 1 ? this.params[0] : this; }; ArrowFunctionExpression.prototype.minify = function minify ( code, chars ) { this.scope.mangle( code, chars ); var c = this.start; if ( this.async ) { c += 5; } if ( this.params.length === 0 ) { if ( this.body.start > c + 4 ) { code.overwrite( c, this.body.start, '()=>' ); } } else if ( this.params.length === 1 ) { this.params[0].minify( code, chars ); if ( this.params[0].type === 'Identifier' ) { // remove parens if ( this.async ) { code.overwrite( c, this.params[0].start, ' ' ); } else { code.remove( c, this.params[0].start ); } if ( this.body.start > this.params[0].end + 2 ) { code.overwrite( this.params[0].end, this.body.start, '=>' ); } } else { if ( this.params[0].start > c + 1 ) { code.remove( c + 1, this.params[0].start ); } if ( this.body.start > this.params[0].end + 3 ) { code.overwrite( this.params[0].end, this.body.start, ')=>' ); } } } else { this.params.forEach( function ( param, i ) { param.minify( code, chars ); if ( param.start > c + 1 ) { code.overwrite( c, param.start, i ? ',' : '(' ); } c = param.end; }); if ( this.body.start > c + 3 ) { code.overwrite( c, this.body.start, ')=>' ); } } this.body.minify( code, chars ); }; return ArrowFunctionExpression; }(Node)); var commutative = {}; // we exclude + because it's not commutative when it's // operating on strings for ( var i = 0, list = '*&^|'; i < list.length; i += 1 ) { var operator = list[i]; commutative[ operator ] = true; } var collapsibleOperators = '** * / % + - << >> >>> & ^ |'.split( ' ' ); var AssignmentExpression = (function (Node$$1) { function AssignmentExpression () { Node$$1.apply(this, arguments); } if ( Node$$1 ) AssignmentExpression.__proto__ = Node$$1; AssignmentExpression.prototype = Object.create( Node$$1 && Node$$1.prototype ); AssignmentExpression.prototype.constructor = AssignmentExpression; AssignmentExpression.prototype.getLeftHandSide = function getLeftHandSide () { return this.left.getLeftHandSide(); }; AssignmentExpression.prototype.getPrecedence = function getPrecedence () { return 3; }; AssignmentExpression.prototype.initialise = function initialise ( program, scope ) { if ( this.left.type === 'Identifier' ) { var declaration = scope.findDeclaration( this.left.name ); if ( declaration && declaration.kind === 'const' ) { throw new CompileError( this.left, ((this.left.name) + " is read-only") ); } } Node$$1.prototype.initialise.call( this, program, scope ); }; AssignmentExpression.prototype.minify = function minify ( code, chars ) { if ( this.right.start > this.left.end + this.operator.length ) { code.overwrite( this.left.end, this.right.start, this.operator ); } // special case – `a = a + 1` -> `a += 1` if ( this.operator === '=' && this.left.type === 'Identifier' && this.right.type === 'BinaryExpression' && ~collapsibleOperators.indexOf( this.right.operator ) ) { if ( this.right.left.type === 'Identifier' && ( this.right.left.name === this.left.name ) ) { code.appendLeft( this.left.end, this.right.operator ); code.remove( this.right.start, this.right.right.start ); this.right.right.minify( code, chars ); return; } // addition and multiplication if ( commutative[ this.right.operator ] && this.right.right.type === 'Identifier' && ( this.right.right.name === this.left.name ) ) { code.appendLeft( this.left.end, this.right.operator ); code.remove( this.right.left.end, this.right.end ); this.right.left.minify( code, chars ); return; } } Node$$1.prototype.minify.call( this, code, chars ); }; return AssignmentExpression; }(Node)); function isNegativeZero ( num ) { return num === 0 && ( 1 / num < 0 ); } // TODO if string, determine which quotes to use // TODO if number, determine whether to use e notation function stringify ( value ) { if ( typeof value === 'function' ) { return null; } if ( value !== value ) { return 'NaN'; } if ( value === Infinity ) { return '1/0'; } if ( value === -Infinity ) { return '-1/0'; } if ( value === true ) { return '!0'; } if ( value === false ) { return '!1'; } if ( value === undefined ) { return 'void 0'; } if ( isNegativeZero( value ) ) { return '-0'; } if ( typeof value === 'number' ) { var str = String( value ).replace( /^(-)?0\./, '$1.' ); var exponential = value.toExponential().replace( 'e+', 'e' ); return exponential.length < str.length ? exponential : str; } return JSON.stringify( value ) .replace( /\u2028/g, '\\u2028' ) .replace( /\u2029/g, '\\u2029' ); } function getValuePrecedence ( value ) { if ( value === true || value === false || value === undefined ) { return 16; } // unary operator — !0, !1, void 0 if ( typeof value === 'number' ) { if ( value === Infinity || value === -Infinity ) { return 14; } // division — 1/0, -1/0 if ( value < 0 || isNegativeZero( value ) ) { return 16; } } return 21; } var calculators = { '**' : function ( a, b ) { return Math.pow( a, b ); }, '*' : function ( a, b ) { return a * b; }, '/' : function ( a, b ) { return a / b; }, '%' : function ( a, b ) { return a % b; }, '+' : function ( a, b ) { return a + b; }, '-' : function ( a, b ) { return a - b; }, '<<' : function ( a, b ) { return a << b; }, '>>' : function ( a, b ) { return a >> b; }, '>>>': function ( a, b ) { return a >>> b; }, '<' : function ( a, b ) { return a < b; }, '<=' : function ( a, b ) { return a <= b; }, '>' : function ( a, b ) { return a > b; }, '>=' : function ( a, b ) { return a >= b; }, '==' : function ( a, b ) { return a == b; }, '!=' : function ( a, b ) { return a != b; }, '===': function ( a, b ) { return a === b; }, '!==': function ( a, b ) { return a !== b; }, '&' : function ( a, b ) { return a & b; }, '^' : function ( a, b ) { return a ^ b; }, '|' : function ( a, b ) { return a | b; }, in : function ( a, b ) { return a in b; }, instanceof: function ( a, b ) { return a instanceof b; } }; var binaryExpressionPrecedence = {}; [ [ 7, '|' ], [ 8, '^' ], [ 9, '&' ], [ 10, '!== === != ==' ], [ 11, 'instanceof in >= > <= <' ], [ 12, '>>> >> <<' ], [ 13, '- +' ], [ 14, '% / *' ], [ 15, '**' ] ].forEach( function (ref) { var precedence = ref[0]; var operators = ref[1]; operators.split( ' ' ).forEach( function (operator) { return binaryExpressionPrecedence[ operator ] = precedence; } ); }); var BinaryExpression = (function (Node$$1) { function BinaryExpression () { Node$$1.apply(this, arguments); } if ( Node$$1 ) BinaryExpression.__proto__ = Node$$1; BinaryExpression.prototype = Object.create( Node$$1 && Node$$1.prototype ); BinaryExpression.prototype.constructor = BinaryExpression; BinaryExpression.prototype.getLeftHandSide = function getLeftHandSide () { return this.left.getLeftHandSide(); }; BinaryExpression.prototype.getPrecedence = function getPrecedence () { var value = this.getValue(); return value === UNKNOWN ? binaryExpressionPrecedence[ this.operator ] : getValuePrecedence( value ); }; // TODO `program.addWord( stringify( this.getValue() ) )`... BinaryExpression.prototype.getValue = function getValue () { var left = this.left.getValue(); var right = this.right.getValue(); if ( left === UNKNOWN || right === UNKNOWN ) { return UNKNOWN; } return calculators[ this.operator ]( left, right ); }; BinaryExpression.prototype.minify = function minify ( code, chars ) { var value = this.getValue(); if ( value !== UNKNOWN ) { code.overwrite( this.start, this.end, stringify( value ) ); } else { var operator = this.operator; if ( code.original[ this.right.getLeftHandSide().start ] === operator ) { // prevent e.g. `1 - --t` becoming 1---t operator = operator + " "; } else if ( /\w/.test( this.operator ) ) { // `foo in bar`, not `fooinbar` operator = " " + operator + " "; } code.overwrite( this.left.end, this.right.start, operator ); Node$$1.prototype.minify.call( this, code, chars ); } }; return BinaryExpression; }(Node)); var safeFunctions = [ // TODO this list is possibly a bit arbitrary. Also *technically* // unsafe, though I'm inclined to wait for examples of it causing // breakage in the wild Array.prototype.concat, Array.prototype.indexOf, Array.prototype.join, Array.prototype.lastIndexOf, Array.prototype.reverse, Array.prototype.slice, Array.prototype.sort, Array.prototype.toString, String.fromCharCode, String.fromCodePoint, String.prototype.charAt, String.prototype.charCodeAt, String.prototype.codePointAt, String.prototype.concat, // WARNING! https://github.com/jquery/jquery/pull/473 String.prototype.endsWith, String.prototype.includes, String.prototype.indexOf, String.prototype.lastIndexOf, String.prototype.slice, String.prototype.startsWith, String.prototype.substr, String.prototype.substring, String.prototype.toLowerCase, String.prototype.toString, String.prototype.toUpperCase, String.prototype.trim, String.prototype.trimLeft, String.prototype.trimRight, String.prototype.valueOf // TODO others... ]; var CallExpression = (function (Node$$1) { function CallExpression () { Node$$1.apply(this, arguments); } if ( Node$$1 ) CallExpression.__proto__ = Node$$1; CallExpression.prototype = Object.create( Node$$1 && Node$$1.prototype ); CallExpression.prototype.constructor = CallExpression; CallExpression.prototype.getLeftHandSide = function getLeftHandSide () { return this.callee.getLeftHandSide(); }; CallExpression.prototype.getPrecedence = function getPrecedence () { var value = this.getValue(); if ( value === UNKNOWN ) { // function expressions are a special (annoying) case var node = this.callee; while ( node.type === 'ParenthesizedExpression' ) { node = node.expression; } if ( /FunctionExpression/.test( node.getLeftHandSide().type ) ) { if ( this.parent.type !== 'ExpressionStatement' ) { return 0; } } return 18; } else { return getValuePrecedence( value ); } }; CallExpression.prototype.getValue = function getValue () { var this$1 = this; if ( this.callee.type !== 'MemberExpression' || this.callee.property.computed ) { return UNKNOWN; } var contextValue = this.callee.object.getValue(); if ( contextValue === UNKNOWN ) { return UNKNOWN; } var calleeValue = contextValue[ this.callee.property.name ]; if ( typeof calleeValue !== 'function' ) { return UNKNOWN; } if ( !~safeFunctions.indexOf( calleeValue ) ) { return UNKNOWN; } var argumentValues = new Array( this.arguments.length ); for ( var i = 0; i < this.arguments.length; i += 1 ) { var argument = this$1.arguments[i]; if ( argument ) { var value = argument.getValue(); if ( value === UNKNOWN ) { return UNKNOWN; } argumentValues[i] = value; } } return calleeValue.apply( contextValue, argumentValues ); }; CallExpression.prototype.initialise = function initialise ( program, scope ) { if ( this.callee.type === 'Identifier' && this.callee.name === 'eval' && !scope.contains( 'eval' ) ) { if ( this.program.options.allowDangerousEval ) { scope.deopt(); } else { this.error( 'Use of direct eval prevents effective minification and can introduce security vulnerabilities. Use `allowDangerousEval: true` if you know what you\'re doing' ); } } Node$$1.prototype.initialise.call( this, program, scope ); }; CallExpression.prototype.minify = function minify ( code, chars ) { var this$1 = this; var value = this.getValue(); if ( value !== UNKNOWN ) { var str = stringify( value ); if ( str !== null ) { code.overwrite( this.start, this.end, str ); return; } } if ( this.arguments.length ) { var lastNode = this.callee; for ( var i = 0; i < this.arguments.length; i += 1 ) { var argument = this$1.arguments[i]; if ( argument.start > lastNode.end + 1 ) { code.overwrite( lastNode.end, argument.start, i ? ',' : '(' ); } lastNode = argument; } if ( this.end > lastNode.end + 1 ) { code.overwrite( lastNode.end, this.end, ')' ); } } else if ( this.end > this.callee.end + 2 ) { code.overwrite( this.callee.end, this.end, '()' ); } Node$$1.prototype.minify.call( this, code, chars ); }; return CallExpression; }(Node)); var CatchClause = (function (Node$$1) { function CatchClause () { Node$$1.apply(this, arguments); } if ( Node$$1 ) CatchClause.__proto__ = Node$$1; CatchClause.prototype = Object.create( Node$$1 && Node$$1.prototype ); CatchClause.prototype.constructor = CatchClause; CatchClause.prototype.attachScope = function attachScope ( program, parent ) { var this$1 = this; this.scope = new Scope({ block: true, parent: parent }); extractNames( this.param ).forEach( function (node) { this$1.scope.addDeclaration( node, 'param' ); }); for ( var i = 0; i < this.body.body.length; i += 1 ) { this$1.body.body[i].attachScope( program, this$1.scope ); } if ( this.finalizer ) { this.finalizer.attachScope( program, this.scope ); } }; CatchClause.prototype.initialise = function initialise ( program ) { program.addWord( 'catch' ); Node$$1.prototype.initialise.call( this, program, this.scope ); }; CatchClause.prototype.minify = function minify ( code, chars ) { this.scope.mangle( code, chars ); if ( this.param.start > this.start + 6 ) { code.overwrite( this.start + 5, this.param.start, '(' ); } if ( this.body.start > this.param.end + 1 ) { code.overwrite( this.param.end, this.body.start, ')' ); } Node$$1.prototype.minify.call( this, code, chars ); }; return CatchClause; }(Node)); var ClassBody = (function (Node$$1) { function ClassBody () { Node$$1.apply(this, arguments); } if ( Node$$1 ) ClassBody.__proto__ = Node$$1; ClassBody.prototype = Object.create( Node$$1 && Node$$1.prototype ); ClassBody.prototype.constructor = ClassBody; ClassBody.prototype.attachScope = function attachScope ( program, parent ) { var this$1 = this; for ( var i = 0; i < this.body.length; i += 1 ) { this$1.body[i].attachScope( program, parent ); } }; ClassBody.prototype.minify = function minify ( code, chars ) { var this$1 = this; var c = this.start + 1; for ( var i = 0; i < this.body.length; i += 1 ) { var method = this$1.body[i]; if ( method.start > c ) { code.remove( c, method.start ); } method.minify( code, chars ); c = method.end; } if ( this.end > c + 1 ) { code.remove( c, this.end - 1 ); } }; return ClassBody; }(Node)); function shouldParenthesizeSuperclass ( node ) { while ( node.type === 'ParenthesizedExpression' ) { node = node.expression; } var value = node.getValue(); if ( value === UNKNOWN ) { return node.getPrecedence() < 18; } return ( value === true || value === false || value === undefined || isNegativeZero( value ) ); } var Class = (function (Node$$1) { function Class () { Node$$1.apply(this, arguments); } if ( Node$$1 ) Class.__proto__ = Node$$1; Class.prototype = Object.create( Node$$1 && Node$$1.prototype ); Class.prototype.constructor = Class; Class.prototype.initialise = function initialise ( program, scope ) { program.addWord( 'class' ); Node$$1.prototype.initialise.call( this, program, scope ); }; Class.prototype.minify = function minify ( code, chars ) { var c = this.start + 5; if ( this.id ) { if ( this.id.start > c + 1 ) { code.remove( c + 1, this.id.start ); } c = this.id.end; } if ( this.superClass ) { // edge case if ( shouldParenthesizeSuperclass( this.superClass ) ) { code.overwrite( c, this.superClass.start, ' extends(' ); code.prependRight( this.body.start, ')' ); } else if ( this.superClass.start > c + 8 ) { code.overwrite( c, this.superClass.start, ' extends ' ); } c = this.superClass.end; } if ( this.body.start > c ) { code.remove( c, this.body.start ); } Node$$1.prototype.minify.call( this, code, chars ); }; return Class; }(Node)); var ClassDeclaration = (function (Class$$1) { function ClassDeclaration () { Class$$1.apply(this, arguments); } if ( Class$$1 ) ClassDeclaration.__proto__ = Class$$1; ClassDeclaration.prototype = Object.create( Class$$1 && Class$$1.prototype ); ClassDeclaration.prototype.constructor = ClassDeclaration; ClassDeclaration.prototype.activate = function activate () { if ( this.activated ) { return; } this.activated = true; this.skip = false; Class$$1.prototype.initialise.call( this, this.program, this.scope ); }; ClassDeclaration.prototype.attachScope = function attachScope ( program, scope ) { this.program = program; this.scope = scope; this.id.declaration = this; this.name = this.id.name; // TODO what is this used for? scope.addDeclaration( this.id, 'class' ); this.id.attachScope( program, this.scope ); if ( this.superClass ) { this.superClass.attachScope( program, this.scope ); } this.body.attachScope( program, scope ); }; ClassDeclaration.prototype.initialise = function initialise ( program, scope ) { if ( scope.parent ) { // noop — we wait for this declaration to be activated } else { Class$$1.prototype.initialise.call( this, program, scope ); } }; return ClassDeclaration; }(Class)); var ClassExpression = (function (Class$$1) { function ClassExpression () { Class$$1.apply(this, arguments); } if ( Class$$1 ) ClassExpression.__proto__ = Class$$1; ClassExpression.prototype = Object.create( Class$$1 && Class$$1.prototype ); ClassExpression.prototype.constructor = ClassExpression; ClassExpression.prototype.attachScope = function attachScope ( program, parent ) { this.scope = new Scope({ block: true, parent: parent }); if ( this.id ) { this.id.attachScope( program, this.scope ); } if ( this.superClass ) { this.superClass.attachScope( program, this.scope ); } this.body.attachScope( program, this.scope ); }; ClassExpression.prototype.initialise = function initialise ( program, scope ) { if ( this.id ) { this.id.declaration = this; // function expression IDs belong to the child scope... this.scope.addDeclaration( this.id, 'class' ); this.scope.addReference( this.id ); } Class$$1.prototype.initialise.call( this, program, scope ); }; ClassExpression.prototype.minify = function minify ( code, chars ) { this.scope.mangle( code, chars ); Class$$1.prototype.minify.call( this, code, chars ); }; return ClassExpression; }(Class)); var ConditionalExpression = (function (Node$$1) { function ConditionalExpression () { Node$$1.apply(this, arguments); } if ( Node$$1 ) ConditionalExpression.__proto__ = Node$$1; ConditionalExpression.prototype = Object.create( Node$$1 && Node$$1.prototype ); ConditionalExpression.prototype.constructor = ConditionalExpression; ConditionalExpression.prototype.getLeftHandSide = function getLeftHandSide () { var testValue = this.test.getValue(); var node = testValue === UNKNOWN ? this.test : ( testValue ? this.consequent : this.alternate ); return node.getLeftHandSide(); }; ConditionalExpression.prototype.getPrecedence = function getPrecedence () { var testValue = this.test.getValue(); return testValue === UNKNOWN ? 4 : ( testValue ? this.consequent : this.alternate ).getPrecedence(); }; ConditionalExpression.prototype.getRightHandSide = function getRightHandSide () { var testValue = this.test.getValue(); var node = testValue === UNKNOWN ? this.alternate : ( testValue ? this.alternate : this.consequent ); return node.getRightHandSide(); }; ConditionalExpression.prototype.getValue = function getValue () { var testValue = this.test.getValue(); var consequentValue = this.consequent.getValue(); var alternateValue = this.alternate.getValue(); if ( testValue === UNKNOWN || consequentValue === UNKNOWN || alternateValue === UNKNOWN ) { return UNKNOWN; } return testValue ? consequentValue : alternateValue; }; ConditionalExpression.prototype.initialise = function initialise ( program, scope ) { var testValue = this.test.getValue(); if ( testValue === UNKNOWN ) { Node$$1.prototype.initialise.call( this, program, scope ); } else if ( testValue ) { this.consequent.initialise( program, scope ); } else { this.alternate.initialise( program, scope ); } }; ConditionalExpression.prototype.minify = function minify ( code, chars ) { var testValue = this.test.getValue(); // TODO rewrite `!a ? b() : c()` as `a ? c() : b()` if ( testValue === UNKNOWN ) { // remove whitespace if ( this.consequent.start > this.test.end + 1 ) { code.overwrite( this.test.end, this.consequent.start, '?' ); } if ( this.alternate.start > this.consequent.end + 1 ) { code.overwrite( this.consequent.end, this.alternate.start, ':' ); } Node$$1.prototype.minify.call( this, code, chars ); } else if ( testValue ) { // remove test and alternate code.remove( this.start, this.consequent.start ); code.remove( this.consequent.end, this.end ); this.consequent.minify( code, chars ); } else { // remove test and consequent code.remove( this.start, this.alternate.start ); this.alternate.minify( code, chars ); } }; return ConditionalExpression; }(Node)); var DoWhileStatement = (function (Node$$1) { function DoWhileStatement () { Node$$1.apply(this, arguments); } if ( Node$$1 ) DoWhileStatement.__proto__ = Node$$1; DoWhileStatement.prototype = Object.create( Node$$1 && Node$$1.prototype ); DoWhileStatement.prototype.constructor = DoWhileStatement; DoWhileStatement.prototype.initialise = function initialise ( program, scope ) { program.addWord( 'dowhile' ); Node$$1.prototype.initialise.call( this, program, scope ); }; DoWhileStatement.prototype.minify = function minify ( code, chars ) { // special case if ( this.body.isEmpty() ) { code.overwrite( this.start + 2, this.test.start, ';while(' ); } else { this.body.minify( code, chars ); if ( this.body.type === 'BlockStatement' ) { code.remove( this.start + 2, this.body.start ); code.overwrite( this.body.end, this.test.start, 'while(' ); } else { if ( this.body.start > this.start + 2 ) { code.remove( this.start + 2, this.body.start ); } this.body.prepend( code, '{' ); var c = this.body.end; while ( code.original[ c - 1 ] === ';' ) { c -= 1; } code.overwrite( c, this.test.start, '}while(' ); } } if ( this.end > this.test.end + 1 ) { var c$1 = this.end; while ( code.original[ c$1 - 1 ] === ';' ) { c$1 -= 1; } code.overwrite( this.test.end, c$1, ')' ); } this.test.minify( code, chars ); }; return DoWhileStatement; }(Node)); var EmptyStatement = (function (Node$$1) { function EmptyStatement () { Node$$1.apply(this, arguments); } if ( Node$$1 ) EmptyStatement.__proto__ = Node$$1; EmptyStatement.prototype = Object.create( Node$$1 && Node$$1.prototype ); EmptyStatement.prototype.constructor = EmptyStatement; EmptyStatement.prototype.initialise = function initialise () { // noop. this prevents Node#initialise from // 'de-skipping' this node }; return EmptyStatement; }(Node)); var ExpressionStatement = (function (Node$$1) { function ExpressionStatement () { Node$$1.apply(this, arguments); } if ( Node$$1 ) ExpressionStatement.__proto__ = Node$$1; ExpressionStatement.prototype = Object.create( Node$$1 && Node$$1.prototype ); ExpressionStatement.prototype.constructor = ExpressionStatement; ExpressionStatement.prototype.canSequentialise = function canSequentialise () { return true; }; ExpressionStatement.prototype.getLeftHandSide = function getLeftHandSide () { return this.expression.getLeftHandSide(); }; ExpressionStatement.prototype.getPrecedence = function getPrecedence () { return this.expression.getPrecedence(); }; ExpressionStatement.prototype.getRightHandSide = function getRightHandSide () { return this.expression.getRightHandSide(); }; ExpressionStatement.prototype.initialise = function initialise ( program, scope ) { if ( this.expression.type === 'Literal' || this.expression.getValue() !== UNKNOWN ) { // remove side-effect-free statements (TODO others, not just literals)... return; } Node$$1.prototype.initialise.call( this, program, scope ); }; return ExpressionStatement; }(Node)); var LoopStatement = (function (Node$$1) { function LoopStatement () { Node$$1.apply(this, arguments); } if ( Node$$1 ) LoopStatement.__proto__ = Node$$1; LoopStatement.prototype = Object.create( Node$$1 && Node$$1.prototype ); LoopStatement.prototype.constructor = LoopStatement; LoopStatement.prototype.attachScope = function attachScope ( program, parent ) { if ( this.hasVariableDeclaration() ) { this.scope = new Scope({ block: true, parent: parent }); Node$$1.prototype.attachScope.call( this, program, this.scope ); } else { Node$$1.prototype.attachScope.call( this, program, parent ); } }; LoopStatement.prototype.initialise = function initialise ( program, scope ) { program.addWord( 'for' ); if ( this.type === 'ForInStatement' ) { program.addWord( 'in' ); } else if ( this.type === 'ForOfStatement' ) { program.addWord( 'of' ); } Node$$1.prototype.initialise.call( this, program, this.scope || scope ); }; LoopStatement.prototype.minify = function minify ( code, chars ) { if ( this.scope ) { this.scope.mangle( code, chars ); } // special case — empty body if ( this.body.isEmpty() ) { code.appendLeft( this.body.start, ';' ); code.remove( this.body.start, this.body.end ); } else { this.body.minify( code, chars ); } }; return LoopStatement; }(Node)); var ForStatement = (function (LoopStatement$$1) { function ForStatement () { LoopStatement$$1.apply(this, arguments); } if ( LoopStatement$$1 ) ForStatement.__proto__ = LoopStatement$$1; ForStatement.prototype = Object.create( LoopStatement$$1 && LoopStatement$$1.prototype ); ForStatement.prototype.constructor = ForStatement; ForStatement.prototype.getRightHandSide = function getRightHandSide () { return this.body.getRightHandSide(); }; ForStatement.prototype.hasVariableDeclaration = function hasVariableDeclaration () { return this.init && this.init.type === 'VariableDeclaration'; }; ForStatement.prototype.minify = function minify ( code, chars ) { var this$1 = this; var c = this.start + 3; var replacement = '('; [ this.init, this.test, this.update ].forEach( function ( statement, i ) { if ( statement && ( !statement.skip || statement === this$1.test ) ) { if ( statement.start > c + replacement.length ) { code.overwrite( c, statement.start, replacement ); } statement.minify( code, chars ); c = statement.end; replacement = ''; } replacement += i === 2 ? ')' : ';'; }); if ( this.body.start > c + replacement.length ) { code.overwrite( c, this.body.start, replacement ); } LoopStatement$$1.prototype.minify.call( this, code, chars ); }; return ForStatement; }(LoopStatement)); var ForInOfStatement = (function (LoopStatement$$1) { function ForInOfStatement () { LoopStatement$$1.apply(this, arguments); } if ( LoopStatement$$1 ) ForInOfStatement.__proto__ = LoopStatement$$1; ForInOfStatement.prototype = Object.create( LoopStatement$$1 && LoopStatement$$1.prototype ); ForInOfStatement.prototype.constructor = ForInOfStatement; ForInOfStatement.prototype.getRightHandSide = function getRightHandSide () { return this.body.getRightHandSide(); }; ForInOfStatement.prototype.hasVariableDeclaration = function hasVariableDeclaration () { return this.left.type === 'VariableDeclaration'; }; ForInOfStatement.prototype.minify = function minify ( code, chars ) { if ( this.left.start > this.start + 4 ) { code.overwrite( this.start + 3, this.left.start, '(' ); } if ( this.right.start > this.left.end + 4 ) { code.overwrite( this.left.end, this.right.start, ' in ' ); } if ( this.body.start > this.right.end + 1 ) { code.overwrite( this.right.end, this.body.start, ')' ); } this.left.minify( code, chars ); this.right.minify( code, chars ); LoopStatement$$1.prototype.minify.call( this, code, chars ); }; return ForInOfStatement; }(LoopStatement)); function hasFunctionKeyword ( node, parent ) { if ( node === parent.value ) { if ( parent.type === 'MethodDefinition' ) { return false; } if ( parent.type === 'Property' ) { if ( parent.method ) { return false; } if ( parent.kind === 'set' || parent.kind === 'get' ) { return false; } } } return true; } function keepId ( node ) { if ( !node.id ) { return false; } if ( node.type === 'FunctionDeclaration' ) { return true; } // if function expression ID is shadowed, or is not referenced (other than // by the function expression itself), remove it return !node.shadowed && node.scope.declarations[ node.id.name ].instances.length > 1; } var FunctionNode = (function (Node$$1) { function FunctionNode () { Node$$1.apply(this, arguments); } if ( Node$$1 ) FunctionNode.__proto__ = Node$$1; FunctionNode.prototype = Object.create( Node$$1 && Node$$1.prototype ); FunctionNode.prototype.constructor = FunctionNode; FunctionNode.prototype.attachScope = function attachScope ( program, parent ) { var this$1 = this; this.program = program; this.scope = new Scope({ block: false, parent: parent }); if ( this.id ) { this.id.declaration = this; // function expression IDs belong to the child scope... if ( this.type === 'FunctionExpression' ) { this.scope.addDeclaration( this.id, this.type ); this.scope.addReference( this.id ); } else { parent.addDeclaration( this.id, this.type ); } } this.params.forEach( function (param) { param.attachScope( program, this$1.scope ); extractNames( param ).forEach( function (node) { node.declaration = this$1; this$1.scope.addDeclaration( node, 'param' ); }); }); this.body.attachScope( program, this.scope ); }; FunctionNode.prototype.findVarDeclarations = function findVarDeclarations () { // noop }; // TODO `program.addWord('async')` if necessary FunctionNode.prototype.minify = function minify ( code, chars ) { var this$1 = this; var c = this.start; if ( hasFunctionKeyword( this, this.parent ) ) { // TODO this could probably be simpler var shouldKeepId = keepId( this ); if ( shouldKeepId ) { c = this.id.start; if ( this.async ) { if ( c > this.start + 15 ) { code.overwrite( this.start + 6, c, this.generator ? 'function*' : 'function ' ); } } else { if ( c > this.start + 9 ) { code.overwrite( this.start + 8, c, this.generator ? '*' : ' ' ); } } c = this.id.end; } else { while ( code.original[c] !== '(' ) { c += 1; } if ( this.async ) { var replacement = this.generator ? 'function*' : 'function'; if ( c > this.start + 6 + replacement.length ) { code.overwrite( this.start + 6, c, replacement ); } } else { var replacement$1 = this.generator ? '*' : ''; if ( c > this.start + 8 + replacement$1.length ) { code.overwrite( this.start + 8, c, replacement$1 ); } } } } if ( this.params.length ) { for ( var i = 0; i < this.params.length; i += 1 ) { var param = this$1.params[i]; param.minify( code, chars ); if ( param.start > c + 1 ) { code.overwrite( c, param.start, i ? ',' : '(' ); } c = param.end; } if ( this.end > c + 1 ) { code.overwrite( c, this.body.start, ')' ); } } else if ( this.body.start > c + 2 ) { code.overwrite( c, this.body.start, "()" ); } this.body.minify( code, chars ); }; return FunctionNode; }(Node)); var FunctionDeclaration = (function (FunctionNode$$1) { function FunctionDeclaration () { FunctionNode$$1.apply(this, arguments); } if ( FunctionNode$$1 ) FunctionDeclaration.__proto__ = FunctionNode$$1; FunctionDeclaration.prototype = Object.create( FunctionNode$$1 && FunctionNode$$1.prototype ); FunctionDeclaration.prototype.constructor = FunctionDeclaration; FunctionDeclaration.prototype.activate = function activate () { var this$1 = this; if ( this.activated ) { return; } this.activated = true; this.skip = false; this.program.addWord( 'function' ); if ( this.id ) { this.id.initialise( this.program, this.scope.parent ); } this.params.forEach( function (param) { param.initialise( this$1.program, this$1.scope ); }); this.body.initialise( this.program, this.scope ); }; FunctionDeclaration.prototype.initialise = function initialise ( program, scope ) { if ( scope.parent ) { // noop — we wait for this declaration to be activated } else { this.activate( program ); } }; return FunctionDeclaration; }(FunctionNode)); var FunctionExpression = (function (FunctionNode$$1) { function FunctionExpression () { FunctionNode$$1.apply(this, arguments); } if ( FunctionNode$$1 ) FunctionExpression.__proto__ = FunctionNode$$1; FunctionExpression.prototype = Object.create( FunctionNode$$1 && FunctionNode$$1.prototype ); FunctionExpression.prototype.constructor = FunctionExpression; FunctionExpression.prototype.getPrecedence = function getPrecedence () { return 0; }; FunctionExpression.prototype.initialise = function initialise ( program ) { program.addWord( 'function' ); // TODO only if has function keyword FunctionNode$$1.prototype.initialise.call( this, program, this.scope ); }; return FunctionExpression; }(FunctionNode)); var Identifier = (function (Node$$1) { function Identifier () { Node$$1.apply(this, arguments); } if ( Node$$1