#!/usr/bin/env bash set -euo pipefail shopt -s nullglob globstar css_files=(assets/*.css assets/components/*.css) js_files=(assets/*.js assets/components/*.js) # Classes other code depends on, which nothing here styles: Cloudflare's # Turnstile script finds its widget by this one. external_classes="cf-turnstile" css_classes=$( { cat "${css_files[@]}" # Components' own styles, in their shadow roots: the css`...` templates # in their scripts. awk '/css`/ { inside = 1 } inside { print } /`;/ { inside = 0 }' "${js_files[@]}" } \ | sed -E 's/url\([^)]*\)//g; s/format\([^)]*\)//g' \ | { grep -ohE '\.[a-zA-Z_][a-zA-Z0-9_-]*' || true; } \ | sed 's/^\.//' echo "$external_classes" | tr ' ' '\n' ) css_classes=$(echo "$css_classes" | sort -u) source_classes=$( # Maud dot syntax (hyphenated only, outside quotes) sed -E 's/"[^"]*"//g' src/**/*.rs \ | { grep -ohE '\.[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)+' || true; } \ | sed 's/^\.//' # class="..." attributes { grep -ohE 'class="[^"]*"' src/**/*.rs "${js_files[@]}" || true; } \ | sed 's/class="//;s/"$//' \ | tr ' ' '\n' \ | { grep -E '^[a-zA-Z]' || true; } # JS classList methods { grep -ohE 'classList\.(add|remove|toggle|contains)\("[^"]*"\)' "${js_files[@]}" || true; } \ | { grep -ohE '"[^"]*"' || true; } \ | tr -d '"' ) source_classes=$(echo "$source_classes" | sort -u) exit_code=0 for class in $css_classes; do if ! grep -rql --include='*.rs' --include='*.js' --exclude-dir=lib "$class" src/ assets/ > /dev/null 2>&1; then echo "CSS class .$class unused" exit_code=1 fi done for class in $source_classes; do if ! echo "$css_classes" | grep -qxF "$class"; then echo "CSS class .$class used but not defined" exit_code=1 fi done exit $exit_code