build: expand validateDecoratorsRule to support blacklisting (#8897)

Expands the decorator validation rule to support blacklisting properties whose values match a pattern. This allows disabling things like inline styles.
This commit is contained in:
Kristiyan Kostadinov
2017-12-15 00:33:34 +01:00
committed by Andrew Seguin
parent f5a2faf22a
commit dc045967d8
2 changed files with 44 additions and 14 deletions
+42 -13
View File
@@ -5,12 +5,14 @@ import * as minimatch from 'minimatch';
/**
* Rule that enforces certain decorator properties to be defined and to match a pattern.
* Supports whitelisting via the third argument. E.g.
* Properties can be forbidden by prefixing their name with a `!`.
* Supports whitelisting files via the third argument. E.g.
*
* ```
* "validate-decorators": [true, {
* "Component": {
* "encapsulation": "\\.None$"
* "encapsulation": "\\.None$",
* "!styles": ".*"
* }
* }, "src/lib"]
* ```
@@ -21,8 +23,16 @@ export class Rule extends Lint.Rules.AbstractRule {
}
}
/** Rules that can be used to validate the decorators in a file. */
type DecoratorRules = {[key: string]: {[key: string]: RegExp}};
/** Represents a set of required and forbidden decorator properties. */
type DecoratorRuleSet = {
required: {[key: string]: RegExp},
forbidden: {[key: string]: RegExp},
};
/** Represents a map between decorator names and rule sets. */
type DecoratorRules = {
[key: string]: DecoratorRuleSet
};
class Walker extends Lint.RuleWalker {
// Whether the file should be checked at all.
@@ -76,20 +86,30 @@ class Walker extends Lint.RuleWalker {
node
}));
// Find all of the rule properties that are missing from the decorator.
const missing = Object.keys(rules).filter(key => !props.find((prop: any) => prop.name === key));
// Find all of the required rule properties that are missing from the decorator.
const missing = Object.keys(rules.required)
.filter(key => !props.find((prop: any) => prop.name === key));
if (missing.length) {
// Exit early if any of the properties are missing.
this.addFailureAtNode(decorator.parent, 'Missing required properties: ' + missing.join(', '));
} else {
// If all the necessary properties are defined, ensure that they match the pattern.
// If all the necessary properties are defined, ensure that
// they match the pattern and aren't in the forbidden list.
props
.filter((prop: any) => rules[prop.name])
.filter((prop: any) => !rules[prop.name].test(prop.value))
.filter((prop: any) => rules.required[prop.name] || rules.forbidden[prop.name])
.forEach((prop: any) => {
this.addFailureAtNode(prop.node,
`Invalid value for property. Expected value to match "${rules[prop.name]}".`);
const {name, value, node} = prop;
const requiredPattern = rules.required[name];
const forbiddenPattern = rules.forbidden[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}".`);
}
});
}
}
@@ -108,9 +128,18 @@ class Walker extends Lint.RuleWalker {
.filter(decoratorName => Object.keys(config[decoratorName]).length > 0)
.forEach(decoratorName => {
output[decoratorName] = Object.keys(config[decoratorName]).reduce((accumulator, prop) => {
accumulator[prop] = new RegExp(config[decoratorName][prop]);
const isForbidden = prop.startsWith('!');
const cleanName = isForbidden ? prop.slice(1) : prop;
const pattern = new RegExp(config[decoratorName][prop]);
if (isForbidden) {
accumulator.forbidden[cleanName] = pattern;
} else {
accumulator.required[cleanName] = pattern;
}
return accumulator;
}, {} as {[key: string]: RegExp});
}, {required: {}, forbidden: {}} as DecoratorRuleSet);
});
}
+2 -1
View File
@@ -96,7 +96,8 @@
"encapsulation": "\\.None$",
"moduleId": "^module\\.id$",
"preserveWhitespaces": "false$",
"changeDetection": "\\.OnPush$"
"changeDetection": "\\.OnPush$",
"!styles": ".*"
}
}, "src/+(lib|cdk|material-experimental)/**/!(*.spec).ts"],
"require-license-banner": [