mirror of
https://github.com/S2-/minifyfromhtml.git
synced 2025-08-03 04:10:04 +02:00
add some packages
This commit is contained in:
96
node_modules/babel-plugin-minify-simplify/lib/assignment-expression.js
generated
vendored
Normal file
96
node_modules/babel-plugin-minify-simplify/lib/assignment-expression.js
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
"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 */
|
||||
;
|
||||
}
|
39
node_modules/babel-plugin-minify-simplify/lib/conditional-expression.js
generated
vendored
Normal file
39
node_modules/babel-plugin-minify-simplify/lib/conditional-expression.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
"use strict";
|
||||
|
||||
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 _h$typeSymbols = h.typeSymbols(t),
|
||||
EX = _h$typeSymbols.Expression; // Convention:
|
||||
// ===============
|
||||
// for each pattern [test, consequent, alternate, handler(expr, cons, alt)]
|
||||
|
||||
|
||||
const matcher = new PatternMatch([[EX, true, false, e => notnot(e)], [EX, false, true, e => not(e)], [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)], [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
|
||||
};
|
||||
};
|
54
node_modules/babel-plugin-minify-simplify/lib/helpers.js
generated
vendored
Normal file
54
node_modules/babel-plugin-minify-simplify/lib/helpers.js
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
"use strict";
|
||||
|
||||
const VOID_0 = t => t.unaryExpression("void", t.numericLiteral(0), true); // Types as Symbols - for comparing types
|
||||
|
||||
|
||||
const types = {}; // This is a test key which is used to avoid Object.keys check
|
||||
// Object.keys() check is really expensive
|
||||
// https://gist.github.com/vigneshshanmugam/c766550ecd02292dcdfbf0bf013b9d3d
|
||||
|
||||
const testKey = "Expression";
|
||||
|
||||
const typeSymbols = t => {
|
||||
// don't recompute
|
||||
if (types[testKey] !== undefined) {
|
||||
return types;
|
||||
}
|
||||
|
||||
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
|
||||
};
|
273
node_modules/babel-plugin-minify-simplify/lib/if-statement.js
generated
vendored
Normal file
273
node_modules/babel-plugin-minify-simplify/lib/if-statement.js
generated
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
"use strict";
|
||||
|
||||
const REPLACED = Symbol("replaced");
|
||||
|
||||
const h = require("./helpers");
|
||||
|
||||
module.exports = t => {
|
||||
function mergeNestedIfs(path) {
|
||||
const consequent = path.get("consequent");
|
||||
const alternate = path.get("alternate"); // not nested if
|
||||
|
||||
if (!consequent.isIfStatement()) return; // there are no alternate nodes in both the if statements (nested)
|
||||
|
||||
if (alternate.node || consequent.get("alternate").node) return;
|
||||
const test = path.get("test");
|
||||
test.replaceWith(t.logicalExpression("&&", test.node, consequent.get("test").node));
|
||||
consequent.replaceWith(t.clone(consequent.get("consequent").node));
|
||||
} // No alternate, make into a guarded expression
|
||||
|
||||
|
||||
function toGuardedExpression(path) {
|
||||
const node = path.node;
|
||||
|
||||
if (node.consequent && !node.alternate && node.consequent.type === "ExpressionStatement") {
|
||||
let op = "&&";
|
||||
|
||||
if (t.isUnaryExpression(node.test, {
|
||||
operator: "!"
|
||||
})) {
|
||||
node.test = node.test.argument;
|
||||
op = "||";
|
||||
}
|
||||
|
||||
path.replaceWith(t.expressionStatement(t.logicalExpression(op, node.test, node.consequent.expression)));
|
||||
return REPLACED;
|
||||
}
|
||||
} // both consequent and alternate are expressions, turn into ternary
|
||||
|
||||
|
||||
function toTernary(path) {
|
||||
const node = path.node;
|
||||
|
||||
if (t.isExpressionStatement(node.consequent) && t.isExpressionStatement(node.alternate)) {
|
||||
path.replaceWith(t.conditionalExpression(node.test, node.consequent.expression, node.alternate.expression));
|
||||
return REPLACED;
|
||||
}
|
||||
} // consequent and alternate are return -- conditional.
|
||||
|
||||
|
||||
function toConditional(path) {
|
||||
const node = path.node;
|
||||
|
||||
if (t.isReturnStatement(node.consequent) && t.isReturnStatement(node.alternate)) {
|
||||
if (!node.consequent.argument && !node.alternate.argument) {
|
||||
path.replaceWith(t.expressionStatement(node.test));
|
||||
return REPLACED;
|
||||
}
|
||||
|
||||
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, node.consequent.argument || h.VOID_0(t), node.alternate.argument || h.VOID_0(t))));
|
||||
return REPLACED;
|
||||
}
|
||||
} // There is nothing after this If block. And one or both
|
||||
// of the consequent and alternate are either expression statment
|
||||
// or return statements.
|
||||
|
||||
|
||||
function toReturn(path) {
|
||||
const node = path.node;
|
||||
|
||||
if (!path.getSibling(path.key + 1).node && path.parentPath && path.parentPath.parentPath && path.parentPath.parentPath.isFunction()) {
|
||||
// Only the consequent is a return, void the alternate.
|
||||
if (t.isReturnStatement(node.consequent) && t.isExpressionStatement(node.alternate)) {
|
||||
if (!node.consequent.argument) {
|
||||
path.replaceWith(t.expressionStatement(t.logicalExpression("||", node.test, node.alternate.expression)));
|
||||
return REPLACED;
|
||||
}
|
||||
|
||||
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, node.consequent.argument || h.VOID_0(t), t.unaryExpression("void", node.alternate.expression, true))));
|
||||
return REPLACED;
|
||||
} // Only the alternate is a return, void the consequent.
|
||||
|
||||
|
||||
if (t.isReturnStatement(node.alternate) && t.isExpressionStatement(node.consequent)) {
|
||||
if (!node.alternate.argument) {
|
||||
path.replaceWith(t.expressionStatement(t.logicalExpression("&&", node.test, node.consequent.expression)));
|
||||
return REPLACED;
|
||||
}
|
||||
|
||||
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, t.unaryExpression("void", node.consequent.expression, true), node.alternate.argument || h.VOID_0(t))));
|
||||
return REPLACED;
|
||||
}
|
||||
|
||||
if (t.isReturnStatement(node.consequent) && !node.alternate) {
|
||||
if (!node.consequent.argument) {
|
||||
path.replaceWith(t.expressionStatement(node.test));
|
||||
return REPLACED;
|
||||
} // This would only be worth it if the previous statement was an if
|
||||
// because then we may merge to create a conditional.
|
||||
|
||||
|
||||
if (path.getSibling(path.key - 1).isIfStatement()) {
|
||||
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, node.consequent.argument || h.VOID_0(t), h.VOID_0(t))));
|
||||
return REPLACED;
|
||||
}
|
||||
}
|
||||
|
||||
if (t.isReturnStatement(node.alternate) && !node.consequent) {
|
||||
if (!node.alternate.argument) {
|
||||
path.replaceWith(t.expressionStatement(node.test));
|
||||
return REPLACED;
|
||||
} // Same as above.
|
||||
|
||||
|
||||
if (path.getSibling(path.key - 1).isIfStatement()) {
|
||||
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, node.alternate.argument || h.VOID_0(t), h.VOID_0(t))));
|
||||
return REPLACED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let next = path.getSibling(path.key + 1); // If the next satatement(s) is an if statement and we can simplify that
|
||||
// to potentailly be an expression (or a return) then this will make it
|
||||
// easier merge.
|
||||
|
||||
if (next.isIfStatement()) {
|
||||
next.pushContext(path.context);
|
||||
next.visit();
|
||||
next.popContext();
|
||||
next = path.getSibling(path.key + 1);
|
||||
} // Some other visitor might have deleted our node. OUR NODE ;_;
|
||||
|
||||
|
||||
if (!path.node) {
|
||||
return;
|
||||
} // No alternate but the next statement is a return
|
||||
// also turn into a return conditional
|
||||
|
||||
|
||||
if (t.isReturnStatement(node.consequent) && !node.alternate && next.isReturnStatement()) {
|
||||
const nextArg = next.node.argument || h.VOID_0(t);
|
||||
next.remove();
|
||||
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, node.consequent.argument || h.VOID_0(t), nextArg)));
|
||||
return REPLACED;
|
||||
} // Next is the last expression, turn into a return while void'ing the exprs
|
||||
|
||||
|
||||
if (path.parentPath && path.parentPath.parentPath && path.parentPath.parentPath.isFunction() && !path.getSibling(path.key + 2).node && t.isReturnStatement(node.consequent) && !node.alternate && next.isExpressionStatement()) {
|
||||
const nextExpr = next.node.expression;
|
||||
next.remove();
|
||||
|
||||
if (node.consequent.argument) {
|
||||
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, node.consequent.argument, t.unaryExpression("void", nextExpr, true))));
|
||||
return REPLACED;
|
||||
}
|
||||
|
||||
path.replaceWith(t.logicalExpression("||", node.test, nextExpr));
|
||||
return REPLACED;
|
||||
}
|
||||
} // Remove else for if-return
|
||||
|
||||
|
||||
function removeUnnecessaryElse(path) {
|
||||
const node = path.node;
|
||||
const consequent = path.get("consequent");
|
||||
const alternate = path.get("alternate");
|
||||
|
||||
if (consequent.node && alternate.node && (consequent.isReturnStatement() || consequent.isBlockStatement() && t.isReturnStatement(consequent.node.body[consequent.node.body.length - 1])) && ( // don't hoist declarations
|
||||
// TODO: validate declarations after fixing scope issues
|
||||
alternate.isBlockStatement() ? !alternate.get("body").some(stmt => stmt.isVariableDeclaration({
|
||||
kind: "let"
|
||||
}) || stmt.isVariableDeclaration({
|
||||
kind: "const"
|
||||
})) : true)) {
|
||||
path.insertAfter(alternate.isBlockStatement() ? alternate.node.body.map(el => t.clone(el)) : t.clone(alternate.node));
|
||||
node.alternate = null;
|
||||
return REPLACED;
|
||||
}
|
||||
}
|
||||
|
||||
function runTransforms(path) {
|
||||
// ordered
|
||||
const transforms = [toGuardedExpression, toTernary, toConditional, toReturn, removeUnnecessaryElse]; // run each of the replacement till we replace something
|
||||
// which is identified by the Symbol(REPLACED) that each of the
|
||||
// functions return when they replace something
|
||||
|
||||
for (var _i = 0; _i < transforms.length; _i++) {
|
||||
const transform = transforms[_i];
|
||||
|
||||
if (transform(path) === REPLACED) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // If the consequent is if and the altenrate is not then
|
||||
// switch them out. That way we know we don't have to print
|
||||
// a block.x
|
||||
|
||||
|
||||
function switchConsequent(path) {
|
||||
const node = path.node;
|
||||
|
||||
if (!node.alternate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!t.isIfStatement(node.consequent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (t.isIfStatement(node.alternate)) {
|
||||
return;
|
||||
}
|
||||
|
||||
node.test = t.unaryExpression("!", node.test, true);
|
||||
var _ref = [node.consequent, node.alternate];
|
||||
node.alternate = _ref[0];
|
||||
node.consequent = _ref[1];
|
||||
} // Make if statements with conditional returns in the body into
|
||||
// an if statement that guards the rest of the block.
|
||||
|
||||
|
||||
function conditionalReturnToGuards(path) {
|
||||
const node = path.node;
|
||||
|
||||
if (!path.inList || !path.get("consequent").isBlockStatement() || node.alternate) {
|
||||
return;
|
||||
}
|
||||
|
||||
let ret;
|
||||
let test;
|
||||
const exprs = [];
|
||||
const statements = node.consequent.body;
|
||||
|
||||
for (let i = 0, statement; statement = statements[i]; i++) {
|
||||
if (t.isExpressionStatement(statement)) {
|
||||
exprs.push(statement.expression);
|
||||
} else if (t.isIfStatement(statement)) {
|
||||
if (i < statements.length - 1) {
|
||||
// This isn't the last statement. Bail.
|
||||
return;
|
||||
}
|
||||
|
||||
if (statement.alternate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!t.isReturnStatement(statement.consequent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = statement.consequent;
|
||||
test = statement.test;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test || !ret) {
|
||||
return;
|
||||
}
|
||||
|
||||
exprs.push(test);
|
||||
const expr = exprs.length === 1 ? exprs[0] : t.sequenceExpression(exprs);
|
||||
const replacement = t.logicalExpression("&&", node.test, expr);
|
||||
path.replaceWith(t.ifStatement(replacement, ret, null));
|
||||
}
|
||||
|
||||
return {
|
||||
mergeNestedIfs,
|
||||
simplify: runTransforms,
|
||||
switchConsequent,
|
||||
conditionalReturnToGuards
|
||||
};
|
||||
};
|
1147
node_modules/babel-plugin-minify-simplify/lib/index.js
generated
vendored
Normal file
1147
node_modules/babel-plugin-minify-simplify/lib/index.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
73
node_modules/babel-plugin-minify-simplify/lib/logical-expression.js
generated
vendored
Normal file
73
node_modules/babel-plugin-minify-simplify/lib/logical-expression.js
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
"use strict";
|
||||
|
||||
const h = require("./helpers");
|
||||
|
||||
const PatternMatch = require("./pattern-match");
|
||||
|
||||
module.exports = t => {
|
||||
const OP_AND = input => input === "&&";
|
||||
|
||||
const OP_OR = input => input === "||";
|
||||
|
||||
function simplifyPatterns(path) {
|
||||
// cache of path.evaluate()
|
||||
const evaluateMemo = new Map();
|
||||
|
||||
const TRUTHY = input => {
|
||||
// !NaN and !undefined are truthy
|
||||
// separate check here as they are considered impure by babel
|
||||
if (input.isUnaryExpression() && input.get("argument").isIdentifier()) {
|
||||
if (input.node.argument.name === "NaN" || input.node.argument.name === "undefined") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const evalResult = input.evaluate();
|
||||
evaluateMemo.set(input, evalResult);
|
||||
return evalResult.confident && input.isPure() && evalResult.value;
|
||||
};
|
||||
|
||||
const FALSY = input => {
|
||||
// NaN and undefined are falsy
|
||||
// separate check here as they are considered impure by babel
|
||||
if (input.isIdentifier()) {
|
||||
if (input.node.name === "NaN" || input.node.name === "undefined") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const evalResult = input.evaluate();
|
||||
evaluateMemo.set(input, evalResult);
|
||||
return evalResult.confident && input.isPure() && !evalResult.value;
|
||||
};
|
||||
|
||||
const _h$typeSymbols = h.typeSymbols(t),
|
||||
EX = _h$typeSymbols.Expression; // Convention:
|
||||
// [left, operator, right, handler(leftNode, rightNode)]
|
||||
|
||||
|
||||
const matcher = new PatternMatch([[TRUTHY, OP_AND, EX, (l, r) => r], [FALSY, OP_AND, EX, l => l], [TRUTHY, OP_OR, EX, l => l], [FALSY, OP_OR, EX, (l, r) => r]]);
|
||||
const left = path.get("left");
|
||||
const right = path.get("right");
|
||||
const operator = path.node.operator;
|
||||
const result = matcher.match([left, operator, right], h.isPatternMatchesPath(t));
|
||||
|
||||
if (result.match) {
|
||||
// here we are sure that left.evaluate is always confident becuase
|
||||
// it satisfied one of TRUTHY/FALSY paths
|
||||
let value;
|
||||
|
||||
if (evaluateMemo.has(left)) {
|
||||
value = evaluateMemo.get(left).value;
|
||||
} else {
|
||||
value = left.evaluate().value;
|
||||
}
|
||||
|
||||
path.replaceWith(result.value(t.valueToNode(value), right.node));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
simplifyPatterns
|
||||
};
|
||||
};
|
156
node_modules/babel-plugin-minify-simplify/lib/pattern-match.js
generated
vendored
Normal file
156
node_modules/babel-plugin-minify-simplify/lib/pattern-match.js
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
"use strict";
|
||||
|
||||
function _toArray(arr) { return _arrayWithHoles(arr) || _iterableToArray(arr) || _nonIterableRest(); }
|
||||
|
||||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
|
||||
|
||||
function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
|
||||
|
||||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
||||
|
||||
const LEAF_NODE = Symbol("LEAF_NODE");
|
||||
module.exports = class PatternMatch {
|
||||
constructor(patterns) {
|
||||
this.decisionTree = this.makeDecisionTree(patterns);
|
||||
}
|
||||
|
||||
handle(input, isMatch) {
|
||||
const result = this.match(input, isMatch);
|
||||
|
||||
if (!result.match) {
|
||||
throw new Error("No Match Found for " + input.toString());
|
||||
}
|
||||
|
||||
if (typeof result.value !== "function") {
|
||||
throw new Error("Expecting a function. Instead got - " + result.value.toString());
|
||||
}
|
||||
|
||||
result.value.call(null, input, result.keys);
|
||||
}
|
||||
|
||||
match(input, isMatch = (a, b) => a === b) {
|
||||
let current = this.decisionTree;
|
||||
const result = {
|
||||
match: false,
|
||||
value: void 0,
|
||||
keys: []
|
||||
}; // to handle falsy keys
|
||||
|
||||
const NO_MATCH = Symbol("NO_MATCH");
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
let matchedKey = NO_MATCH; // because map doesn't support custom key equal function
|
||||
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = current.keys()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
const key = _step.value;
|
||||
|
||||
if (isMatch(key, input[i])) {
|
||||
matchedKey = key;
|
||||
result.keys.push(matchedKey);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matchedKey !== NO_MATCH) {
|
||||
current = current.get(matchedKey);
|
||||
|
||||
if (i === input.length - 1) {
|
||||
if (current.has(LEAF_NODE)) {
|
||||
result.match = true;
|
||||
result.value = current.get(LEAF_NODE);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
makeDecisionTree(patterns) {
|
||||
// order of keys in a Map is the order of insertion
|
||||
const root = new Map();
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator2 = patterns[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
const pattern = _step2.value;
|
||||
make(root, pattern);
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
|
||||
_iterator2.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
|
||||
function make(parent, pattern) {
|
||||
if (pattern.length < 2) {
|
||||
throw new Error("at least 2 elements required in a pattern");
|
||||
}
|
||||
|
||||
if (pattern.length === 2) {
|
||||
if (parent.has(pattern[0])) {
|
||||
const pattern0 = parent.get(pattern[0]);
|
||||
|
||||
if (!pattern0.has(LEAF_NODE)) {
|
||||
pattern0.set(LEAF_NODE, pattern[1]);
|
||||
} // here we don't handle duplicates
|
||||
// this pattern would have already been matched
|
||||
|
||||
} else {
|
||||
parent.set(pattern[0], new Map([[LEAF_NODE, pattern[1]]]));
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
const _pattern = _toArray(pattern),
|
||||
current = _pattern[0],
|
||||
rest = _pattern.slice(1);
|
||||
|
||||
if (parent.has(current)) {
|
||||
make(parent.get(current), rest);
|
||||
} else {
|
||||
parent.set(current, make(new Map(), rest));
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
Reference in New Issue
Block a user