Frontend: Update to Eslint 9 (#94823)

* chore(eslint): bump all eslint related packages to latest

* chore(eslint): update linting scripts work with v9

* chore(eslint): introduce flat config

* chore(eslint): delete legacy configs

* feat(grafana-eslint-rules): update rules to use eslint 9 APIs

* chore(eslint): migrate all nested eslintrc files over to root config

* chore(packages): bump eslint dependencies

* feat(betterer): make it work with eslint 9

* style(grafana-data): remove non-existant ban-types rule from disable declarations

* chore(wip): [wip] link eslint-config-grafana

* chore(packages): add @eslint/compat

* chore(eslint): add compat to testing library and fix alerting rules

* chore(eslint): bump grafana eslint-config to v8

* chore(explore): delete legacy eslint config

* chore: clean codeowners file, remove grafana/eslint-config from e2e plugins

* test(eslint-rules): fix no-border-radius-literal and no-aria-label-e2e-selectors rule tests

* Add .js to prettier checks so new eslint.config.js file isn't missed

* chore(eslint): move emotion/syntax-preference to grafana/defaults

* test(eslint): use core-js structured-clone

* revert(services): undo merge backend-format githook changes

* test(eslint-rules): remove structured-clone polyfill from tests

* chore(eslint): add back public/lib/monaco to ignore list, sort alphabetically

* chore(e2e-plugins): remove eslint config 7 from plugins package.json

---------

Co-authored-by: Tom Ratcliffe <tom.ratcliffe@grafana.com>
This commit is contained in:
Jack Westbrook 2024-11-07 16:31:06 +01:00 committed by GitHub
parent 8956400498
commit 8c41137bcf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 788 additions and 1051 deletions

View File

@ -1,8 +1,6 @@
import { BettererFileTest } from '@betterer/betterer';
import { promises as fs } from 'fs';
import { ESLint, Linter } from 'eslint';
import path from 'path';
import { glob } from 'glob';
import { promises as fs } from 'fs';
// Why are we ignoring these?
// They're all deprecated/being removed so doesn't make sense to fix types
@ -82,11 +80,6 @@ function countEslintErrors() {
}
const { baseDirectory } = resolver;
const cli = new ESLint({ cwd: baseDirectory });
// Get the base config to set up parsing etc correctly
// this is by far the slowest part of this code. It takes eslint about 2 seconds just to find the config
const baseConfig = await cli.calculateConfigForFile(filePaths[0]);
const baseRules: Partial<Linter.RulesRecord> = {
'@typescript-eslint/no-explicit-any': 'error',
@ -105,57 +98,52 @@ function countEslintErrors() {
],
};
const config: Linter.Config = {
...baseConfig,
rules: baseRules,
// Be careful when specifying overrides for the same rules as in baseRules - it will... override
// the same rule, not merge them with different configurations
overrides: [
{
files: ['**/*.{ts,tsx}'],
excludedFiles: ['*.{test,spec}.{ts,tsx}', '**/__mocks__/**', '**/public/test/**'],
rules: {
'@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'never' }],
},
const config: Linter.Config[] = [
{
files: ['**/*.{js,jsx,ts,tsx}'],
rules: baseRules,
},
{
files: ['**/*.{ts,tsx}'],
ignores: ['**/*.{test,spec}.{ts,tsx}', '**/__mocks__/**', '**/public/test/**'],
rules: {
'@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'never' }],
},
{
files: ['public/app/**/*.{ts,tsx}'],
rules: {
'no-barrel-files/no-barrel-files': 'error',
},
},
{
files: ['public/app/**/*.{ts,tsx}'],
rules: {
'no-barrel-files/no-barrel-files': 'error',
},
{
files: ['public/**/*.tsx', 'packages/grafana-ui/**/*.tsx'],
excludedFiles: [
'public/app/plugins/**',
'*.story.tsx',
'*.{test,spec}.{ts,tsx}',
'**/__mocks__/**',
'public/test/**',
],
rules: {
'@grafana/no-untranslated-strings': 'error',
},
},
{
files: ['public/**/*.tsx', 'packages/grafana-ui/**/*.tsx'],
ignores: [
'public/app/plugins/**',
'**/*.story.tsx',
'**/*.{test,spec}.{ts,tsx}',
'**/__mocks__/',
'public/test',
],
rules: {
'@grafana/no-untranslated-strings': 'error',
},
],
};
},
];
const runner = new ESLint({
baseConfig: config,
useEslintrc: false,
overrideConfig: config,
cwd: baseDirectory,
warnIgnored: false,
});
const lintResults = await runner.lintFiles(Array.from(filePaths));
lintResults
.filter((lintResult) => lintResult.source)
.forEach(({ messages, filePath }) => {
const file = fileTestResult.addFile(filePath, '');
messages.forEach((message, index) => {
file.addIssue(0, 0, message.message, `${index}`);
});
lintResults.forEach(({ messages, filePath }) => {
const file = fileTestResult.addFile(filePath, '');
messages.forEach((message, index) => {
file.addIssue(0, 0, message.message, `${index}`);
});
});
});
}

View File

@ -1,27 +0,0 @@
.git
.github
.yarn
build
compiled
/data
deployment_tools_config.json
/devenv
dist
/e2e/tmp
node_modules
/pkg
/public/lib/monaco
/scripts/grafana-server/tmp
vendor
e2e/test-plugins
playwright-report
# TS generate from cue by cuetsy
**/*.gen.ts
# Auto-generated internationalization files
/public/locales/_build/
/public/locales/**/*.js
# Auto-generated icon file
/packages/grafana-ui/src/components/Icon/iconBundle.ts

171
.eslintrc
View File

@ -1,171 +0,0 @@
{
"extends": ["@grafana/eslint-config", "plugin:react/jsx-runtime"],
"root": true,
"plugins": [
"@emotion",
"lodash",
"jest",
"import",
"jsx-a11y",
"@grafana",
"no-barrel-files",
// Included so betterer doesn't fail when processing all files,
// as other parts of the code use testing-library plugin
"testing-library",
],
"settings": {
"import/internal-regex": "^(app/)|(@grafana)",
"import/external-module-folders": ["node_modules", ".yarn"],
},
"rules": {
"@emotion/syntax-preference": [2, "object"],
"@grafana/no-border-radius-literal": "error",
"@grafana/no-unreduced-motion": "error",
"react/prop-types": "off",
// need to ignore emotion's `css` prop, see https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md#rule-options
"react/no-unknown-property": ["error", { "ignore": ["css"] }],
"@emotion/jsx-import": "error",
"lodash/import-scope": [2, "member"],
"jest/no-focused-tests": "error",
"import/order": [
"error",
{
"groups": [["builtin", "external"], "internal", "parent", "sibling", "index"],
"newlines-between": "always",
"alphabetize": { "order": "asc" },
},
],
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "react-redux",
"importNames": ["useDispatch", "useSelector"],
"message": "Please import from app/types instead.",
},
{
"name": "react-i18next",
"importNames": ["Trans", "t"],
"message": "Please import from app/core/internationalization instead",
},
{
"name": "react-router-dom",
"message": "Please import from react-router-dom-v5-compat instead"
},
{
"name": "react-router",
"message": "Please import from react-router-dom-v5-compat instead"
}
],
},
],
// Use typescript's no-redeclare for compatibility with overrides
"no-redeclare": "off",
"@typescript-eslint/no-redeclare": ["error"],
},
"overrides": [
{
"files": ["packages/grafana-ui/src/components/uPlot/**/*.{ts,tsx}"],
"rules": {
"react-hooks/rules-of-hooks": "off",
"react-hooks/exhaustive-deps": "off",
},
},
{
"files": ["packages/grafana-ui/src/components/ThemeDemos/**/*.{ts,tsx}"],
"rules": {
"@emotion/jsx-import": "off",
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off",
},
},
{
"files": ["public/dashboards/scripted*.js"],
"rules": {
"no-redeclare": "error",
"@typescript-eslint/no-redeclare": "off",
},
},
{
"extends": ["plugin:jsx-a11y/recommended"],
"files": ["**/*.tsx"],
"excludedFiles": ["**/*.{spec,test}.tsx"],
"rules": {
// rules marked "off" are those left in the recommended preset we need to fix
// we should remove the corresponding line and fix them one by one
// any marked "error" contain specific overrides we'll need to keep
"jsx-a11y/no-autofocus": [
"error",
{
"ignoreNonDOM": true,
},
],
"jsx-a11y/label-has-associated-control": [
"error",
{
"controlComponents": ["NumberInput"],
"depth": 2,
},
],
},
},
{
"files": [
"public/app/plugins/datasource/azuremonitor/*.{ts,tsx}",
"public/app/plugins/datasource/azuremonitor/**/*.{ts,tsx}",
"public/app/plugins/datasource/cloud-monitoring/*.{ts,tsx}",
"public/app/plugins/datasource/cloud-monitoring/**/*.{ts,tsx}",
"public/app/plugins/datasource/elasticsearch/*.{ts,tsx}",
"public/app/plugins/datasource/elasticsearch/**/*.{ts,tsx}",
"public/app/plugins/datasource/grafana-postgresql-datasource/*.{ts,tsx}",
"public/app/plugins/datasource/grafana-postgresql-datasource/**/*.{ts,tsx}",
"public/app/plugins/datasource/grafana-pyroscope-datasource/*.{ts,tsx}",
"public/app/plugins/datasource/grafana-pyroscope-datasource/**/*.{ts,tsx}",
"public/app/plugins/datasource/grafana-testdata-datasource/*.{ts,tsx}",
"public/app/plugins/datasource/grafana-testdata-datasource/**/*.{ts,tsx}",
"public/app/plugins/datasource/jaeger/*.{ts,tsx}",
"public/app/plugins/datasource/jaeger/**/*.{ts,tsx}",
"public/app/plugins/datasource/loki/*.{ts,tsx}",
"public/app/plugins/datasource/loki/**/*.{ts,tsx}",
"public/app/plugins/datasource/mysql/*.{ts,tsx}",
"public/app/plugins/datasource/mysql/**/*.{ts,tsx}",
"public/app/plugins/datasource/parca/*.{ts,tsx}",
"public/app/plugins/datasource/parca/**/*.{ts,tsx}",
"public/app/plugins/datasource/tempo/*.{ts,tsx}",
"public/app/plugins/datasource/tempo/**/*.{ts,tsx}",
"public/app/plugins/datasource/loki/*.{ts,tsx}",
"public/app/plugins/datasource/loki/**/*.{ts,tsx}",
"public/app/plugins/datasource/elasticsearch/*.{ts,tsx}",
"public/app/plugins/datasource/elasticsearch/**/*.{ts,tsx}",
"public/app/plugins/datasource/cloudwatch/*.{ts,tsx}",
"public/app/plugins/datasource/cloudwatch/**/*.{ts,tsx}",
"public/app/plugins/datasource/zipkin/*.{ts,tsx}",
"public/app/plugins/datasource/zipkin/**/*.{ts,tsx}",
],
"settings": {
"import/resolver": {
"node": {
"extensions": [".ts", ".tsx"],
},
},
},
"rules": {
"import/no-restricted-paths": [
"error",
{
"zones": [
{
"target": "./public/app/plugins",
"from": "./public",
"except": ["./app/plugins"],
"message": "Core plugins are not allowed to depend on Grafana core packages",
},
],
},
],
},
},
],
}

3
.github/CODEOWNERS vendored
View File

@ -382,7 +382,7 @@
/.nxignore @grafana/frontend-ops
/tsconfig.json @grafana/frontend-ops
/.editorconfig @grafana/frontend-ops
/.eslintignore @grafana/frontend-ops
/eslint.config.js @grafana/frontend-ops
/.gitattributes @grafana/frontend-ops
/.gitignore @grafana/frontend-ops
/.nvmrc @grafana/frontend-ops
@ -392,7 +392,6 @@
/yarn.lock @grafana/frontend-ops
/lerna.json @grafana/frontend-ops
/.prettierrc.js @grafana/frontend-ops
/.eslintrc @grafana/frontend-ops
/.vim @zoltanbedi
/jest.config.js @grafana/frontend-ops
/latest.json @grafana/frontend-ops

View File

@ -11,7 +11,6 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"devDependencies": {
"@grafana/eslint-config": "7.0.0",
"@grafana/plugin-configs": "11.4.0-pre",
"@types/lodash": "4.17.7",
"@types/node": "20.14.14",

View File

@ -11,7 +11,6 @@
"author": "Grafana",
"license": "Apache-2.0",
"devDependencies": {
"@grafana/eslint-config": "7.0.0",
"@grafana/plugin-configs": "11.4.0-pre",
"@types/lodash": "4.17.7",
"@types/node": "20.14.14",

312
eslint.config.js Normal file
View File

@ -0,0 +1,312 @@
// @ts-check
const emotionPlugin = require('@emotion/eslint-plugin');
const { fixupPluginRules } = require('@eslint/compat');
const importPlugin = require('eslint-plugin-import');
const jestPlugin = require('eslint-plugin-jest');
const jestDomPlugin = require('eslint-plugin-jest-dom');
const jsxA11yPlugin = require('eslint-plugin-jsx-a11y');
const lodashPlugin = require('eslint-plugin-lodash');
const barrelPlugin = require('eslint-plugin-no-barrel-files');
const reactPlugin = require('eslint-plugin-react');
const testingLibraryPlugin = require('eslint-plugin-testing-library');
const grafanaConfig = require('@grafana/eslint-config/flat');
const grafanaPlugin = require('@grafana/eslint-plugin');
/**
* @type {Array<import('eslint').Linter.Config>}
*/
module.exports = [
{
name: 'grafana/ignores',
ignores: [
'.github',
'.yarn',
'**/.*', // dotfiles aren't ignored by default in FlatConfig
'**/*.gen.ts',
'**/build/',
'**/compiled/',
'**/dist/',
'data/',
'deployment_tools_config.json',
'devenv',
'e2e/test-plugins',
'e2e/tmp',
'packages/grafana-ui/src/components/Icon/iconBundle.ts',
'pkg',
'playwright-report',
'public/lib/monaco/', // this path is no longer required but local dev environments may still have it
'public/locales/_build',
'public/locales/**/*.js',
'public/vendor/',
'scripts/grafana-server/tmp',
],
},
grafanaConfig,
{
name: 'react/jsx-runtime',
// @ts-ignore - not sure why but flat config is typed as a maybe?
...reactPlugin.configs.flat['jsx-runtime'],
},
{
name: 'grafana/defaults',
linterOptions: {
// This reports unused disable directives that we can clean up but
// it also conflicts with the betterer eslint rules so disabled
reportUnusedDisableDirectives: false,
},
files: ['**/*.{ts,tsx,js}'],
plugins: {
'@emotion': emotionPlugin,
lodash: lodashPlugin,
jest: jestPlugin,
import: importPlugin,
'jsx-a11y': jsxA11yPlugin,
'no-barrel-files': barrelPlugin,
'@grafana': grafanaPlugin,
},
settings: {
'import/internal-regex': '^(app/)|(@grafana)',
'import/external-module-folders': ['node_modules', '.yarn'],
},
rules: {
'@grafana/no-border-radius-literal': 'error',
'@grafana/no-unreduced-motion': 'error',
'react/prop-types': 'off',
// need to ignore emotion's `css` prop, see https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md#rule-options
'react/no-unknown-property': ['error', { ignore: ['css'] }],
'@emotion/jsx-import': 'error',
'@emotion/syntax-preference': [2, 'object'],
'lodash/import-scope': [2, 'member'],
'jest/no-focused-tests': 'error',
'import/order': [
'error',
{
groups: [['builtin', 'external'], 'internal', 'parent', 'sibling', 'index'],
'newlines-between': 'always',
alphabetize: { order: 'asc' },
},
],
'no-restricted-imports': [
'error',
{
paths: [
{
name: 'react-redux',
importNames: ['useDispatch', 'useSelector'],
message: 'Please import from app/types instead.',
},
{
name: 'react-i18next',
importNames: ['Trans', 't'],
message: 'Please import from app/core/internationalization instead',
},
],
},
],
// Use typescript's no-redeclare for compatibility with overrides
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': ['error'],
},
},
{
name: 'grafana/uplot-overrides',
files: ['packages/grafana-ui/src/components/uPlot/**/*.{ts,tsx}'],
rules: {
'react-hooks/rules-of-hooks': 'off',
'react-hooks/exhaustive-deps': 'off',
},
},
{
name: 'grafana/theme-demo-overrides',
files: ['packages/grafana-ui/src/components/ThemeDemos/**/*.{ts,tsx}'],
rules: {
'@emotion/jsx-import': 'off',
'react/jsx-uses-react': 'off',
'react/react-in-jsx-scope': 'off',
},
},
{
name: 'grafana/public-dashboards-overrides',
files: ['public/dashboards/scripted*.js'],
rules: {
'no-redeclare': 'error',
'@typescript-eslint/no-redeclare': 'off',
},
},
{
name: 'grafana/jsx-a11y-overrides',
files: ['**/*.tsx'],
ignores: ['**/*.{spec,test}.tsx'],
rules: {
// rules marked "off" are those left in the recommended preset we need to fix
// we should remove the corresponding line and fix them one by one
// any marked "error" contain specific overrides we'll need to keep
'jsx-a11y/no-autofocus': [
'error',
{
ignoreNonDOM: true,
},
],
'jsx-a11y/label-has-associated-control': [
'error',
{
controlComponents: ['NumberInput'],
depth: 2,
},
],
},
},
{
name: 'grafana/data-overrides',
files: ['packages/grafana-data/**/*.{ts,tsx}'],
ignores: ['packages/grafana-data/src/**/*.{spec,test}.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{
patterns: ['@grafana/runtime', '@grafana/ui', '@grafana/data'],
},
],
},
},
{
name: 'grafana/ui-overrides',
files: ['packages/grafana-ui/**/*.{ts,tsx}'],
ignores: ['packages/grafana-ui/**/*.{test,story}.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{
patterns: ['@grafana/runtime', '@grafana/data/*', '@grafana/ui', '@grafana/e2e-selectors/*'],
paths: [
{
name: 'react-i18next',
importNames: ['Trans', 't'],
message: 'Please import from grafana-ui/src/utils/i18n instead',
},
],
},
],
},
},
{
name: 'grafana/schema-overrides',
files: ['packages/grafana-schema/**/*.{ts,tsx}'],
ignores: ['packages/grafana-schema/**/*.test.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{
patterns: ['@grafana/*'],
},
],
},
},
{
name: 'grafana/runtime-overrides',
files: ['packages/grafana-runtime/**/*.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{
patterns: ['@grafana/runtime', '@grafana/data/*', '@grafana/ui/*', '@grafana/e2e/*'],
},
],
},
},
{
name: 'grafana/flamegraph-overrides',
files: ['packages/grafana-flamegraph/**/*.{ts,tsx}'],
ignores: ['packages/grafana-flamegraph/**/*.{test,story}.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{
patterns: ['@grafana/runtime', '@grafana/e2e', '@grafana/e2e-selectors/*'],
},
],
},
},
{
name: 'grafana/alerting-overrides',
files: ['public/app/features/alerting/**/*.{ts,tsx,js,jsx}'],
rules: {
'dot-notation': 'error',
'prefer-const': 'error',
'react/no-unused-prop-types': 'error',
},
},
{
name: 'grafana/alerting-test-overrides',
plugins: {
'testing-library': fixupPluginRules({ rules: testingLibraryPlugin.rules }),
'jest-dom': jestDomPlugin,
},
files: [
'public/app/features/alerting/**/__tests__/**/*.[jt]s?(x)',
'public/app/features/alerting/**/?(*.)+(spec|test).[jt]s?(x)',
],
rules: {
...testingLibraryPlugin.configs['flat/react'].rules,
...jestDomPlugin.configs['flat/recommended'].rules,
'testing-library/prefer-user-event': 'error',
'jest/expect-expect': ['error', { assertFunctionNames: ['expect*', 'reducerTester'] }],
},
},
{
name: 'grafana/explore-traceview-overrides',
files: ['public/app/features/explore/TraceView/components/demo/**/*.{ts,tsx,js,jsx}'],
rules: {
'import/no-extraneous-dependencies': 'off',
},
},
{
name: 'grafana/decoupled-plugins-overrides',
files: [
'public/app/plugins/datasource/azuremonitor/**/*.{ts,tsx}',
'public/app/plugins/datasource/cloud-monitoring/**/*.{ts,tsx}',
'public/app/plugins/datasource/cloudwatch/**/*.{ts,tsx}',
'public/app/plugins/datasource/elasticsearch/**/*.{ts,tsx}',
'public/app/plugins/datasource/elasticsearch/**/*.{ts,tsx}',
'public/app/plugins/datasource/grafana-postgresql-datasource/**/*.{ts,tsx}',
'public/app/plugins/datasource/grafana-pyroscope-datasource/**/*.{ts,tsx}',
'public/app/plugins/datasource/grafana-testdata-datasource/**/*.{ts,tsx}',
'public/app/plugins/datasource/jaeger/**/*.{ts,tsx}',
'public/app/plugins/datasource/loki/**/*.{ts,tsx}',
'public/app/plugins/datasource/loki/**/*.{ts,tsx}',
'public/app/plugins/datasource/mysql/**/*.{ts,tsx}',
'public/app/plugins/datasource/parca/**/*.{ts,tsx}',
'public/app/plugins/datasource/tempo/**/*.{ts,tsx}',
'public/app/plugins/datasource/zipkin/**/*.{ts,tsx}',
],
plugins: {
import: importPlugin,
},
settings: {
'import/resolver': {
node: {
extensions: ['.ts', '.tsx'],
},
},
},
rules: {
'import/no-restricted-paths': [
'error',
{
zones: [
{
target: './public/app/plugins',
from: './public',
except: ['./app/plugins'],
message: 'Core plugins are not allowed to depend on Grafana core packages',
},
],
},
],
},
},
];

View File

@ -27,7 +27,7 @@
"test:coverage:changes": "jest --coverage --changedSince=origin/main",
"test:accessibility-report": "./scripts/generate-a11y-report.sh",
"lint": "yarn run lint:ts && yarn run lint:sass",
"lint:ts": "eslint . --ext .js,.tsx,.ts --cache",
"lint:ts": "eslint . --cache",
"lint:sass": "yarn stylelint '{public/sass,packages}/**/*.scss' --cache",
"test:ci": "mkdir -p reports/junit && JEST_JUNIT_OUTPUT_DIR=reports/junit jest --ci --reporters=default --reporters=jest-junit -w ${TEST_MAX_WORKERS:-100%}",
"lint:fix": "yarn lint:ts --fix",
@ -36,7 +36,7 @@
"packages:prepare": "lerna version --no-push --no-git-tag-version --force-publish --exact",
"packages:pack": "mkdir -p ./npm-artifacts && lerna exec --no-private -- yarn pack --out \"../../npm-artifacts/%s-%v.tgz\"",
"packages:typecheck": "nx run-many -t typecheck --projects='tag:scope:package'",
"prettier:check": "prettier --check --list-different=false --log-level=warn \"**/*.{ts,tsx,scss,md,mdx,json}\"",
"prettier:check": "prettier --check --list-different=false --log-level=warn \"**/*.{ts,tsx,scss,md,mdx,json,js}\"",
"prettier:checkDocs": "prettier --check --list-different=false --log-level=warn \"docs/**/*.md\" \"*.md\" \"packages/**/*.{ts,tsx,scss,md,mdx,json}\"",
"prettier:write": "prettier --list-different \"**/*.{js,ts,tsx,scss,md,mdx,json}\" --write",
"start": "NODE_ENV=dev nx exec -- webpack --config scripts/webpack/webpack.dev.js --watch",
@ -46,7 +46,7 @@
"storybook": "yarn workspace @grafana/ui storybook --ci",
"storybook:build": "yarn workspace @grafana/ui storybook:build",
"themes-generate": "esbuild --target=es6 ./scripts/cli/generateSassVariableFiles.ts --bundle --platform=node --tsconfig=./scripts/cli/tsconfig.json | node",
"themes:usage": "eslint . --ext .tsx,.ts --ignore-pattern '*.test.ts*' --ignore-pattern '*.spec.ts*' --cache --rule '{ @grafana/theme-token-usage: \"error\" }'",
"themes:usage": "eslint . --ignore-pattern '*.test.ts*' --ignore-pattern '*.spec.ts*' --cache --plugin '@grafana' --rule '{ @grafana/theme-token-usage: \"error\" }'",
"typecheck": "tsc --noEmit && yarn run packages:typecheck",
"plugins:build-bundled": "find plugins-bundled -name package.json -not -path '*/node_modules/*' -execdir yarn build \\;",
"watch": "yarn start -d watch,start core:start --watchTheme",
@ -77,7 +77,8 @@
"@betterer/eslint": "5.4.0",
"@cypress/webpack-preprocessor": "6.0.2",
"@emotion/eslint-plugin": "11.12.0",
"@grafana/eslint-config": "7.0.0",
"@eslint/compat": "^1.2.0",
"@grafana/eslint-config": "8.0.0",
"@grafana/eslint-plugin": "link:./packages/grafana-eslint-rules",
"@grafana/plugin-e2e": "^1.11.0",
"@grafana/tsconfig": "^2.0.0",
@ -90,6 +91,7 @@
"@react-types/shared": "3.25.0",
"@rtk-query/codegen-openapi": "^1.2.0",
"@rtsao/plugin-proposal-class-properties": "7.0.1-patch.1",
"@stylistic/eslint-plugin-ts": "^2.9.0",
"@swc/core": "1.4.2",
"@swc/helpers": "0.5.13",
"@testing-library/dom": "10.4.0",
@ -152,8 +154,8 @@
"@types/webpack-assets-manifest": "^5",
"@types/webpack-env": "^1.18.4",
"@types/yargs": "17.0.33",
"@typescript-eslint/eslint-plugin": "6.21.0",
"@typescript-eslint/parser": "6.21.0",
"@typescript-eslint/eslint-plugin": "8.9.0",
"@typescript-eslint/parser": "8.9.0",
"autoprefixer": "10.4.20",
"babel-loader": "9.2.1",
"blob-polyfill": "9.0.20240710",
@ -171,19 +173,19 @@
"esbuild": "0.24.0",
"esbuild-loader": "4.2.2",
"esbuild-plugin-browserslist": "^0.15.0",
"eslint": "8.57.0",
"eslint": "9.12.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "28.8.3",
"eslint-plugin-jest-dom": "^5.4.0",
"eslint-plugin-jsdoc": "48.11.0",
"eslint-plugin-jsdoc": "50.4.1",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-lodash": "7.4.0",
"eslint-plugin-no-barrel-files": "^1.1.0",
"eslint-plugin-lodash": "8.0.0",
"eslint-plugin-no-barrel-files": "^1.1.1",
"eslint-plugin-react": "7.37.2",
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-testing-library": "^6.2.2",
"eslint-scope": "^8.0.0",
"eslint-plugin-react-hooks": "5.0.0",
"eslint-plugin-testing-library": "^6.3.0",
"eslint-scope": "^8.1.0",
"eslint-webpack-plugin": "4.2.0",
"expose-loader": "5.0.0",
"fork-ts-checker-webpack-plugin": "9.0.2",

View File

@ -1,13 +0,0 @@
{
"rules": {
"no-restricted-imports": ["error", { "patterns": ["@grafana/runtime", "@grafana/ui", "@grafana/data"] }],
},
"overrides": [
{
"files": ["**/*.test.{ts,tsx}"],
"rules": {
"no-restricted-imports": "off",
},
},
],
}

View File

@ -1,4 +1,4 @@
/* eslint-disable id-blacklist, no-restricted-imports, @typescript-eslint/ban-types */
/* eslint-disable id-blacklist, no-restricted-imports */
import moment, { Moment } from 'moment-timezone';
import { TimeZone } from '../types/time';

View File

@ -1,7 +1,7 @@
import moment, { Moment, MomentInput, DurationInputArg1, DurationInputArg2 } from 'moment';
import { TimeZone } from '../types/time';
/* eslint-disable id-blacklist, no-restricted-imports, @typescript-eslint/ban-types */
/* eslint-disable id-blacklist, no-restricted-imports */
export interface DateTimeBuiltinFormat {
__momentBuiltinFormatBrand: any;
}

View File

@ -1,4 +1,4 @@
/* eslint-disable id-blacklist, no-restricted-imports, @typescript-eslint/ban-types */
/* eslint-disable id-blacklist, no-restricted-imports */
import { lowerCase } from 'lodash';
import moment from 'moment-timezone';

View File

@ -14,11 +14,11 @@
"directory": "packages/grafana-eslint-rules"
},
"dependencies": {
"@typescript-eslint/utils": "^6.0.0"
"@typescript-eslint/utils": "^8.9.0"
},
"devDependencies": {
"@typescript-eslint/types": "^6.0.0",
"eslint": "8.57.0",
"@typescript-eslint/types": "^8.9.0",
"eslint": "9.12.0",
"tslib": "2.7.0"
},
"private": true

View File

@ -39,7 +39,7 @@ const rule = createRule({
const identifiers = findIdentifiers(node.value.expression);
for (const identifier of identifiers) {
const scope = context.getScope();
const scope = context.sourceCode.getScope(node);
// Find the actual "scoped variable" to inspect it's import
// This is relatively fragile, and will fail to find the import if the variable is reassigned

View File

@ -9,7 +9,7 @@ const noUntranslatedStrings = createRule({
create(context) {
return {
JSXText(node) {
const ancestors = context.getAncestors();
const ancestors = context.sourceCode.getAncestors(node);
const isEmpty = !node.value.trim();
const hasTransAncestor = ancestors.some((ancestor) => {
return (

View File

@ -8,7 +8,7 @@ const themeTokenUsage = createRule({
return {
Identifier: function (node) {
if (node.name === 'theme') {
const ancestors = context.getAncestors().reverse();
const ancestors = context.sourceCode.getAncestors(node).reverse();
const paths = [];
let lastAncestor = null;
for (const ancestor of ancestors) {

View File

@ -3,11 +3,13 @@ import { RuleTester } from 'eslint';
import noAriaLabelE2ESelector from '../rules/no-aria-label-e2e-selectors.cjs';
RuleTester.setDefaultConfig({
parserOptions: {
languageOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
});

View File

@ -3,11 +3,13 @@ import { RuleTester } from 'eslint';
import noBorderRadiusLiteral from '../rules/no-border-radius-literal.cjs';
RuleTester.setDefaultConfig({
parserOptions: {
languageOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
});

View File

@ -1,19 +0,0 @@
{
"rules": {
"no-restricted-imports": [
"error",
{
"patterns": ["@grafana/runtime", "@grafana/e2e", "@grafana/e2e-selectors/*"]
}
]
},
"overrides": [
{
"files": ["**/*.{test,story}.{ts,tsx}"],
"rules": {
"no-restricted-imports": "off",
"react/prop-types": "off"
}
}
]
}

View File

@ -11,7 +11,7 @@
"@swc/core": "1.4.2",
"@types/eslint": "9.6.1",
"copy-webpack-plugin": "12.0.2",
"eslint": "8.57.0",
"eslint": "9.12.0",
"eslint-webpack-plugin": "4.2.0",
"fork-ts-checker-webpack-plugin": "9.0.2",
"glob": "11.0.0",

View File

@ -101,20 +101,20 @@
"@types/react-window": "1.8.8",
"@types/semver": "7.5.8",
"@types/uuid": "9.0.8",
"@typescript-eslint/eslint-plugin": "6.21.0",
"@typescript-eslint/parser": "6.21.0",
"@typescript-eslint/eslint-plugin": "8.9.0",
"@typescript-eslint/parser": "8.9.0",
"copy-webpack-plugin": "12.0.2",
"css-loader": "7.1.2",
"esbuild": "0.24.0",
"eslint": "8.57.0",
"eslint": "9.12.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "28.8.3",
"eslint-plugin-jsdoc": "48.11.0",
"eslint-plugin-jsdoc": "50.4.1",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-lodash": "7.4.0",
"eslint-plugin-lodash": "8.0.0",
"eslint-plugin-react": "7.37.2",
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-react-hooks": "5.0.0",
"eslint-webpack-plugin": "4.2.0",
"fork-ts-checker-webpack-plugin": "9.0.2",
"glob": "11.0.0",

View File

@ -1,5 +0,0 @@
{
"rules": {
"no-restricted-imports": ["error", { "patterns": ["@grafana/runtime", "@grafana/data/*", "@grafana/ui/*", "@grafana/e2e/*"] }]
}
}

View File

@ -1,13 +0,0 @@
{
"rules": {
"no-restricted-imports": ["error", { "patterns": ["@grafana/*"] }]
},
"overrides": [
{
"files": ["**/*.test.{ts,tsx}"],
"rules": {
"no-restricted-imports": "off"
}
}
]
}

View File

@ -1,26 +0,0 @@
{
"rules": {
"no-restricted-imports": [
"error",
{
"patterns": ["@grafana/runtime", "@grafana/data/*", "@grafana/ui", "@grafana/e2e-selectors/*"],
"paths": [
{
"name": "react-i18next",
"importNames": ["Trans", "t"],
"message": "Please import from grafana-ui/src/utils/i18n instead",
},
],
},
],
},
"overrides": [
{
"files": ["**/*.{test,story}.{ts,tsx}"],
"rules": {
"no-restricted-imports": "off",
"react/prop-types": "off",
},
},
],
}

View File

@ -1,19 +0,0 @@
{
"plugins": ["testing-library", "jest-dom"],
"extends": ["plugin:jest-dom/recommended"],
"rules": {
"dot-notation": "error",
"prefer-const": "error",
"react/no-unused-prop-types": "error",
},
"overrides": [
{
"files": ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"],
"extends": ["plugin:testing-library/react"],
"rules": {
"testing-library/prefer-user-event": "error",
"jest/expect-expect": ["error", { "assertFunctionNames": ["expect*", "reducerTester"] }],
},
},
],
}

View File

@ -1,5 +0,0 @@
{
"rules": {
"import/no-extraneous-dependencies": 0
}
}

1056
yarn.lock

File diff suppressed because it is too large Load Diff