diff --git a/package.json b/package.json index e0fd3ac3286..2dcb136329c 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,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\" }'", "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", diff --git a/packages/grafana-eslint-rules/README.md b/packages/grafana-eslint-rules/README.md index 75d00ed64a8..b497ed73814 100644 --- a/packages/grafana-eslint-rules/README.md +++ b/packages/grafana-eslint-rules/README.md @@ -11,3 +11,7 @@ Require aria-label JSX properties to not include selectors from the `@grafana/e2 Previously we hijacked the aria-label property to use as E2E selectors as an attempt to "improve accessibility" while making this easier for testing. However, this lead to many elements having poor, verbose, and unnecessary labels. Now, we prefer using data-testid for E2E selectors. + +### `@grafana/theme-token-usage` + +Used to find all instances of `theme` tokens being used in the codebase and emit the counts as metrics. Should **not** be used as an actual lint rule! diff --git a/packages/grafana-eslint-rules/index.cjs b/packages/grafana-eslint-rules/index.cjs index e09ba21d720..2ce0fe5ef27 100644 --- a/packages/grafana-eslint-rules/index.cjs +++ b/packages/grafana-eslint-rules/index.cjs @@ -1,7 +1,9 @@ const noAriaLabelSelectors = require('./rules/no-aria-label-e2e-selectors.cjs'); +const themeTokenUsage = require('./rules/theme-token-usage.cjs'); module.exports = { rules: { 'no-aria-label-selectors': noAriaLabelSelectors, + 'theme-token-usage': themeTokenUsage, }, }; diff --git a/packages/grafana-eslint-rules/rules/theme-token-usage.cjs b/packages/grafana-eslint-rules/rules/theme-token-usage.cjs new file mode 100644 index 00000000000..944fc3add3f --- /dev/null +++ b/packages/grafana-eslint-rules/rules/theme-token-usage.cjs @@ -0,0 +1,51 @@ +// @ts-check +const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils'); + +const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/grafana/grafana#${name}`); + +const themeTokenUsage = createRule({ + create(context) { + return { + Identifier: function (node) { + if (node.name === 'theme') { + const ancestors = context.getAncestors().reverse(); + const paths = []; + let lastAncestor = null; + for (const ancestor of ancestors) { + if (ancestor.type === AST_NODE_TYPES.MemberExpression && ancestor.property.type === AST_NODE_TYPES.Identifier) { + paths.push(ancestor.property.name) + lastAncestor = ancestor; + } else { + break; + } + } + if (paths.length > 0 && lastAncestor) { + paths.unshift('theme'); + context.report({ + node: lastAncestor, + messageId: "themeTokenUsed", + data: { + identifier: paths.join('.') + } + }) + } + } + } + }; + }, + name: 'theme-token-usage', + meta: { + type: 'problem', + docs: { + description: 'Check for theme token usage', + recommended: false, + }, + messages: { + themeTokenUsed: '{{ identifier }}', + }, + schema: [], + }, + defaultOptions: [], +}); + +module.exports = themeTokenUsage; diff --git a/scripts/ci-frontend-metrics.sh b/scripts/ci-frontend-metrics.sh index fcc3d665639..f2f084f0047 100755 --- a/scripts/ci-frontend-metrics.sh +++ b/scripts/ci-frontend-metrics.sh @@ -41,7 +41,15 @@ do BETTERER_STATS+="\"grafana.ci-code.betterer.${name}\": \"${value}\"," done <<< "$(yarn betterer:stats)" +THEME_TOKEN_USAGE="" +while read -r name value +do + THEME_TOKEN_USAGE+=$'\n ' + THEME_TOKEN_USAGE+="\"grafana.ci-code.themeUsage.${name}\": \"${value}\"," +done <<< "$(yarn themes:usage | awk '$4 == "@grafana/theme-token-usage" {print $3}' | awk '{!seen[$0]++}END{for (i in seen) print i, seen[i]}')" + echo "Metrics: { + $THEME_TOKEN_USAGE $BETTERER_STATS \"grafana.ci-code.strictErrors\": \"${ERROR_COUNT}\", \"grafana.ci-code.accessibilityErrors\": \"${ACCESSIBILITY_ERRORS}\",