-
-
Notifications
You must be signed in to change notification settings - Fork 225
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split simplify plugin to multiple files (#624)
* Split Simplify to multiple files - IfStatement * Extract Conditional Expression transformations to separate file * Move Logical Expression patterns to separate file * Move AssignmentExpression to separate file
- Loading branch information
Showing
6 changed files
with
710 additions
and
623 deletions.
There are no files selected for viewing
125 changes: 125 additions & 0 deletions
125
packages/babel-plugin-minify-simplify/src/assignment-expression.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
"use strict"; | ||
|
||
const operators = new Set([ | ||
"+", | ||
"-", | ||
"*", | ||
"%", | ||
"<<", | ||
">>", | ||
">>>", | ||
"&", | ||
"|", | ||
"^", | ||
"/", | ||
"**" | ||
]); | ||
|
||
const updateOperators = new Set(["+", "-"]); | ||
|
||
module.exports = t => { | ||
function simplify(path) { | ||
const rightExpr = path.get("right"); | ||
const leftExpr = path.get("left"); | ||
|
||
if (path.node.operator !== "=") { | ||
return; | ||
} | ||
|
||
const canBeUpdateExpression = | ||
rightExpr.get("right").isNumericLiteral() && | ||
rightExpr.get("right").node.value === 1 && | ||
updateOperators.has(rightExpr.node.operator); | ||
|
||
if (leftExpr.isMemberExpression()) { | ||
const leftPropNames = getPropNames(leftExpr); | ||
const rightPropNames = getPropNames(rightExpr.get("left")); | ||
|
||
if ( | ||
!leftPropNames || | ||
leftPropNames.indexOf(undefined) > -1 || | ||
!rightPropNames || | ||
rightPropNames.indexOf(undefined) > -1 || | ||
!operators.has(rightExpr.node.operator) || | ||
!areArraysEqual(leftPropNames, rightPropNames) | ||
) { | ||
return; | ||
} | ||
} else { | ||
if ( | ||
!rightExpr.isBinaryExpression() || | ||
!operators.has(rightExpr.node.operator) || | ||
leftExpr.node.name !== rightExpr.node.left.name | ||
) { | ||
return; | ||
} | ||
} | ||
|
||
let newExpression; | ||
|
||
// special case x=x+1 --> ++x | ||
if (canBeUpdateExpression) { | ||
newExpression = t.updateExpression( | ||
rightExpr.node.operator + rightExpr.node.operator, | ||
t.clone(leftExpr.node), | ||
true /* prefix */ | ||
); | ||
} else { | ||
newExpression = t.assignmentExpression( | ||
rightExpr.node.operator + "=", | ||
t.clone(leftExpr.node), | ||
t.clone(rightExpr.node.right) | ||
); | ||
} | ||
|
||
path.replaceWith(newExpression); | ||
} | ||
|
||
return { | ||
simplify | ||
}; | ||
}; | ||
|
||
function areArraysEqual(arr1, arr2) { | ||
return arr1.every((value, index) => { | ||
return String(value) === String(arr2[index]); | ||
}); | ||
} | ||
|
||
function getPropNames(path) { | ||
if (!path.isMemberExpression()) { | ||
return; | ||
} | ||
|
||
let obj = path.get("object"); | ||
|
||
const prop = path.get("property"); | ||
const propNames = [getName(prop.node)]; | ||
|
||
while (obj.type === "MemberExpression") { | ||
const node = obj.get("property").node; | ||
if (node) { | ||
propNames.push(getName(node)); | ||
} | ||
obj = obj.get("object"); | ||
} | ||
propNames.push(getName(obj.node)); | ||
|
||
return propNames; | ||
} | ||
|
||
function getName(node) { | ||
if (node.type === "ThisExpression") { | ||
return "this"; | ||
} | ||
if (node.type === "Super") { | ||
return "super"; | ||
} | ||
if (node.type === "NullLiteral") { | ||
return "null"; | ||
} | ||
// augment identifiers so that they don't match | ||
// string/number literals | ||
// but still match against each other | ||
return node.name ? node.name + "_" : node.value /* Literal */; | ||
} |
53 changes: 53 additions & 0 deletions
53
packages/babel-plugin-minify-simplify/src/conditional-expression.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
const h = require("./helpers"); | ||
const PatternMatch = require("./pattern-match"); | ||
|
||
module.exports = t => { | ||
// small abstractions | ||
const not = node => t.unaryExpression("!", node); | ||
const notnot = node => not(not(node)); | ||
const or = (a, b) => t.logicalExpression("||", a, b); | ||
const and = (a, b) => t.logicalExpression("&&", a, b); | ||
|
||
function simplifyPatterns(path) { | ||
const test = path.get("test"); | ||
const consequent = path.get("consequent"); | ||
const alternate = path.get("alternate"); | ||
|
||
const { Expression: EX, LogicalExpression: LE } = h.typeSymbols(t); | ||
|
||
// Convention: | ||
// =============== | ||
// for each pattern [test, consequent, alternate, handler(expr, cons, alt)] | ||
const matcher = new PatternMatch([ | ||
[LE, true, false, e => e], | ||
[EX, true, false, e => notnot(e)], | ||
|
||
[EX, false, true, e => not(e)], | ||
|
||
[LE, true, EX, (e, c, a) => or(e, a)], | ||
[EX, true, EX, (e, c, a) => or(notnot(e), a)], | ||
|
||
[EX, false, EX, (e, c, a) => and(not(e), a)], | ||
|
||
[EX, EX, true, (e, c) => or(not(e), c)], | ||
|
||
[LE, EX, false, (e, c) => and(e, c)], | ||
[EX, EX, false, (e, c) => and(notnot(e), c)] | ||
]); | ||
|
||
const result = matcher.match( | ||
[test, consequent, alternate], | ||
h.isPatternMatchesPath(t) | ||
); | ||
|
||
if (result.match) { | ||
path.replaceWith( | ||
result.value(test.node, consequent.node, alternate.node) | ||
); | ||
} | ||
} | ||
|
||
return { | ||
simplifyPatterns | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
"use strict"; | ||
|
||
const VOID_0 = t => t.unaryExpression("void", t.numericLiteral(0), true); | ||
|
||
// Types as Symbols - for comparing types | ||
// init must be empty object - | ||
// computing this involves checking object.keys() to be of length 0 | ||
// skipped otherwise | ||
const types = {}; | ||
const typeSymbols = t => { | ||
// don't recompute | ||
if (Object.keys(types).length < 1) { | ||
t.TYPES.forEach(type => { | ||
types[type] = Symbol.for(type); | ||
}); | ||
} | ||
return types; | ||
}; | ||
|
||
const isNodeOfType = (t, node, typeSymbol) => | ||
typeof typeSymbol !== "symbol" | ||
? false | ||
: t["is" + Symbol.keyFor(typeSymbol)](node); | ||
|
||
const isPatternMatchesPath = t => | ||
function _isPatternMatchesPath(patternValue, inputPath) { | ||
if (Array.isArray(patternValue)) { | ||
for (let i = 0; i < patternValue.length; i++) { | ||
if (_isPatternMatchesPath(patternValue[i], inputPath)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
if (typeof patternValue === "function") { | ||
return patternValue(inputPath); | ||
} | ||
if (isNodeOfType(t, inputPath.node, patternValue)) return true; | ||
const evalResult = inputPath.evaluate(); | ||
if (!evalResult.confident || !inputPath.isPure()) return false; | ||
return evalResult.value === patternValue; | ||
}; | ||
|
||
module.exports = { | ||
VOID_0, | ||
// Types as Symbols | ||
typeSymbols, | ||
// This is required for resolving type aliases | ||
isNodeOfType, | ||
isPatternMatchesPath | ||
}; |
Oops, something went wrong.