From deb0666accc4393e2580dc0e9f837d5b47efdb3a Mon Sep 17 00:00:00 2001 From: cm-ayf Date: Thu, 25 Jan 2024 00:50:13 +0900 Subject: [PATCH] =?UTF-8?q?=E5=8F=B3=E8=BE=BA=E3=81=8Cundefined=E3=82=84nu?= =?UTF-8?q?ll=E3=81=A7=E3=81=82=E3=82=8B=E5=8E=B3=E5=AF=86=E7=AD=89?= =?UTF-8?q?=E4=BE=A1=E6=BC=94=E7=AE=97=E5=AD=90=E3=82=92=E7=AD=89=E4=BE=A1?= =?UTF-8?q?=E6=BC=94=E7=AE=97=E5=AD=90=E3=81=AB=E8=87=AA=E5=8B=95=E3=81=A7?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E3=81=A7=E3=81=8D=E3=82=8B=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=20(#1752)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- .eslintrc.js | 28 +------------- eslint-plugin/create-rule.js | 6 +++ eslint-plugin/index.js | 14 +++++++ eslint-plugin/no-strict-nullable.js | 58 +++++++++++++++++++++++++++++ eslint-plugin/no-strict-nullable.md | 18 +++++++++ eslint-plugin/package.json | 6 +++ package-lock.json | 15 ++++++++ package.json | 3 ++ 8 files changed, 121 insertions(+), 27 deletions(-) create mode 100644 eslint-plugin/create-rule.js create mode 100644 eslint-plugin/index.js create mode 100644 eslint-plugin/no-strict-nullable.js create mode 100644 eslint-plugin/no-strict-nullable.md create mode 100644 eslint-plugin/package.json diff --git a/.eslintrc.js b/.eslintrc.js index 3340e91301..7967b7b53b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -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", @@ -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: [ { diff --git a/eslint-plugin/create-rule.js b/eslint-plugin/create-rule.js new file mode 100644 index 0000000000..523eb12eb6 --- /dev/null +++ b/eslint-plugin/create-rule.js @@ -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` +); diff --git a/eslint-plugin/index.js b/eslint-plugin/index.js new file mode 100644 index 0000000000..ae70df66fc --- /dev/null +++ b/eslint-plugin/index.js @@ -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"), + }, +}; diff --git a/eslint-plugin/no-strict-nullable.js b/eslint-plugin/no-strict-nullable.js new file mode 100644 index 0000000000..e0eaf931b5 --- /dev/null +++ b/eslint-plugin/no-strict-nullable.js @@ -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: [], +}); diff --git a/eslint-plugin/no-strict-nullable.md b/eslint-plugin/no-strict-nullable.md new file mode 100644 index 0000000000..a66749c494 --- /dev/null +++ b/eslint-plugin/no-strict-nullable.md @@ -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 diff --git a/eslint-plugin/package.json b/eslint-plugin/package.json new file mode 100644 index 0000000000..4429e24199 --- /dev/null +++ b/eslint-plugin/package.json @@ -0,0 +1,6 @@ +{ + "name": "@voicevox/eslint-plugin", + "version": "0.0.0", + "description": "eslint plugin for voicevox", + "main": "index.js" +} diff --git a/package-lock.json b/package-lock.json index 9e1aba68ac..aa41a79cde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,7 +57,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", @@ -103,6 +106,11 @@ "dmg-license": "1.0.11" } }, + "eslint-plugin": { + "name": "@voicevox/eslint-plugin", + "version": "0.0.0", + "dev": true + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -2212,6 +2220,10 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@voicevox/eslint-plugin": { + "resolved": "eslint-plugin", + "link": true + }, "node_modules/@volar/language-core": { "version": "1.10.10", "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.10.tgz", @@ -14425,6 +14437,9 @@ "pretty-format": "^29.5.0" } }, + "@voicevox/eslint-plugin": { + "version": "file:eslint-plugin" + }, "@volar/language-core": { "version": "1.10.10", "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.10.tgz", diff --git a/package.json b/package.json index 66bbff6279..a25da878a5 100644 --- a/package.json +++ b/package.json @@ -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",