-
Notifications
You must be signed in to change notification settings - Fork 6.8k
build: tslint rule to enforce html tags escaping in comments #8200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for reviving it!
visitSourceFile(sourceFile) { | ||
utils.forEachComment(sourceFile, (fullText, commentRange) => { | ||
|
||
let isEscapedHtmlTag = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
htmlIsEscaped
?
tslint.json
Outdated
], | ||
[ | ||
"xdescribe" | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the original formatting was fine. Just add no-unescaped-html-tag
without any of the other changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, that was and accidental shift+alt+f
in Visual Studio Code.
} | ||
} | ||
|
||
exports.Rule = Rule; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another way to write this rule that might be cleaner would be,
utils.forEachComment(sourceFile, (fullText, commentRange) => {
const htmlIsEscaped = parseForHtml(fullText);
if (commentRange.kind === ... && !htmlIsEscaped) {
this.addFailureAt(...);
}
}
/** Gets whether the comment's HTML, if any, is properly escaped */
parseForHtml(fullText) {
// parsing done here
}
I think that way you wouldn't have to worry so much about carrying the flag through the whole method and could instead return early, when needed.
let isEscapedHtmlTag = true; | ||
const matches = new RegExp(/[<>]/); | ||
|
||
const numberOfBackticks = fullText.split('`').length - 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
backtickCount
?
* backtick is found in the string, and it is set back to false when the next matching | ||
* (closing) backtick is found. Every backtick must have a matching. < and > | ||
* must always be between two matching backticks. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use // single-line comments
Also I think only the last sentence is needed.
* must always be between two matching backticks. | ||
*/ | ||
const splitedFullText = fullText.split(''); | ||
let isBacktickWithoutMatch = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
// Whether an opening backtick has been found without a closing pair
let openBacktick = false;
* (closing) backtick is found. Every backtick must have a matching. < and > | ||
* must always be between two matching backticks. | ||
*/ | ||
const splitedFullText = fullText.split(''); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fullTextArray
?
const splitedFullText = fullText.split(''); | ||
let isBacktickWithoutMatch = false; | ||
|
||
for (var i = 0; i < splitedFullText.length; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let i = 0
// if there are no backticks and [<>], there's no need for any more checks | ||
if ((numberOfBackticks > 1) && matches.test(fullText)) { | ||
// if there are backticks there should be an even number of them | ||
if (!!(numberOfBackticks % 2)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can just use numberOfBackticks % 2
since you only care whether its zero or non-zero.
return false; | ||
} else { | ||
// < and > must always be between two matching backticks. | ||
const fullTextArray = fullText.split(''); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the logic could be tightened up like this,
const matches = new RegExp(/[<>]/);
const backtickCount = fullText.split('`').length - 1;
// An odd number of backticks or html without backticks is invalid
if (backtickCount % 2 || (!backtickCount && matches.test(fullText))) {
return false;
}
// Text without html is valid
if (!matches.test(fullText)) {
return true;
}
// < and > must always be between two matching backticks
const fullTextArray = fullText.split('');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better!
|
||
/** Gets whether the comment's HTML, if any, is properly escaped */ | ||
parseForHtml(fullText) { | ||
const matches = new RegExp(/[<>]/); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be simplified to const matches = /[<>]/;
const backtickCount = fullText.split('`').length - 1; | ||
|
||
// An odd number of backticks or html without backticks is invalid | ||
if ((backtickCount % 2) || ((backtickCount === 0) && matches.test(fullText))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are too many parenthesis. It could be: if (backtickCount % 2 || (backtickCount === 0 && matches.test(fullText)) {
Also, you can just do !backtickCount
as @willshowell mentioned in his comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I missed it.
// Whether an opening backtick has been found without a closing pair | ||
let openBacktick = false; | ||
|
||
for (let i = 0; i < fullTextArray.length; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use for..of
and loop over the string, so you don't need to use split('')
above.
@julianobrasil one last thing I noticed: since you opened the original PR, the other linters have been rewritten in typescript. Might want to convert it and make sure it matches the updated format of the other ones. |
Awesome! You can also run |
@willshowell, locally I got a lot of lint error messages because windows line breaks are different from LF. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. @julianobrasil you should be able to switch the line endings in your IDE. If you're using vscode, there should be a "CRLF" button in the bottom right corner.
@julianobrasil could you rebase this PR? |
Done. |
tslint.json
Outdated
@@ -118,7 +118,8 @@ | |||
"missing-rollup-globals": [ | |||
true, | |||
"./tools/package-tools/rollup-globals.ts", | |||
"src/+(lib|cdk|material-examples|material-experimental|cdk-experimental)/**/*.ts" | |||
] | |||
"src/+(lib|cdk|material-examples)/**/*.ts" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to remove material-experimental|cdk-experimental
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely not. This was an oversight.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@julianobrasil ah, this actually catches a pretty large number of instances already in the codebase. Did you want to send a separate PR that just removes those first so we can merge this and still have everything be green? |
I'll do it. |
Looks like there are still two things being caught:
|
This gave me more trouble than I expected. @jelbourn, sorry for all of this commiting ping-pong, but after updating my local copy of material repo on wednesday, I got a lot of errors with |
This one passes now, so I'm merging it |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Refactoring of #5825 logic
cc @willshowell
This rule will look for:
1 - Any HTML open/close tag delimiter (
<
or>
) outside backtick pairs (``)2 - Any "opening" backtick without its "closing" match:
And it will allow any html provided it's between backtick matching pairs: