From 12c58f6d31c8752c0fb18c1b54c3490c017ccde3 Mon Sep 17 00:00:00 2001 From: asimonok Date: Tue, 21 Nov 2023 18:01:32 +0300 Subject: [PATCH 1/4] Add eslint naming convention rule --- packages/eslint-config/.prettierrc.js | 10 +++ packages/eslint-config/index.js | 61 +++++++++---- packages/eslint-config/package.json | 2 +- packages/eslint-config/tests/eslint.config.js | 10 +++ .../eslint-config/tests/naming-convention.tsx | 85 +++++++++++++++++++ packages/eslint-config/tsconfig.json | 16 ++++ 6 files changed, 166 insertions(+), 18 deletions(-) create mode 100644 packages/eslint-config/.prettierrc.js create mode 100644 packages/eslint-config/tests/eslint.config.js create mode 100644 packages/eslint-config/tests/naming-convention.tsx create mode 100644 packages/eslint-config/tsconfig.json diff --git a/packages/eslint-config/.prettierrc.js b/packages/eslint-config/.prettierrc.js new file mode 100644 index 0000000..b76db6d --- /dev/null +++ b/packages/eslint-config/.prettierrc.js @@ -0,0 +1,10 @@ +module.exports = { + endOfLine: 'auto', + printWidth: 120, + trailingComma: 'es5', + semi: true, + jsxSingleQuote: false, + singleQuote: true, + useTabs: false, + tabWidth: 2, +}; diff --git a/packages/eslint-config/index.js b/packages/eslint-config/index.js index 980e148..b0bc370 100644 --- a/packages/eslint-config/index.js +++ b/packages/eslint-config/index.js @@ -1,34 +1,61 @@ -"use strict"; +'use strict'; /** * Documentation - https://eslint.org/docs/latest/extend/plugins#configs-in-plugins */ module.exports = { - extends: ["plugin:@typescript-eslint/recommended"], - plugins: [ - "@typescript-eslint/eslint-plugin", - "simple-import-sort", - "deprecation", - ], + extends: ['plugin:@typescript-eslint/recommended'], + plugins: ['@typescript-eslint/eslint-plugin', 'simple-import-sort', 'deprecation'], rules: { - "@typescript-eslint/no-explicit-any": "warn", - "@typescript-eslint/no-unused-vars": [ - "error", + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'error', { - args: "after-used", + args: 'after-used', ignoreRestSiblings: true, - vars: "all", + vars: 'all', }, ], - "deprecation/deprecation": ["warn"], - "simple-import-sort/exports": "error", - "simple-import-sort/imports": "error", - "sort-imports": [ - "error", + 'deprecation/deprecation': ['warn'], + 'simple-import-sort/exports': 'error', + 'simple-import-sort/imports': 'error', + 'sort-imports': [ + 'error', { ignoreCase: true, ignoreDeclarationSort: true, }, ], + '@typescript-eslint/naming-convention': [ + 'error', + { + selector: 'default', + format: ['camelCase'], + leadingUnderscore: 'forbid', + }, + { + selector: 'variable', + modifiers: ['global'], + format: ['UPPER_CASE'], + }, + { + selector: 'variable', + types: ['function'], + format: ['camelCase'], + }, + { + selector: 'typeLike', + format: ['PascalCase'], + }, + { + selector: 'typeParameter', + format: ['PascalCase'], + prefix: ['T', 'K'], + }, + { + selector: 'enumMember', + format: ['UPPER_CASE'], + }, + ], }, }; diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 93ae609..9ac19d1 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -29,7 +29,7 @@ }, "scripts": { "build": "", - "lint": "", + "lint": "eslint ./tests/*.ts -c ./tests/eslint.config.js", "test:ci": "" }, "version": "1.1.0" diff --git a/packages/eslint-config/tests/eslint.config.js b/packages/eslint-config/tests/eslint.config.js new file mode 100644 index 0000000..9ce4768 --- /dev/null +++ b/packages/eslint-config/tests/eslint.config.js @@ -0,0 +1,10 @@ +const config = require('../index'); + +module.exports = { + ...config, + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + sourceType: 'module', + }, +}; diff --git a/packages/eslint-config/tests/naming-convention.tsx b/packages/eslint-config/tests/naming-convention.tsx new file mode 100644 index 0000000..76141e1 --- /dev/null +++ b/packages/eslint-config/tests/naming-convention.tsx @@ -0,0 +1,85 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +/** + * Global Constant + */ +const GLOBAL_CONSTANT = {}; + +/** + * Global Function + */ +const func = () => { + /** + * Local Variable + */ + const localVariable = 'hello'; + + return localVariable.trim(); +}; + +/** + * Enum + */ +enum Status { + ACTIVE = 'active', + PAUSED = 'paused', +} + +/** + * Type + */ +type User = object; + +/** + * Interface + */ +interface UserObject {} + +/** + * Generic Type + */ +type ApiResponse = { + data: TData; +}; + +/** + * Property + */ +const ADMIN_USER = { + firstName: 'John', +}; + +/** + * Non matched external property + */ +interface ThirdPartyObject { + // eslint-disable-next-line @typescript-eslint/naming-convention + Name: string; +} + +/** + * Now it's not solved - https://github.com/typescript-eslint/typescript-eslint/issues/2160 + */ +const THIRD_PARTY_OBJECT: ThirdPartyObject = { + // eslint-disable-next-line @typescript-eslint/naming-convention + Name: 'john', +}; + +/** + * Destructing + */ +const printName = () => { + const { firstName } = { firstName: 'John' }; +}; + +/** + * Class + */ +class Service {} + +export const service = new Service(); + +/** + * Component + */ +const Component = () => null; diff --git a/packages/eslint-config/tsconfig.json b/packages/eslint-config/tsconfig.json new file mode 100644 index 0000000..727d39f --- /dev/null +++ b/packages/eslint-config/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "esnext", + "jsx": "react", + "sourceMap": true, + "outDir": "dist", + "strict": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["./"] +} From 7b4f6a8aafab3cc4ce23eec13b3f5e6b6a3d14ec Mon Sep 17 00:00:00 2001 From: asimonok Date: Wed, 22 Nov 2023 09:33:46 +0300 Subject: [PATCH 2/4] Finalize eslint naming convention rule --- packages/eslint-config/.eslintrc | 8 ++ packages/eslint-config/index.js | 85 ++++++++++++------- packages/eslint-config/package.json | 2 +- .../tests/constants/naming-convention.ts | 21 +++++ packages/eslint-config/tests/eslint.config.js | 10 --- .../eslint-config/tests/naming-convention.tsx | 25 +++--- 6 files changed, 99 insertions(+), 52 deletions(-) create mode 100644 packages/eslint-config/.eslintrc create mode 100644 packages/eslint-config/tests/constants/naming-convention.ts delete mode 100644 packages/eslint-config/tests/eslint.config.js diff --git a/packages/eslint-config/.eslintrc b/packages/eslint-config/.eslintrc new file mode 100644 index 0000000..e7315b2 --- /dev/null +++ b/packages/eslint-config/.eslintrc @@ -0,0 +1,8 @@ +{ + "extends": "./index.js", + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "tsconfig.json", + "sourceType": "module" + } +} diff --git a/packages/eslint-config/index.js b/packages/eslint-config/index.js index b0bc370..ee998ab 100644 --- a/packages/eslint-config/index.js +++ b/packages/eslint-config/index.js @@ -1,5 +1,46 @@ 'use strict'; +/** + * Get Naming Convention Rule + */ +const getNamingConventionRule = ({ target = 'default' }) => ({ + '@typescript-eslint/naming-convention': [ + 'error', + target === 'component' + ? { + selector: ['default'], + format: ['strictCamelCase', 'StrictPascalCase'], + } + : { + selector: ['default'], + format: ['strictCamelCase'], + }, + target === 'constant' && { + selector: 'variable', + modifiers: ['global'], + format: ['UPPER_CASE'], + }, + { + selector: 'typeLike', + format: ['StrictPascalCase'], + }, + { + selector: 'typeParameter', + format: ['StrictPascalCase'], + prefix: ['T', 'K'], + }, + { + selector: 'enumMember', + format: ['UPPER_CASE'], + }, + { + selector: ['classProperty', 'objectLiteralProperty'], + format: null, + modifiers: ['requiresQuotes'], + }, + ].filter(Boolean), +}); + /** * Documentation - https://eslint.org/docs/latest/extend/plugins#configs-in-plugins */ @@ -26,36 +67,20 @@ module.exports = { ignoreDeclarationSort: true, }, ], - '@typescript-eslint/naming-convention': [ - 'error', - { - selector: 'default', - format: ['camelCase'], - leadingUnderscore: 'forbid', - }, - { - selector: 'variable', - modifiers: ['global'], - format: ['UPPER_CASE'], - }, - { - selector: 'variable', - types: ['function'], - format: ['camelCase'], - }, - { - selector: 'typeLike', - format: ['PascalCase'], - }, - { - selector: 'typeParameter', - format: ['PascalCase'], - prefix: ['T', 'K'], + ...getNamingConventionRule({ target: 'default' }), + }, + overrides: [ + { + files: ['**/*.tsx'], + rules: { + ...getNamingConventionRule({ target: 'component' }), }, - { - selector: 'enumMember', - format: ['UPPER_CASE'], + }, + { + files: ['**/constants/*.ts', '**/constants.ts'], + rules: { + ...getNamingConventionRule({ target: 'constant' }), }, - ], - }, + }, + ], }; diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 9ac19d1..b42901d 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -29,7 +29,7 @@ }, "scripts": { "build": "", - "lint": "eslint ./tests/*.ts -c ./tests/eslint.config.js", + "lint": "eslint ./tests/**/*.ts ./tests/**/*.tsx", "test:ci": "" }, "version": "1.1.0" diff --git a/packages/eslint-config/tests/constants/naming-convention.ts b/packages/eslint-config/tests/constants/naming-convention.ts new file mode 100644 index 0000000..0316b2d --- /dev/null +++ b/packages/eslint-config/tests/constants/naming-convention.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +/** + * Global Constant + */ +const GLOBAL_CONSTANT = {}; + +/** + * Property + */ +const ADMIN_USER = { + firstName: 'John', +}; + +/** + * Enum + */ +enum Status { + ACTIVE = 'active', + PAUSED = 'paused', +} diff --git a/packages/eslint-config/tests/eslint.config.js b/packages/eslint-config/tests/eslint.config.js deleted file mode 100644 index 9ce4768..0000000 --- a/packages/eslint-config/tests/eslint.config.js +++ /dev/null @@ -1,10 +0,0 @@ -const config = require('../index'); - -module.exports = { - ...config, - parser: '@typescript-eslint/parser', - parserOptions: { - project: 'tsconfig.json', - sourceType: 'module', - }, -}; diff --git a/packages/eslint-config/tests/naming-convention.tsx b/packages/eslint-config/tests/naming-convention.tsx index 76141e1..bcf7331 100644 --- a/packages/eslint-config/tests/naming-convention.tsx +++ b/packages/eslint-config/tests/naming-convention.tsx @@ -1,9 +1,10 @@ +import React from 'react'; /* eslint-disable @typescript-eslint/no-unused-vars */ /** - * Global Constant + * Constant */ -const GLOBAL_CONSTANT = {}; +const globalConstant = {}; /** * Global Function @@ -14,6 +15,8 @@ const func = () => { */ const localVariable = 'hello'; + const func = () => {}; + return localVariable.trim(); }; @@ -45,7 +48,7 @@ type ApiResponse = { /** * Property */ -const ADMIN_USER = { +const adminUser = { firstName: 'John', }; @@ -54,15 +57,11 @@ const ADMIN_USER = { */ interface ThirdPartyObject { // eslint-disable-next-line @typescript-eslint/naming-convention - Name: string; + 'custom-name': string; } -/** - * Now it's not solved - https://github.com/typescript-eslint/typescript-eslint/issues/2160 - */ -const THIRD_PARTY_OBJECT: ThirdPartyObject = { - // eslint-disable-next-line @typescript-eslint/naming-convention - Name: 'john', +const ThirdPartyObject: ThirdPartyObject = { + 'custom-name': 'john', }; /** @@ -82,4 +81,8 @@ export const service = new Service(); /** * Component */ -const Component = () => null; +const Component: React.FC = () => { + const onChange = () => {}; + + return ; +}; From 1c0e5748b7899342ff1864a4e5dddba12f6c3dd7 Mon Sep 17 00:00:00 2001 From: asimonok Date: Wed, 22 Nov 2023 09:34:35 +0300 Subject: [PATCH 3/4] ESLint Config 1.2.0 --- packages/eslint-config/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index b42901d..13c2010 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -32,5 +32,5 @@ "lint": "eslint ./tests/**/*.ts ./tests/**/*.tsx", "test:ci": "" }, - "version": "1.1.0" + "version": "1.2.0" } From 320f59ef75532b7de13a435d79a2e9276fa65750 Mon Sep 17 00:00:00 2001 From: asimonok Date: Wed, 22 Nov 2023 09:39:27 +0300 Subject: [PATCH 4/4] Fix lint errors --- packages/components/src/Collapse/Collapse.styles.ts | 2 +- packages/components/src/Collapse/Collapse.tsx | 4 ++-- packages/eslint-config/package.json | 2 +- packages/jest-selectors/src/types.ts | 4 ++-- packages/jest-selectors/src/utils.ts | 12 ++++++------ 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/components/src/Collapse/Collapse.styles.ts b/packages/components/src/Collapse/Collapse.styles.ts index abdfee8..5b31f61 100644 --- a/packages/components/src/Collapse/Collapse.styles.ts +++ b/packages/components/src/Collapse/Collapse.styles.ts @@ -4,7 +4,7 @@ import { GrafanaTheme2 } from '@grafana/data'; /** * Styles */ -export const Styles = (theme: GrafanaTheme2) => { +export const getStyles = (theme: GrafanaTheme2) => { return { rootOutline: css` border: 1px solid ${theme.colors.border.weak}; diff --git a/packages/components/src/Collapse/Collapse.tsx b/packages/components/src/Collapse/Collapse.tsx index bf0c0d5..993ebe7 100644 --- a/packages/components/src/Collapse/Collapse.tsx +++ b/packages/components/src/Collapse/Collapse.tsx @@ -2,7 +2,7 @@ import { cx } from '@emotion/css'; import { IconButton, useStyles2, useTheme2 } from '@grafana/ui'; import React from 'react'; -import { Styles } from './Collapse.styles'; +import { getStyles } from './Collapse.styles'; /** * Properties @@ -72,7 +72,7 @@ export const Collapse: React.FC = ({ * Styles and Theme */ const theme = useTheme2(); - const styles = useStyles2(Styles); + const styles = useStyles2(getStyles); /** * Fill Options diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 13c2010..6ba723c 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -29,7 +29,7 @@ }, "scripts": { "build": "", - "lint": "eslint ./tests/**/*.ts ./tests/**/*.tsx", + "lint": "eslint './tests/**/*.{ts,tsx}'", "test:ci": "" }, "version": "1.2.0" diff --git a/packages/jest-selectors/src/types.ts b/packages/jest-selectors/src/types.ts index f193939..ac48770 100644 --- a/packages/jest-selectors/src/types.ts +++ b/packages/jest-selectors/src/types.ts @@ -3,9 +3,9 @@ import { GetByBoundAttribute } from '@testing-library/react'; /** * Jest Selector */ -type JestSelector = ( +type JestSelector = ( noThrowOnNotFound?: boolean, - ...args: Args + ...args: TArgs ) => ReturnType; /** diff --git a/packages/jest-selectors/src/utils.ts b/packages/jest-selectors/src/utils.ts index 07beb71..0d95ce8 100644 --- a/packages/jest-selectors/src/utils.ts +++ b/packages/jest-selectors/src/utils.ts @@ -8,10 +8,10 @@ import { JestSelectors } from './types'; * @param enforceTestIdSelectorForKeys */ export const getJestSelectors = - >( - selectors: Selectors, - enforceTestIdSelectorForKeys: Array = [] - ): ((screen: Screen | BoundFunctions) => JestSelectors) => + >( + selectors: TSelectors, + enforceTestIdSelectorForKeys: Array = [] + ): ((screen: Screen | BoundFunctions) => JestSelectors) => (screen) => { return Object.entries(selectors).reduce((acc, [key, selector]) => { /** @@ -22,7 +22,7 @@ export const getJestSelectors = const getElement = (noThrowOnNotFound = false, ...args: unknown[]) => { const value = typeof selector === 'function' ? selector(...args) : selector; - if (value.startsWith('data-testid') || enforceTestIdSelectorForKeys.includes(key as keyof Selectors)) { + if (value.startsWith('data-testid') || enforceTestIdSelectorForKeys.includes(key as keyof TSelectors)) { return noThrowOnNotFound ? screen.queryByTestId(value) : screen.getByTestId(value); } @@ -33,5 +33,5 @@ export const getJestSelectors = ...acc, [key]: getElement, }; - }, {} as JestSelectors); + }, {} as JestSelectors); };