Skip to content

Commit

Permalink
右辺がundefinedやnullである厳密等価演算子を等価演算子に自動で修正できる設定を追加 (#1752)
Browse files Browse the repository at this point in the history
* setup eslint plugin for this repository

* add rule no-strict-nullable

* separate createRule to a file

* add documentation for rule no-strict-nullable

* fix rule documentation url

* add trailing newline to eslint-plugin/package.json

---------

Co-authored-by: Hiroshiba <[email protected]>
  • Loading branch information
cm-ayf and Hiroshiba authored Jan 24, 2024
1 parent 1ca1585 commit deb0666
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 27 deletions.
28 changes: 1 addition & 27 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = {
"@vue/prettier",
"@vue/eslint-config-typescript/recommended",
"@vue/eslint-config-prettier",
"plugin:@voicevox/all",
],
plugins: ["import"],
parser: "vue-eslint-parser",
Expand Down Expand Up @@ -62,33 +63,6 @@ module.exports = {
},
],
"import/order": "error",
"no-restricted-syntax": [
"warn",
{
selector:
"BinaryExpression[operator='==='][right.type='Literal'][right.value=null]",
message:
"'=== null'ではなく'== null'を使用してください。詳細: https://github.com/VOICEVOX/voicevox/issues/1513",
},
{
selector:
"BinaryExpression[operator='!=='][right.type='Literal'][right.value=null]",
message:
"'!== null'ではなく'!= null'を使用してください。詳細: https://github.com/VOICEVOX/voicevox/issues/1513",
},
{
selector:
"BinaryExpression[operator='==='][right.type='Identifier'][right.name=undefined]",
message:
"'=== undefined'ではなく'== undefined'を使用してください。詳細: https://github.com/VOICEVOX/voicevox/issues/1513",
},
{
selector:
"BinaryExpression[operator='!=='][right.type='Identifier'][right.name=undefined]",
message:
"'!== undefined'ではなく'!= undefined'を使用してください。詳細: https://github.com/VOICEVOX/voicevox/issues/1513",
},
],
},
overrides: [
{
Expand Down
6 changes: 6 additions & 0 deletions eslint-plugin/create-rule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const { ESLintUtils } = require("@typescript-eslint/utils");

exports.createRule = ESLintUtils.RuleCreator(
(name) =>
`https://github.com/VOICEVOX/voicevox/blob/main/eslint-plugin/${name}.md`
);
14 changes: 14 additions & 0 deletions eslint-plugin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @ts-check
module.exports = {
configs: {
all: {
plugins: ["@voicevox"],
rules: {
"@voicevox/no-strict-nullable": "error",
},
},
},
rules: {
"no-strict-nullable": require("./no-strict-nullable"),
},
};
58 changes: 58 additions & 0 deletions eslint-plugin/no-strict-nullable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// @ts-check
const { AST_NODE_TYPES } = require("@typescript-eslint/utils");
const { createRule } = require("./create-rule");

/**
* @param {import("@typescript-eslint/types").TSESTree.BinaryExpression["left"]} node
*/
function isNull(node) {
return node.type === AST_NODE_TYPES.Literal && node.value == null;
}

/**
* @param {import("@typescript-eslint/types").TSESTree.BinaryExpression["right"]} node
*/
function isUndefined(node) {
return node.type === AST_NODE_TYPES.Identifier && node.name === "undefined";
}

module.exports = createRule({
create(context) {
return {
BinaryExpression(node) {
if (node.operator !== "===" && node.operator !== "!==") return;
if (!isNull(node.right) && !isUndefined(node.right)) return;

context.report({
node,
messageId: "report",
data: {
operator: node.operator.slice(0, 2),
expression: context.getSourceCode().getText(node.right),
},
fix(fixer) {
return fixer.replaceTextRange(
[node.left.range[1], node.right.range[0]],
node.operator.slice(0, 2) + " "
);
},
});
},
};
},
name: "no-strict-nullable",
meta: {
type: "problem",
docs: {
description: "undefinedとnullと比較する際に厳密等価演算子を使わない",
recommended: "error",
},
messages: {
report:
"'{{ operator }}= {{ expression }}'ではなく'{{ operator }} {{ expression }}'を使用してください。",
},
schema: [],
fixable: "code",
},
defaultOptions: [],
});
18 changes: 18 additions & 0 deletions eslint-plugin/no-strict-nullable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# no-strict-nullable

厳密等価演算子`===`は有用ですが,`undefined``null`を右辺に持ってくる時,左辺が`undefined`を取りうるのか,それとも`null`を取りうるのかを考える必要があります.
一方,等価演算子`==``undefined``null`を区別しないため,このような場合に`==`を使うようにすることで,左辺が取る値を考える必要がなくなります.

このルールでは,右辺が`null`または`undefined`の場合に,厳密等価演算子`===`を使うことを禁止し,代わりに等価演算子`==`を使うようにします.

```ts
const a = fuga === undefined;
// ^^^^^^^^^^^^^^^^^^ '=== null'ではなく'== null'を使用してください。
const button = { text: null };
const c = button.text !== null;
// ^^^^^^^^^^^^^^^^^^^^ '!== null'ではなく'!= null'を使用してください。
```

## リンク

https://github.com/VOICEVOX/voicevox/issues/1513
6 changes: 6 additions & 0 deletions eslint-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "@voicevox/eslint-plugin",
"version": "0.0.0",
"description": "eslint plugin for voicevox",
"main": "index.js"
}
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@
"@types/wicg-file-system-access": "2020.9.6",
"@typescript-eslint/eslint-plugin": "5.38.1",
"@typescript-eslint/parser": "5.38.1",
"@typescript-eslint/types": "5.38.1",
"@typescript-eslint/utils": "5.38.1",
"@vitejs/plugin-vue": "4.0.0",
"@voicevox/eslint-plugin": "file:./eslint-plugin",
"@vue/eslint-config-prettier": "7.0.0",
"@vue/eslint-config-typescript": "11.0.2",
"@vue/test-utils": "2.3.0",
Expand Down

0 comments on commit deb0666

Please sign in to comment.