@bazel/typescript
Version:
TypeScript rules for Bazel
60 lines (59 loc) • 2.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
// TODO: Export the matched node kinds here.
/**
* This class matches a property access node, based on a property holder type
* (through its name), i.e. a class, and a property name.
*
* The logic is voluntarily simple: if a matcher for `a.b` tests a `x.y` node,
* it will return true if:
* - `x` is of type `a` either directly (name-based) or through inheritance
* (ditto),
* - and, textually, `y` === `b`.
*
* Note that the logic is different from TS's type system: this matcher doesn't
* have any knowledge of structural typing.
*/
class PropertyMatcher {
constructor(bannedType, bannedProperty) {
this.bannedType = bannedType;
this.bannedProperty = bannedProperty;
}
static fromSpec(spec) {
if (spec.indexOf('.prototype.') === -1) {
throw new Error(`BANNED_PROPERTY expects a .prototype in your query.`);
}
const requestParser = /^([\w\d_.-]+)\.prototype\.([\w\d_.-]+)$/;
const matches = requestParser.exec(spec);
if (!matches) {
throw new Error('Cannot understand the BannedProperty spec' + spec);
}
const [bannedType, bannedProperty] = matches.slice(1);
return new PropertyMatcher(bannedType, bannedProperty);
}
/**
* @param n The PropertyAccessExpression we're looking at.
*/
matches(n, tc) {
return n.name.text === this.bannedProperty &&
this.typeMatches(tc.getTypeAtLocation(n.expression));
}
exactTypeMatches(inspectedType) {
const typeSymbol = inspectedType.getSymbol() || false;
return typeSymbol && typeSymbol.getName() === this.bannedType;
}
// TODO: Account for unknown types/ '?', and 'loose type matches', i.e. if the
// actual type is a supertype of the prohibited type.
typeMatches(inspectedType) {
if (this.exactTypeMatches(inspectedType)) {
return true;
}
// If the type is an intersection/union, check if any of the component matches
if (inspectedType.isUnionOrIntersection()) {
return inspectedType.types.some(comp => this.typeMatches(comp));
}
const baseTypes = inspectedType.getBaseTypes() || [];
return baseTypes.some(base => this.typeMatches(base));
}
}
exports.PropertyMatcher = PropertyMatcher;