Skip to content
This repository has been archived by the owner on Jul 15, 2023. It is now read-only.

Commit

Permalink
Converted react-anchor-blank-noopener to use a walk function. (#722)
Browse files Browse the repository at this point in the history
  • Loading branch information
reduckted authored and Josh Goldberg committed Dec 30, 2018
1 parent 991da07 commit ed028a2
Showing 1 changed file with 36 additions and 23 deletions.
59 changes: 36 additions & 23 deletions src/reactAnchorBlankNoopenerRule.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as ts from 'typescript';
import * as Lint from 'tslint';
import * as tsutils from 'tsutils';

import { ExtendedMetadata } from './utils/ExtendedMetadata';
import { Utils } from './utils/Utils';
Expand All @@ -8,6 +9,10 @@ import { getJsxAttributesFromJsxElement, getStringLiteral, isEmpty } from './uti

const OPTION_FORCE_REL_REDUNDANCY = 'force-rel-redundancy';

interface Options {
forceRelRedundancy: boolean;
}

export class Rule extends Lint.Rules.AbstractRule {
public static metadata: ExtendedMetadata = {
ruleName: 'react-anchor-blank-noopener',
Expand Down Expand Up @@ -37,50 +42,58 @@ export class Rule extends Lint.Rules.AbstractRule {

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
if (sourceFile.languageVariant === ts.LanguageVariant.JSX) {
return this.applyWithWalker(new ReactAnchorBlankNoopenerRuleWalker(sourceFile, this.getOptions()));
return this.applyWithFunction(sourceFile, walk, this.parseOptions(this.getOptions()));
} else {
return [];
}
}
}

class ReactAnchorBlankNoopenerRuleWalker extends Lint.RuleWalker {
private readonly forceRelRedundancy: boolean = false;
private readonly failureString: string = 'Anchor tags with target="_blank" should also include rel="noreferrer"';
private parseOptions(options: Lint.IOptions): Options {
const parsed: Options = {
forceRelRedundancy: false
};

constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) {
super(sourceFile, options);
if (options.ruleArguments !== undefined && options.ruleArguments.length > 0) {
this.forceRelRedundancy = options.ruleArguments.indexOf(OPTION_FORCE_REL_REDUNDANCY) > -1;
parsed.forceRelRedundancy = options.ruleArguments.indexOf(OPTION_FORCE_REL_REDUNDANCY) > -1;
}
if (this.forceRelRedundancy) {
this.failureString = 'Anchor tags with target="_blank" should also include rel="noopener noreferrer"';
}
}

protected visitJsxElement(node: ts.JsxElement): void {
const openingElement: ts.JsxOpeningElement = node.openingElement;
this.validateOpeningElement(openingElement);
super.visitJsxElement(node);
return parsed;
}
}

protected visitJsxSelfClosingElement(node: ts.JsxSelfClosingElement): void {
this.validateOpeningElement(node);
super.visitJsxSelfClosingElement(node);
}
function walk(ctx: Lint.WalkContext<Options>) {
const failureString: string = ctx.options.forceRelRedundancy
? 'Anchor tags with target="_blank" should also include rel="noopener noreferrer"'
: 'Anchor tags with target="_blank" should also include rel="noreferrer"';

private validateOpeningElement(openingElement: ts.JsxOpeningLikeElement): void {
function validateOpeningElement(openingElement: ts.JsxOpeningLikeElement): void {
if (openingElement.tagName.getText() === 'a') {
const allAttributes: { [propName: string]: ts.JsxAttribute } = getJsxAttributesFromJsxElement(openingElement);
/* tslint:disable:no-string-literal */
const target: ts.JsxAttribute = allAttributes['target'];
const rel: ts.JsxAttribute = allAttributes['rel'];
/* tslint:enable:no-string-literal */
if (target !== undefined && getStringLiteral(target) === '_blank' && !isRelAttributeValue(rel, this.forceRelRedundancy)) {
this.addFailureAt(openingElement.getStart(), openingElement.getWidth(), this.failureString);
if (
target !== undefined &&
getStringLiteral(target) === '_blank' &&
!isRelAttributeValue(rel, ctx.options.forceRelRedundancy)
) {
ctx.addFailureAt(openingElement.getStart(), openingElement.getWidth(), failureString);
}
}
}

function cb(node: ts.Node): void {
if (tsutils.isJsxElement(node)) {
validateOpeningElement(node.openingElement);
} else if (tsutils.isJsxSelfClosingElement(node)) {
validateOpeningElement(node);
}

return ts.forEachChild(node, cb);
}

return ts.forEachChild(ctx.sourceFile, cb);
}

function isRelAttributeValue(attribute: ts.JsxAttribute, forceRedundancy: boolean): boolean {
Expand Down

0 comments on commit ed028a2

Please sign in to comment.