Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

add jsx-ignore option to no-magic-numbers rule #4460

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
73 changes: 57 additions & 16 deletions src/rules/noMagicNumbersRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,18 @@ import { isCallExpression, isIdentifier } from "tsutils";
import * as Lint from "../index";
import { isNegativeNumberLiteral } from "../language/utils";

interface AllowedNumbersType {
"allowed-numbers": number[];
[key: string]: number[];
}
interface IgnoreJsxType {
"ignore-jsx": boolean;
[key: string]: boolean;
}
type OptionsType = IgnoreJsxType | AllowedNumbersType;

const IGNORE_JSX_OPTION = "ignore-jsx";
const ALLOWED_NUMBERS_OPTION = "allowed-numbers";

export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
Expand All @@ -34,23 +45,36 @@ export class Rule extends Lint.Rules.AbstractRule {
Magic numbers should be avoided as they often lack documentation.
Forcing them to be stored in variables gives them implicit documentation.
`,
optionsDescription: "A list of allowed numbers.",
optionsDescription: Lint.Utils.dedent`
\`${IGNORE_JSX_OPTION}\` specifies that jsx attributes with magic numbers should be ignored.
\`${ALLOWED_NUMBERS_OPTION}\` is a list of allowed numbers.`,
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
options: {
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
type: "array",
items: {
anyOf: [
{
type: "number",
minLength: 1,
},
{
type: "string",
options: [IGNORE_JSX_OPTION],
},
],
type: "number",
},
properties: {
type: "object",
[ALLOWED_NUMBERS_OPTION]: {
type: "array",
},
[IGNORE_JSX_OPTION]: {
type: "boolean",
},
},
minLength: 1,
},
optionExamples: [true, [true, 1, 2, 3], [true, "ignore-jsx"]],

optionExamples: [
[true, 1, 2, 3],
[
true,
{
[ALLOWED_NUMBERS_OPTION]: [1, 2, 3],
[IGNORE_JSX_OPTION]: true,
},
],
],
type: "typescript",
typescriptOnly: false,
};
Expand Down Expand Up @@ -86,7 +110,7 @@ export class Rule extends Lint.Rules.AbstractRule {
}
}

class NoMagicNumbersWalker extends Lint.AbstractWalker<Array<number | string>> {
class NoMagicNumbersWalker extends Lint.AbstractWalker<Array<OptionsType | number>> {
public walk(sourceFile: ts.SourceFile) {
const cb = (node: ts.Node): void => {
if (
Expand All @@ -111,12 +135,29 @@ class NoMagicNumbersWalker extends Lint.AbstractWalker<Array<number | string>> {
return ts.forEachChild(sourceFile, cb);
}

private checkNumericLiteral(node: ts.Node, num: string) {
private isAllowedNumber(num: string): boolean {
/* Using Object.is() to differentiate between pos/neg zero */
const compareNumbers = (allowedNum: number) => Object.is(allowedNum, parseFloat(num));
return this.options.some(option => {
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
if (typeof option === "number") {
return compareNumbers(option);
}
if (option.hasOwnProperty(ALLOWED_NUMBERS_OPTION)) {
return (option as AllowedNumbersType)[ALLOWED_NUMBERS_OPTION].some(compareNumbers);
}
return false;
});
}

private checkNumericLiteral(node: ts.Node, num: string) {
const shouldIgnoreJsxExpression =
node.parent.kind === ts.SyntaxKind.JsxExpression &&
this.options.some(option => (option as IgnoreJsxType)[IGNORE_JSX_OPTION]);

if (
!Rule.ALLOWED_NODES.has(node.parent.kind) &&
!this.options.some(allowedNum => Object.is(allowedNum, parseFloat(num))) &&
this.options.indexOf(IGNORE_JSX_OPTION) === -1
!this.isAllowedNumber(num) &&
!shouldIgnoreJsxExpression
) {
this.addFailureAtNode(node, Rule.FAILURE_STRING(num));
}
Expand Down
23 changes: 23 additions & 0 deletions test/rules/no-magic-numbers/allowed-numbers/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
parseInt();
parseInt('123', 2);
parseInt('123', 8);
parseInt('123', 10);
parseInt('123', 16);
console.log(-0);
console.log(1337);
console.log(-1337);
console.log(- 1337);
console.log(1337.7);
console.log(1338);
~~~~ ['magic numbers' are not allowed: 1338]
console.log(-1338)
~~~~~ ['magic numbers' are not allowed: -1338]
parseInt(foo === 4711 ? bar : baz, 10);
~~~~ ['magic numbers' are not allowed: 4711]
parseInt(foo === -0 ? bar : baz, 10);
export let x = 1337;
export let x = -1337;
export let x = 1337.7;
export let x = 1338;
export let x = -1338;
export let x = -0;
7 changes: 7 additions & 0 deletions test/rules/no-magic-numbers/allowed-numbers/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"rules": {
"no-magic-numbers": [true, {
"allowed-numbers": [1337, 1337.7, -1337, -0]
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
}]
}
}
9 changes: 9 additions & 0 deletions test/rules/no-magic-numbers/default-jsx/test.tsx.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as React from "react";
class App extends React.Component {
render() {
return <Component width={200} height={200} />;
~~~ ['magic numbers' are not allowed: 200]
~~~ ['magic numbers' are not allowed: 200]
}
}

5 changes: 5 additions & 0 deletions test/rules/no-magic-numbers/default-jsx/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"no-magic-numbers": true
}
}
8 changes: 0 additions & 8 deletions test/rules/no-magic-numbers/default/test.ts.lint
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,3 @@ class A {
enum Test {
key = 1337,
}

import * as React from "react";
class App extends React.Component {
render() {
return <Component width={200} height={200} />;
~~~ ['magic numbers' are not allowed: 200]
}
}
4 changes: 3 additions & 1 deletion test/rules/no-magic-numbers/ignore-jsx/tslint.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"rules": {
"no-magic-numbers": [true, "ignore-jsx"]
"no-magic-numbers": [true, {
"ignore-jsx": true
}]
}
}