-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add support for combining arguments
- Loading branch information
Showing
6 changed files
with
321 additions
and
9 deletions.
There are no files selected for viewing
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
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,95 @@ | ||
const t = require('@babel/types'); | ||
const compareNodes = require('./utils/compareNodes'); | ||
const helpers = require('./utils/helpers'); | ||
|
||
module.exports = args => { | ||
const [match, noMatch] = helpers.filterArray(args, item => { | ||
return t.isLogicalExpression(item) && helpers.isAllLogicalAndOperators(item); | ||
}); | ||
|
||
// Not enough items to optimize | ||
if (match.length < 2) return args; | ||
|
||
const operators = match.map(helpers.flattenLogicalOperator); | ||
|
||
const node = helpers.getMostFrequentNode(operators); | ||
const rootNode = combineOperators(operators, node); | ||
|
||
const newAST = convertToAST(rootNode); | ||
|
||
return [...noMatch, ...newAST]; | ||
|
||
function convertToAST(node) { | ||
if (node.type !== 'rootNode') { | ||
return node; | ||
} | ||
|
||
const result = []; | ||
|
||
if (node.child.type === 'rootNode') { | ||
const arr = t.arrayExpression(convertToAST(node.child)); | ||
result.push(t.logicalExpression('&&', node.node, arr)); | ||
} else { | ||
result.push(t.logicalExpression('&&', node.node, node.child)); | ||
} | ||
|
||
if (node.next !== undefined) { | ||
const r = convertToAST(node.next); | ||
if (r.push !== undefined) { | ||
result.push(...r); | ||
} else { | ||
result.push(r); | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
|
||
function combineOperators(operators, node) { | ||
const newNode = { | ||
type: 'rootNode', | ||
node: node, | ||
child: [], | ||
next: [], | ||
}; | ||
|
||
operators.forEach(row => { | ||
const filtered = row.filter(item => !compareNodes(item, node)); | ||
if (filtered.length === row.length) { | ||
newNode.next.push(row); | ||
} else { | ||
newNode.child.push(filtered); | ||
} | ||
}); | ||
|
||
newNode.next = checkSub(newNode.next); | ||
newNode.child = checkSub(newNode.child); | ||
|
||
return newNode; | ||
|
||
function checkSub(items) { | ||
if (items.length === 0) return undefined; | ||
if (items.length === 1) { | ||
const item = items[0]; | ||
|
||
if (item.length === 1) { | ||
return item[0]; | ||
} | ||
|
||
let result = t.logicalExpression('&&', item.shift(), item.shift()); | ||
while (item.length > 0) { | ||
result = t.logicalExpression('&&', result, item.shift()); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
const nextCheck = helpers.getMostFrequentNode(items); | ||
if (nextCheck !== null) { | ||
return combineOperators(items, nextCheck); | ||
} | ||
|
||
return t.arrayExpression(items.map(e => e[0])); | ||
} | ||
} | ||
}; |
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
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,40 @@ | ||
module.exports = function compareNodes(a, b) { | ||
if (typeof a !== typeof b) { | ||
return false; | ||
} | ||
|
||
if (typeof a !== 'object') { | ||
return a === b; | ||
} | ||
|
||
for (const key in a) { | ||
if (a.hasOwnProperty(key)) { | ||
// Ignore location data | ||
if (key === 'start' || key === 'end' || key === 'loc') { | ||
continue; | ||
} | ||
|
||
if (b.hasOwnProperty(key) === false) { | ||
return false; | ||
} | ||
|
||
if (typeof a[key] === 'object') { | ||
if (typeof b[key] !== 'object') { | ||
return false; | ||
} | ||
|
||
if (compareNodes(a[key], b[key]) === false) { | ||
return false; | ||
} | ||
|
||
continue; | ||
} | ||
|
||
if (a[key] !== b[key]) { | ||
return false; | ||
} | ||
} | ||
} | ||
|
||
return true; | ||
}; |
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,84 @@ | ||
const t = require('@babel/types'); | ||
const compareNodes = require('./compareNodes'); | ||
|
||
function flattenLogicalOperator(node) { | ||
if (t.isLogicalExpression(node)) { | ||
return [...flattenLogicalOperator(node.left), node.right]; | ||
} | ||
|
||
return [node]; | ||
} | ||
|
||
function isAllLogicalAndOperators(node) { | ||
if (t.isLogicalExpression(node)) { | ||
if (node.operator !== '&&') { | ||
return false; | ||
} | ||
|
||
return isAllLogicalAndOperators(node.left); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
function filterArray(array, callback) { | ||
const match = []; | ||
const noMatch = []; | ||
|
||
for (const item of array) { | ||
if (callback(item) === true) { | ||
match.push(item); | ||
} else { | ||
noMatch.push(item); | ||
} | ||
} | ||
|
||
return [match, noMatch]; | ||
} | ||
|
||
function getMostFrequentNode(operators) { | ||
let maxNode = null; | ||
let maxLength = 0; | ||
|
||
operators.forEach(row => { | ||
for (let x = 0; x < row.length - 1; x++) { | ||
const item = row[x]; | ||
let length = 0; | ||
|
||
operators.forEach(row2 => { | ||
for (let x2 = 0; x2 < row2.length - 1; x2++) { | ||
const item2 = row2[x2]; | ||
if (compareNodes(item, item2) === true) { | ||
length += item.end - item.start; | ||
} | ||
} | ||
}); | ||
|
||
if (length > maxLength) { | ||
maxNode = item; | ||
maxLength = length; | ||
} | ||
} | ||
}); | ||
|
||
return maxNode; | ||
} | ||
|
||
function stringify(object) { | ||
function replacer(name, val) { | ||
if (name === 'start' || name === 'loc' || name === 'end') { | ||
return undefined; | ||
} | ||
return val; | ||
} | ||
|
||
return JSON.stringify(object, replacer, 1); | ||
} | ||
|
||
module.exports = { | ||
flattenLogicalOperator, | ||
isAllLogicalAndOperators, | ||
filterArray, | ||
getMostFrequentNode, | ||
stringify, | ||
}; |
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