in tools/tslint-rules/validateDecoratorsRule.ts [103:194]
private _validateDecorator(decorator: ts.Decorator) {
const expression = decorator.expression;
if (!expression || !ts.isCallExpression(expression)) {
return;
}
// Get the rules that are relevant for the current decorator.
const rules = this._rules[expression.expression.getText()];
const args = expression.arguments;
// Don't do anything if there are no rules.
if (!rules) {
return;
}
const allPropsRequirement = rules.requiredProps[ALL_PROPS_TOKEN];
// If we have a rule that applies to all properties, we just run it through once and we exit.
if (allPropsRequirement) {
const argumentText = args[rules.argument] ? args[rules.argument].getText() : '';
if (!allPropsRequirement.test(argumentText)) {
this.addFailureAtNode(
expression.parent,
`Expected decorator argument ${rules.argument} ` + `to match "${allPropsRequirement}"`,
);
}
return;
}
if (!args[rules.argument]) {
if (rules.required) {
this.addFailureAtNode(
expression.parent,
`Missing required argument at index ${rules.argument}`,
);
}
return;
}
if (!ts.isObjectLiteralExpression(args[rules.argument])) {
return;
}
// Extract the property names and values.
const props: {name: string; value: string; node: ts.PropertyAssignment}[] = [];
(args[rules.argument] as ts.ObjectLiteralExpression).properties.forEach(prop => {
if (ts.isPropertyAssignment(prop) && prop.name && prop.initializer) {
props.push({
name: prop.name.getText(),
value: prop.initializer.getText(),
node: prop,
});
}
});
// Find all of the required rule properties that are missing from the decorator.
const missing = Object.keys(rules.requiredProps).filter(
key => !props.find(prop => prop.name === key),
);
if (missing.length) {
// Exit early if any of the properties are missing.
this.addFailureAtNode(
expression.expression,
'Missing required properties: ' + missing.join(', '),
);
} else {
// If all the necessary properties are defined, ensure that
// they match the pattern and aren't in the forbidden list.
props
.filter(prop => rules.requiredProps[prop.name] || rules.forbiddenProps[prop.name])
.forEach(prop => {
const {name, value, node} = prop;
const requiredPattern = rules.requiredProps[name];
const forbiddenPattern = rules.forbiddenProps[name];
if (requiredPattern && !requiredPattern.test(value)) {
this.addFailureAtNode(
node,
`Invalid value for property. ` + `Expected value to match "${requiredPattern}".`,
);
} else if (forbiddenPattern && forbiddenPattern.test(value)) {
this.addFailureAtNode(
node,
`Property value not allowed. ` + `Value should not match "${forbiddenPattern}".`,
);
}
});
}
}