From ae4810f854aa5c28a41c5e8a1130fbc2f3a5cd50 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Fri, 28 Jul 2023 15:08:02 +0100 Subject: [PATCH] Chore: Emit theme token usage metrics (#72500) * create rule to find instances of theme variables * emit theme token usage metrics * move awking into script * make sure theme usage is covering ts and tsx files, remove commented lines --- package.json | 1 + packages/grafana-eslint-rules/README.md | 4 ++ packages/grafana-eslint-rules/index.cjs | 2 + .../rules/theme-token-usage.cjs | 51 +++++++++++++++++++ scripts/ci-frontend-metrics.sh | 8 +++ 5 files changed, 66 insertions(+) create mode 100644 packages/grafana-eslint-rules/rules/theme-token-usage.cjs 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}\",