Matches pattern cleanup (#5826)

* Extract duplicated function into babel-types

Also reimplements in a sane way.

* Add tests

* cleanup
This commit is contained in:
Justin Ridgewell 2017-06-06 11:20:05 -04:00 committed by Henry Zhu
parent 8df5514083
commit 5cc1cbf3bc
3 changed files with 93 additions and 90 deletions

View File

@ -12,54 +12,7 @@ import * as t from "babel-types";
*/
export function matchesPattern(pattern: string, allowPartial?: boolean): boolean {
// not a member expression
if (!this.isMemberExpression()) return false;
const parts = pattern.split(".");
const search = [this.node];
let i = 0;
function matches(name) {
const part = parts[i];
return part === "*" || name === part;
}
while (search.length) {
const node = search.shift();
if (allowPartial && i === parts.length) {
return true;
}
if (t.isIdentifier(node)) {
// this part doesn't match
if (!matches(node.name)) return false;
} else if (t.isLiteral(node)) {
// this part doesn't match
if (!matches(node.value)) return false;
} else if (t.isMemberExpression(node)) {
if (node.computed && !t.isLiteral(node.property)) {
// we can't deal with this
return false;
} else {
search.unshift(node.property);
search.unshift(node.object);
continue;
}
} else if (t.isThisExpression(node)) {
if (!matches("this")) return false;
} else {
// we can't deal with this
return false;
}
// too many parts
if (++i > parts.length) {
return false;
}
}
return i === parts.length;
return t.matchesPattern(this.node, pattern, allowPartial);
}
/**

View File

@ -314,6 +314,51 @@ export function cloneDeep(node: Object): Object {
return newNode;
}
/**
* Determines whether or not the input node `member` matches the
* input `match`.
*
* For example, given the match `React.createClass` it would match the
* parsed nodes of `React.createClass` and `React["createClass"]`.
*/
export function matchesPattern(
member: Object,
match: string | Array<string>,
allowPartial?: boolean
): boolean {
// not a member expression
if (!t.isMemberExpression(member)) return false;
const parts = Array.isArray(match) ? match : match.split(".");
const nodes = [];
let node;
for (node = member; t.isMemberExpression(node); node = node.object) {
nodes.push(node.property);
}
nodes.push(node);
if (nodes.length < parts.length) return false;
if (!allowPartial && nodes.length > parts.length) return false;
for (let i = 0, j = nodes.length - 1; i < parts.length; i++, j--) {
const node = nodes[j];
let value;
if (t.isIdentifier(node)) {
value = node.name;
} else if (t.isStringLiteral(node)) {
value = node.value;
} else {
return false;
}
if (parts[i] !== value) return false;
}
return true;
}
/**
* Build a function that when called will return whether or not the
* input `node` `MemberExpression` matches the input `match`.
@ -322,50 +367,10 @@ export function cloneDeep(node: Object): Object {
* parsed nodes of `React.createClass` and `React["createClass"]`.
*/
export function buildMatchMemberExpression(match:string, allowPartial?: boolean): Function {
export function buildMatchMemberExpression(match: string, allowPartial?: boolean): (Object) => boolean {
const parts = match.split(".");
return function (member) {
// not a member expression
if (!t.isMemberExpression(member)) return false;
const search = [member];
let i = 0;
while (search.length) {
const node = search.shift();
if (allowPartial && i === parts.length) {
return true;
}
if (t.isIdentifier(node)) {
// this part doesn't match
if (parts[i] !== node.name) return false;
} else if (t.isStringLiteral(node)) {
// this part doesn't match
if (parts[i] !== node.value) return false;
} else if (t.isMemberExpression(node)) {
if (node.computed && !t.isStringLiteral(node.property)) {
// we can't deal with this
return false;
} else {
search.push(node.object);
search.push(node.property);
continue;
}
} else {
// we can't deal with this
return false;
}
// too many parts
if (++i > parts.length) {
return false;
}
}
return true;
return matchesPattern(member, parts, allowPartial);
};
}

View File

@ -0,0 +1,45 @@
import * as t from "../lib";
import { assert } from "chai";
import { parse } from "babylon";
function parseCode(string) {
return parse(string, {
allowReturnOutsideFunction: true,
}).program.body[0];
}
describe("misc helpers", function () {
describe("matchesPattern", function () {
it("matches explicitly", function () {
const ast = parseCode("a.b.c.d").expression;
assert(t.matchesPattern(ast, "a.b.c.d"));
assert.isFalse(t.matchesPattern(ast, "a.b.c"));
assert.isFalse(t.matchesPattern(ast, "b.c.d"));
assert.isFalse(t.matchesPattern(ast, "a.b.c.d.e"));
});
it("matches partially", function () {
const ast = parseCode("a.b.c.d").expression;
assert(t.matchesPattern(ast, "a.b.c.d", true));
assert(t.matchesPattern(ast, "a.b.c", true));
assert.isFalse(t.matchesPattern(ast, "b.c.d", true));
assert.isFalse(t.matchesPattern(ast, "a.b.c.d.e", true));
});
it("matches string literal expressions", function () {
const ast = parseCode("a['b'].c.d").expression;
assert(t.matchesPattern(ast, "a.b.c.d"));
assert.isFalse(t.matchesPattern(ast, "a.b.c"));
assert.isFalse(t.matchesPattern(ast, "b.c.d"));
assert.isFalse(t.matchesPattern(ast, "a.b.c.d.e"));
});
it("matches string literal expressions partially", function () {
const ast = parseCode("a['b'].c.d").expression;
assert(t.matchesPattern(ast, "a.b.c.d", true));
assert(t.matchesPattern(ast, "a.b.c", true));
assert.isFalse(t.matchesPattern(ast, "b.c.d", true));
assert.isFalse(t.matchesPattern(ast, "a.b.c.d.e", true));
});
});
});