Skip to content

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

Merged
merged 6 commits into from
Jan 26, 2018
Merged

build: tslint rule to enforce html tags escaping in comments #8200

merged 6 commits into from
Jan 26, 2018

Conversation

julianobrasil
Copy link
Contributor

@julianobrasil julianobrasil commented Nov 2, 2017

Refactoring of #5825 logic

cc @willshowell

This rule will look for:

1 - Any HTML open/close tag delimiter (< or >) outside backtick pairs (``)

/** 
 * The using of <svg> is very good because...
 */
/** 
 * Look for every occurrency of `i <= 18...
 */

2 - Any "opening" backtick without its "closing" match:

/** 
 * The using of `<svg> is very good because...
 */
/** 
 * We recommend using backticks (`) to escape...
 */

And it will allow any html provided it's between backtick matching pairs:

/** 
 * The using of `    <div><br></div> is very` good because...
 */

@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label Nov 2, 2017
Copy link
Contributor

@willshowell willshowell left a 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;
Copy link
Contributor

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"
]
Copy link
Contributor

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.

Copy link
Contributor Author

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;
Copy link
Contributor

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;
Copy link
Contributor

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.
*/
Copy link
Contributor

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;
Copy link
Contributor

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('');
Copy link
Contributor

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++) {
Copy link
Contributor

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)) {
Copy link
Contributor

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('');
Copy link
Contributor

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('');

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better!

@jelbourn jelbourn requested a review from crisbeto November 2, 2017 22:23

/** Gets whether the comment's HTML, if any, is properly escaped */
parseForHtml(fullText) {
const matches = new RegExp(/[<>]/);
Copy link
Contributor

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))) {
Copy link
Contributor

@rafaelss95 rafaelss95 Nov 3, 2017

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.

Copy link
Contributor Author

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++) {
Copy link
Contributor

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.

@willshowell
Copy link
Contributor

@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.

@julianobrasil
Copy link
Contributor Author

julianobrasil commented Nov 3, 2017

Travis output bellow. Apparently this lint rule is doing what it is supposed to do (there was a little mistake of mine in the code in the part that capture the comment to be tested, but it's fixed now).

image

@willshowell
Copy link
Contributor

Awesome! You can also run npm run tslint locally to lint. And I think those fixes should be included with this PR.

@julianobrasil
Copy link
Contributor Author

julianobrasil commented Nov 3, 2017

@willshowell, locally I got a lot of lint error messages because windows line breaks are different from LF.

Copy link
Member

@crisbeto crisbeto left a 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.

@jelbourn
Copy link
Member

@julianobrasil could you rebase this PR?

@julianobrasil
Copy link
Contributor Author

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"
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jelbourn
Copy link
Member

@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?

@julianobrasil
Copy link
Contributor Author

I'll do it.

@jelbourn jelbourn added pr: lgtm action: merge The PR is ready for merge by the caretaker labels Jan 25, 2018
@jelbourn
Copy link
Member

Looks like there are still two things being caught:

ERROR: /home/travis/build/angular/material2/src/lib/chips/chip-input.ts[24, 1]: An HTML tag delimiter (< or >) may only appear in a JSDoc comment if it is escaped. This prevents failures in document generation caused by a misinterpreted tag.
ERROR: /home/travis/build/angular/material2/src/lib/icon/icon.ts[33, 1]: An HTML tag delimiter (< or >) may only appear in a JSDoc comment if it is escaped. This prevents failures in document generation caused by a misinterpreted tag.

@julianobrasil
Copy link
Contributor Author

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 npm install. And this is preventing me from running anything locally (not sure yet what the problem is, but I'm trying to talk to the guys of grpc/grpc-node repo about it as it seems to be the culprit)

@jelbourn
Copy link
Member

This one passes now, so I'm merging it

@jelbourn jelbourn merged commit c306297 into angular:master Jan 26, 2018
@julianobrasil julianobrasil deleted the tslint-html-rule branch January 26, 2018 00:50
@jelbourn jelbourn added the target: patch This PR is targeted for the next patch release label Jan 29, 2018
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 8, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes PR author has agreed to Google's Contributor License Agreement target: patch This PR is targeted for the next patch release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants