diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..ab1714cfc --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,17 @@ +name: Lint + +on: pull_request + +jobs: + eslint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: mrdivyansh/eslint-action@v1.0.7 + # GITHUB_TOKEN in forked repositories is read-only + # https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-event-pull_request + if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }} + with: + repo-token: ${{secrets.GITHUB_TOKEN}} + eslint-rc: /pkg/web/.eslintrc.js + execute-on-files: /pkg/web/src diff --git a/pkg/web/.eslintrc.js b/pkg/web/.eslintrc.js index 3a644a39b..64b89afe9 100644 --- a/pkg/web/.eslintrc.js +++ b/pkg/web/.eslintrc.js @@ -8,7 +8,7 @@ module.exports = { 'plugin:@typescript-eslint/recommended', // 'plugin:i18next/recommended', ], - plugins: ['eslint-plugin-prettier'], + plugins: ['eslint-plugin-prettier', 'unused-imports'], parser: '@typescript-eslint/parser', env: { jest: true, @@ -28,6 +28,16 @@ module.exports = { allowForLoopAfterthoughts: true, }, ], + 'no-unused-vars': 0, + '@typescript-eslint/no-unused-vars': 0, + 'unused-imports/no-unused-imports': 'error', + 'unused-imports/no-unused-vars': [ + 'warn', + { vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_' }, + ], + 'prefer-destructuring': 0, + 'array-callback-return': 0, + 'no-useless-escape': 0, 'react/display-name': 'off', // jsx 单引号 'jsx-quotes': [2, 'prefer-single'], diff --git a/pkg/web/package.json b/pkg/web/package.json index 8bf6f1f0d..8040c463d 100644 --- a/pkg/web/package.json +++ b/pkg/web/package.json @@ -40,6 +40,7 @@ "eslint-plugin-import": "^2.25.4", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-react": "^7.28.0", + "eslint-plugin-unused-imports": "^2.0.0", "husky": "^7.0.4", "i18next-scanner": "^3.1.0", "less": "^4.1.2", diff --git a/pkg/web/src/components/BoardChart/index.tsx b/pkg/web/src/components/BoardChart/index.tsx index 041ca72f0..d06c0067a 100644 --- a/pkg/web/src/components/BoardChart/index.tsx +++ b/pkg/web/src/components/BoardChart/index.tsx @@ -226,7 +226,6 @@ function getPercentageChange(oldNumber: any, newNumber: any) { const BoardChart = ({ title, - prefix, countPrefix, desc, Icon, diff --git a/pkg/web/src/components/PieChart/index.tsx b/pkg/web/src/components/PieChart/index.tsx index 49b73f0b0..b0474ed47 100644 --- a/pkg/web/src/components/PieChart/index.tsx +++ b/pkg/web/src/components/PieChart/index.tsx @@ -1,5 +1,5 @@ import { useCraneUrl } from '../../hooks'; -import { Card, CardProps, DateRangePicker, MessagePlugin, Popup } from 'tdesign-react'; +import { Card, DateRangePicker, MessagePlugin, Popup } from 'tdesign-react'; import React, { useState } from 'react'; import ReactEcharts from 'echarts-for-react'; import dayjs from 'dayjs'; @@ -24,6 +24,7 @@ const fetchPieData = (craneUrl: string, title: string, timeDateRangePicker: stri const start = dayjs(timeDateRangePicker[0]).valueOf(); const end = dayjs(timeDateRangePicker[1]).valueOf(); const minutesDuration = Math.round((end - start) / 1000 / 60); + // eslint-disable-next-line no-param-reassign query = query.replaceAll('{DURATION}', minutesDuration.toString()); const { data, isError } = useRangePrometheusQuery({ craneUrl, start, end, step, query }); if (isError) MessagePlugin.error(`[${title}] Check Your Network Or Query Params !!!`); diff --git a/pkg/web/src/components/SeriesLineChart/index.tsx b/pkg/web/src/components/SeriesLineChart/index.tsx index 277f895e8..59266e525 100644 --- a/pkg/web/src/components/SeriesLineChart/index.tsx +++ b/pkg/web/src/components/SeriesLineChart/index.tsx @@ -116,17 +116,7 @@ const buildLineChartOption = (lineStyle: LineStyle | undefined, linesData: ISeri }; }; -const SeriesLineChart = ({ - title, - subTitle, - datePicker, - timeRange, - step, - xAxis, - lines, - lineStyle, - tips, -}: ISeriesLineChart) => { +const SeriesLineChart = ({ title, subTitle, datePicker, timeRange, step, lines, lineStyle }: ISeriesLineChart) => { const { t } = useTranslation(); const craneUrl: any = useCraneUrl(); diff --git a/pkg/web/src/components/common/Editor.tsx b/pkg/web/src/components/common/Editor.tsx index bac71fa57..d344814a9 100644 --- a/pkg/web/src/components/common/Editor.tsx +++ b/pkg/web/src/components/common/Editor.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useRef } from 'react'; import 'monaco-editor/esm/vs/basic-languages/dockerfile/dockerfile.contribution'; import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution'; diff --git a/pkg/web/src/hooks/useDynamicChart.ts b/pkg/web/src/hooks/useDynamicChart.ts index e132d588d..9afff8b4e 100644 --- a/pkg/web/src/hooks/useDynamicChart.ts +++ b/pkg/web/src/hooks/useDynamicChart.ts @@ -7,7 +7,7 @@ import lodashSet from 'lodash/set'; import lodashMap from 'lodash/map'; import { ETheme } from '../types'; -export type TChartColorKey = keyof typeof CHART_COLORS[ETheme.light]; +export type TChartColorKey = keyof (typeof CHART_COLORS)[ETheme.light]; /** * 根据当前主题色返回动态的图表颜色列表 * @param options 图表的固定配置 diff --git a/pkg/web/src/hooks/useGrafanaQueyStr.ts b/pkg/web/src/hooks/useGrafanaQueyStr.ts index 5b1c1380a..d7d595880 100644 --- a/pkg/web/src/hooks/useGrafanaQueyStr.ts +++ b/pkg/web/src/hooks/useGrafanaQueyStr.ts @@ -6,14 +6,14 @@ import React from 'react'; import { useIsNeedSelectNamespace } from './useIsNeedSelectNamespace'; import { useSelector } from './useSelector'; -export const useGrafanaQueryStr = ({ panelId, selectedDashboard }: { panelId: string, selectedDashboard: any }) => { +export const useGrafanaQueryStr = ({ panelId, selectedDashboard }: { panelId: string; selectedDashboard: any }) => { const customRange = useSelector((state) => state.insight.customRange); const selectedNamespace = useSelector((state) => state.insight.selectedNamespace); const discount = useSelector((state) => state.insight.discount); const selectedWorkload = useSelector((state) => state.insight.selectedWorkload); const selectedWorkloadType = useSelector((state) => state.insight.selectedWorkloadType); - const isNeedSelectNamespace = useIsNeedSelectNamespace({selectedDashboard}); + const isNeedSelectNamespace = useIsNeedSelectNamespace({ selectedDashboard }); const [from, to] = React.useMemo( () => [dayjs(customRange.start).toDate().getTime(), dayjs(customRange.end).toDate().getTime()], diff --git a/pkg/web/src/hooks/useIsNeedSelectNamespace.ts b/pkg/web/src/hooks/useIsNeedSelectNamespace.ts index 0bde09329..4dde6ee30 100644 --- a/pkg/web/src/hooks/useIsNeedSelectNamespace.ts +++ b/pkg/web/src/hooks/useIsNeedSelectNamespace.ts @@ -1,14 +1,12 @@ -import { useSelector } from './useSelector'; import { grafanaApi } from 'services/grafanaApi'; -export const useIsNeedSelectNamespace = ({ selectedDashboard }: { selectedDashboard?: any } = {})=> { - +export const useIsNeedSelectNamespace = ({ selectedDashboard }: { selectedDashboard?: any } = {}) => { const dashboardDetail = grafanaApi.useFetchDashboardDetailQuery( { dashboardUid: selectedDashboard?.uid }, { skip: !selectedDashboard?.uid }, ); return (dashboardDetail?.data?.dashboard?.templating?.list ?? []).find( - (item: { name: string }) => (item.name === 'namespace' || item.name === 'Namespace'), + (item: { name: string }) => item.name === 'namespace' || item.name === 'Namespace', ); }; diff --git a/pkg/web/src/layouts/components/Header/HeaderIcon.tsx b/pkg/web/src/layouts/components/Header/HeaderIcon.tsx index fe2992d76..d5028dc20 100644 --- a/pkg/web/src/layouts/components/Header/HeaderIcon.tsx +++ b/pkg/web/src/layouts/components/Header/HeaderIcon.tsx @@ -4,14 +4,12 @@ import { toggleSetting } from 'modules/global'; import { useAppDispatch } from 'modules/store'; import React, { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; import { HelpCircleIcon, Icon, LogoGithubIcon, SettingIcon } from 'tdesign-icons-react'; import { Button, Col, Dropdown, Popup, Row } from 'tdesign-react'; export default memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); - const navigate = useNavigate(); const gotoWiki = () => { window.open('https://gocrane.io/docs'); diff --git a/pkg/web/src/layouts/components/Header/index.tsx b/pkg/web/src/layouts/components/Header/index.tsx index 9e34e4652..2e5a0166a 100644 --- a/pkg/web/src/layouts/components/Header/index.tsx +++ b/pkg/web/src/layouts/components/Header/index.tsx @@ -11,11 +11,6 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { ViewListIcon } from 'tdesign-icons-react'; import { Button, Col, Layout, MessagePlugin, Row, Select } from 'tdesign-react'; import { useFetchClusterListQuery } from '../../../services/clusterApi'; -import { recommendationRuleApi } from '../../../services/recommendationRuleApi'; -import { recommendationApi } from '../../../services/recommendationApi'; -import { prometheusApi } from '../../../services/prometheusApi'; -import { namespaceApi } from '../../../services/namespaceApi'; -import { grafanaApi } from '../../../services/grafanaApi'; const { Header } = Layout; diff --git a/pkg/web/src/modules/insightSlice.ts b/pkg/web/src/modules/insightSlice.ts index af18e2cc2..46e17f51b 100644 --- a/pkg/web/src/modules/insightSlice.ts +++ b/pkg/web/src/modules/insightSlice.ts @@ -17,10 +17,9 @@ export interface InsightState { discount: number; - selectedWorkloadType?: string| null; - - selectedWorkload?: string| null; + selectedWorkloadType?: string | null; + selectedWorkload?: string | null; } export const initialInsightState: InsightState = { diff --git a/pkg/web/src/pages/Cost/CarbonInsight/components/CarbonChart.tsx b/pkg/web/src/pages/Cost/CarbonInsight/components/CarbonChart.tsx index e1771a372..0c422c617 100644 --- a/pkg/web/src/pages/Cost/CarbonInsight/components/CarbonChart.tsx +++ b/pkg/web/src/pages/Cost/CarbonInsight/components/CarbonChart.tsx @@ -12,7 +12,7 @@ const CarbonChart = () => { subTitle: t('(瓦特/小时)'), datePicker: true, step: '1h', - xAxis: {type: 'time'}, + xAxis: { type: 'time' }, lines: [ { name: t('energy consumption'), diff --git a/pkg/web/src/pages/Cost/ClusterOverview/ClusterOverviewPanel.tsx b/pkg/web/src/pages/Cost/ClusterOverview/ClusterOverviewPanel.tsx index 133f84297..77cc2602f 100644 --- a/pkg/web/src/pages/Cost/ClusterOverview/ClusterOverviewPanel.tsx +++ b/pkg/web/src/pages/Cost/ClusterOverview/ClusterOverviewPanel.tsx @@ -1,9 +1,9 @@ import { OverviewSearchPanel } from './OverviewSearchPanel'; import { PanelWrapper } from './PanelWrapper'; -import {useCraneUrl, useSelector} from 'hooks'; +import { useCraneUrl } from 'hooks'; import React, { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import {useFetchDashboardDetailQuery, useFetchDashboardListQuery} from 'services/grafanaApi'; +import { useFetchDashboardDetailQuery, useFetchDashboardListQuery } from 'services/grafanaApi'; import { Row } from 'tdesign-react'; export default memo(() => { diff --git a/pkg/web/src/pages/Cost/ClusterOverview/OverviewSearchPanel.tsx b/pkg/web/src/pages/Cost/ClusterOverview/OverviewSearchPanel.tsx index 83240afbf..693133899 100644 --- a/pkg/web/src/pages/Cost/ClusterOverview/OverviewSearchPanel.tsx +++ b/pkg/web/src/pages/Cost/ClusterOverview/OverviewSearchPanel.tsx @@ -1,32 +1,28 @@ -import {QueryWindow, useQueryWindowOptions} from '../../../models'; +import { QueryWindow, useQueryWindowOptions } from '../../../models'; import CommonStyle from '../../../styles/common.module.less'; import classnames from 'classnames'; -import {Card} from 'components/common/Card'; -import {useSelector} from 'hooks'; -import {insightAction} from 'modules/insightSlice'; +import { Card } from 'components/common/Card'; +import { useSelector } from 'hooks'; +import { insightAction } from 'modules/insightSlice'; import React from 'react'; -import {useTranslation} from 'react-i18next'; -import {useDispatch} from 'react-redux'; -import {DatePicker, DateValue, InputNumber, Radio, RadioValue} from 'tdesign-react'; -import {rangeMap} from 'utils/rangeMap'; - -const ALL_NAMESPACE_VALUE = "ALL"; +import { useTranslation } from 'react-i18next'; +import { useDispatch } from 'react-redux'; +import { DatePicker, DateValue, InputNumber, Radio, RadioValue } from 'tdesign-react'; +import { rangeMap } from 'utils/rangeMap'; export const OverviewSearchPanel = React.memo(() => { const dispatch = useDispatch(); - const {t} = useTranslation(); + const { t } = useTranslation(); const customRange = useSelector((state) => state.insight.customRange); const window = useSelector((state) => state.insight.window); - const clusterId = useSelector((state) => state.insight.selectedClusterId); const discount = useSelector((state) => state.insight.discount); const queryWindowOptions = useQueryWindowOptions(); - return (