From 72aba1d491cf8d50e740f80fd384f0f312a9907d Mon Sep 17 00:00:00 2001 From: Vida Xie <38204901+9romise@users.noreply.github.com> Date: Sun, 7 Apr 2024 22:35:46 +0800 Subject: [PATCH 1/3] feat: add no-duplicate-store-ids --- README.md | 1 + docs/rules/no-duplicate-store-ids.md | 55 ++++++++++++++++++++++ src/index.ts | 13 +++-- src/rules/no-duplicate-store-ids.ts | 47 ++++++++++++++++++ tests/rules/no-duplicate-store-ids.test.ts | 43 +++++++++++++++++ 5 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 docs/rules/no-duplicate-store-ids.md create mode 100644 src/rules/no-duplicate-store-ids.ts create mode 100644 tests/rules/no-duplicate-store-ids.test.ts diff --git a/README.md b/README.md index 27b9eec..56b4671 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ To use the all configuration, extend it in your `.eslintrc` file: | Name                                  | Description | 💼 | ⚠️ | 🚫 | | :------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- | :- | :- | :- | +| [no-duplicate-store-ids](docs/rules/no-duplicate-store-ids.md) | Disallow duplicate store ids | ✅ | | | | [no-return-global-properties](docs/rules/no-return-global-properties.md) | Disallows returning globally provided properties from Pinia stores. | ✅ | 🌐 | | | [prefer-single-store-per-file](docs/rules/prefer-single-store-per-file.md) | Encourages defining each store in a separate file. | | | 🌐 | | [prefer-use-store-naming-convention](docs/rules/prefer-use-store-naming-convention.md) | Enforces the convention of naming stores with the prefix `use` followed by the store name. | | 🌐 | | diff --git a/docs/rules/no-duplicate-store-ids.md b/docs/rules/no-duplicate-store-ids.md new file mode 100644 index 0000000..621df9e --- /dev/null +++ b/docs/rules/no-duplicate-store-ids.md @@ -0,0 +1,55 @@ +# Disallow duplicate store ids (`pinia/no-duplicate-store-ids`) + +💼 This rule is enabled in the ✅ `recommended` config. + + + +## Rule Details + +❌ Examples of **incorrect** code for this rule: + +```js +// a.js +export const useBarStore = defineStore('bar', () => { + const bar = ref(0) + + return { bar } +}) + +// b.js +export const useAnotherBarStore = defineStore('bar', () => { + const foo = ref(0) + + return { foo } +}) +``` + +```js +export const useBarStore = defineStore('bar', () => { + const bar = ref(0) + + return { bar } +}) + +export const useAnotherBarStore = defineStore('bar', () => { + const foo = ref(0) + + return { foo } +}) +``` + +✅ Examples of **correct** code for this rule: + +```js +export const useBarStore = defineStore('bar', () => { + const bar = ref(0) + + return { bar } +}) + +export const useAnotherCounterStore = defineStore('foo', () => { + const foo = ref(0) + + return { foo } +}) +``` diff --git a/src/index.ts b/src/index.ts index 2f18940..6b1b212 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,10 @@ import noReturnGlobalProperties, { RULE_NAME as noReturnGlobalPropertiesName } from './rules/no-return-global-properties' +import noDuplicateStoreIds, { + RULE_NAME as noDuplicateStoreIdsName +} from './rules/no-duplicate-store-ids' + const createConfig = (rules: Record) => ({ plugins: ['pinia'], rules: Object.keys(rules).reduce((acc, ruleName) => { @@ -28,12 +32,14 @@ const allRules = { [requireSetupStorePropsName]: 'warn', [preferNamingConventionName]: 'warn', [preferSingleStoreName]: 'off', - [noReturnGlobalPropertiesName]: 'warn' + [noReturnGlobalPropertiesName]: 'warn', + [noDuplicateStoreIdsName]: 'error' } const recommended = { [requireSetupStorePropsName]: 'error', - [noReturnGlobalPropertiesName]: 'error' + [noReturnGlobalPropertiesName]: 'error', + [noDuplicateStoreIdsName]: 'error' } export default { @@ -41,7 +47,8 @@ export default { [requireSetupStorePropsName]: requireSetupStoreProps, [preferNamingConventionName]: preferNamingConvention, [preferSingleStoreName]: preferSingleStore, - [noReturnGlobalPropertiesName]: noReturnGlobalProperties + [noReturnGlobalPropertiesName]: noReturnGlobalProperties, + [noDuplicateStoreIdsName]: noDuplicateStoreIds }, configs: { all: createConfig(allRules), diff --git a/src/rules/no-duplicate-store-ids.ts b/src/rules/no-duplicate-store-ids.ts new file mode 100644 index 0000000..1825b09 --- /dev/null +++ b/src/rules/no-duplicate-store-ids.ts @@ -0,0 +1,47 @@ +import { createEslintRule } from '../utils/rule-creator' +import { AST_NODE_TYPES } from '@typescript-eslint/utils' + +export const RULE_NAME = 'no-duplicate-store-ids' +export type MESSAGE_IDS = 'duplicatedStoreIds' +type Options = [] + +const usingStoreIds = new Set() + +export default createEslintRule({ + name: RULE_NAME, + meta: { + type: 'problem', + docs: { + description: 'Disallow duplicate store ids.' + }, + schema: [], + messages: { + duplicatedStoreIds: 'No duplicated store ids allowed.' + } + }, + defaultOptions: [], + create: (context) => { + return { + CallExpression(node) { + const callee = node.callee + if (callee.type !== 'Identifier' || callee.name !== 'defineStore') + return + + const storeId = node.arguments && node.arguments[0] + + if (!storeId || storeId.type !== AST_NODE_TYPES.Literal) return + + const value = storeId.value as string + + if (usingStoreIds.has(value)) { + context.report({ + node, + messageId: 'duplicatedStoreIds' + }) + } else { + usingStoreIds.add(value) + } + } + } + } +}) diff --git a/tests/rules/no-duplicate-store-ids.test.ts b/tests/rules/no-duplicate-store-ids.test.ts new file mode 100644 index 0000000..d047b08 --- /dev/null +++ b/tests/rules/no-duplicate-store-ids.test.ts @@ -0,0 +1,43 @@ +import rule, { RULE_NAME } from '../../src/rules/no-duplicate-store-ids' +import { ruleTester } from '../rule-tester' + +ruleTester.run(RULE_NAME, rule, { + valid: [ + { + code: `import { defineStore } from 'pinia' + export const useCounterStore = defineStore()` + }, + { + code: `import { defineStore } from 'pinia' +export const useCounterStore = defineStore('counter', () => { + const count = ref(0) + return { count } +}) + export const useTodoStore = defineStore('todo', () => { + const todo = ref(0) + return { todo } +})` + } + ], + invalid: [ + { + code: `import { defineStore } from 'pinia' + +const useCounterStore = defineStore('counter', () => { + const count = ref(0) + return { count } +}) + +const useCounter2Store = defineStore('counter', () => { +})`, + errors: [ + { + messageId: 'duplicatedStoreIds' + }, + { + messageId: 'duplicatedStoreIds' + } + ] + } + ] +}) From 4760444ab3c36fcd620c21bcd8710c5b77435850 Mon Sep 17 00:00:00 2001 From: Vida Xie <38204901+9romise@users.noreply.github.com> Date: Mon, 8 Apr 2024 19:36:08 +0800 Subject: [PATCH 2/3] fix(no-duplicate-store-ids): change to warn when all rules are enabled --- README.md | 2 +- docs/rules/no-duplicate-store-ids.md | 2 +- src/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 56b4671..514f40e 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ To use the all configuration, extend it in your `.eslintrc` file: | Name                                  | Description | 💼 | ⚠️ | 🚫 | | :------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- | :- | :- | :- | -| [no-duplicate-store-ids](docs/rules/no-duplicate-store-ids.md) | Disallow duplicate store ids | ✅ | | | +| [no-duplicate-store-ids](docs/rules/no-duplicate-store-ids.md) | Disallow duplicate store ids | ✅ | 🌐 | | | [no-return-global-properties](docs/rules/no-return-global-properties.md) | Disallows returning globally provided properties from Pinia stores. | ✅ | 🌐 | | | [prefer-single-store-per-file](docs/rules/prefer-single-store-per-file.md) | Encourages defining each store in a separate file. | | | 🌐 | | [prefer-use-store-naming-convention](docs/rules/prefer-use-store-naming-convention.md) | Enforces the convention of naming stores with the prefix `use` followed by the store name. | | 🌐 | | diff --git a/docs/rules/no-duplicate-store-ids.md b/docs/rules/no-duplicate-store-ids.md index 621df9e..1c958ba 100644 --- a/docs/rules/no-duplicate-store-ids.md +++ b/docs/rules/no-duplicate-store-ids.md @@ -1,6 +1,6 @@ # Disallow duplicate store ids (`pinia/no-duplicate-store-ids`) -💼 This rule is enabled in the ✅ `recommended` config. +💼⚠️ This rule is enabled in the ✅ `recommended` config. This rule _warns_ in the 🌐 `all` config. diff --git a/src/index.ts b/src/index.ts index 6b1b212..05eabb1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,7 +33,7 @@ const allRules = { [preferNamingConventionName]: 'warn', [preferSingleStoreName]: 'off', [noReturnGlobalPropertiesName]: 'warn', - [noDuplicateStoreIdsName]: 'error' + [noDuplicateStoreIdsName]: 'warn' } const recommended = { From c7a434d8d814d60cdb2e7e56e8dc47b8e3af0f3b Mon Sep 17 00:00:00 2001 From: Vida Xie <38204901+9romise@users.noreply.github.com> Date: Sat, 13 Apr 2024 23:06:37 +0800 Subject: [PATCH 3/3] fix(no-duplicate-store-ids): add more context --- src/rules/no-duplicate-store-ids.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/rules/no-duplicate-store-ids.ts b/src/rules/no-duplicate-store-ids.ts index 1825b09..4bd701a 100644 --- a/src/rules/no-duplicate-store-ids.ts +++ b/src/rules/no-duplicate-store-ids.ts @@ -16,7 +16,7 @@ export default createEslintRule({ }, schema: [], messages: { - duplicatedStoreIds: 'No duplicated store ids allowed.' + duplicatedStoreIds: 'No duplicated store ids allowed: {{storeId}}' } }, defaultOptions: [], @@ -35,8 +35,11 @@ export default createEslintRule({ if (usingStoreIds.has(value)) { context.report({ - node, - messageId: 'duplicatedStoreIds' + node: storeId, + messageId: 'duplicatedStoreIds', + data: { + storeId: storeId.value + } }) } else { usingStoreIds.add(value)