All checks were successful
Co-authored-by: Jeffrey Smith <jasafpro@gmail.com> Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
288 lines
8.4 KiB
Bash
Executable File
288 lines
8.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# generate-ui-inventory.sh — Walk kernel + package CSS, extract class selectors with metadata.
|
|
# Outputs ui-inventory.json to stdout.
|
|
# Usage: bash scripts/generate-ui-inventory.sh [css-dir] [packages-dir]
|
|
|
|
set -euo pipefail
|
|
CSS_DIR="${1:-src/css}"
|
|
PKG_DIR="${2:-packages}"
|
|
|
|
# Collect all CSS files: kernel + packages
|
|
FILES=()
|
|
for f in "$CSS_DIR"/*.css; do [ -f "$f" ] && FILES+=("$f"); done
|
|
for f in "$PKG_DIR"/*/css/main.css; do [ -f "$f" ] && FILES+=("$f"); done
|
|
|
|
# Derive surface name from file path
|
|
surface_for() {
|
|
local f="$1"
|
|
if [[ "$f" == "$PKG_DIR"/* ]]; then
|
|
local slug
|
|
slug="$(basename "$(dirname "$(dirname "$f")")")"
|
|
echo "ext/$slug"
|
|
else
|
|
local base
|
|
base="$(basename "$f" .css)"
|
|
echo "kernel/$base"
|
|
fi
|
|
}
|
|
|
|
echo "["
|
|
first_entry=true
|
|
|
|
for f in "${FILES[@]}"; do
|
|
surface="$(surface_for "$f")"
|
|
relpath="$f"
|
|
|
|
in_comment=0
|
|
in_keyframes=0
|
|
in_fontface=0
|
|
brace_depth=0
|
|
media_breakpoint=""
|
|
current_selector=""
|
|
current_line=0
|
|
current_classes=""
|
|
current_spacing=""
|
|
current_fontsize=""
|
|
current_breakpoints=""
|
|
lineno=0
|
|
|
|
while IFS= read -r line; do
|
|
lineno=$((lineno + 1))
|
|
|
|
# Track block comments
|
|
if (( in_comment )); then
|
|
[[ "$line" == *"*/"* ]] && in_comment=0
|
|
continue
|
|
fi
|
|
if [[ "$line" == *"/*"* ]] && [[ "$line" != *"*/"* ]]; then
|
|
in_comment=1
|
|
continue
|
|
fi
|
|
|
|
# Trim leading whitespace
|
|
trimmed="${line#"${line%%[![:space:]]*}"}"
|
|
[[ -z "$trimmed" ]] && continue
|
|
[[ "$trimmed" == /\** ]] && continue
|
|
|
|
# Track @keyframes blocks
|
|
if [[ "$trimmed" =~ ^@keyframes ]]; then
|
|
in_keyframes=1
|
|
continue
|
|
fi
|
|
if (( in_keyframes )); then
|
|
[[ "$trimmed" == "}" ]] && in_keyframes=0
|
|
continue
|
|
fi
|
|
|
|
# Skip @font-face
|
|
if [[ "$trimmed" =~ ^@font-face ]]; then
|
|
in_fontface=1
|
|
continue
|
|
fi
|
|
if (( in_fontface )); then
|
|
[[ "$trimmed" == *"}"* ]] && in_fontface=0
|
|
continue
|
|
fi
|
|
|
|
# Track @media breakpoints
|
|
if [[ "$trimmed" =~ ^@media.*max-width ]]; then
|
|
if [[ "$trimmed" =~ 768 ]]; then
|
|
media_breakpoint="768px"
|
|
elif [[ "$trimmed" =~ 1024 ]]; then
|
|
media_breakpoint="1024px"
|
|
else
|
|
media_breakpoint="other"
|
|
fi
|
|
continue
|
|
fi
|
|
|
|
# Skip other @-rules
|
|
[[ "$trimmed" =~ ^@.+ ]] && continue
|
|
|
|
# Handle closing brace
|
|
if [[ "$trimmed" == "}" ]]; then
|
|
if [[ -n "$media_breakpoint" ]] && (( brace_depth == 0 )); then
|
|
media_breakpoint=""
|
|
fi
|
|
# If we had an open selector block, emit it
|
|
if [[ -n "$current_selector" ]]; then
|
|
# Build breakpoints array
|
|
bp_json="[]"
|
|
if [[ -n "$current_breakpoints" ]]; then
|
|
bp_json="["
|
|
bp_first=true
|
|
for bp in $current_breakpoints; do
|
|
$bp_first && bp_first=false || bp_json="$bp_json,"
|
|
bp_json="$bp_json\"$bp\""
|
|
done
|
|
bp_json="$bp_json]"
|
|
fi
|
|
|
|
# Build spacing array
|
|
sp_json="[]"
|
|
if [[ -n "$current_spacing" ]]; then
|
|
sp_json="["
|
|
sp_first=true
|
|
# Deduplicate
|
|
seen_sp=""
|
|
for sp in $current_spacing; do
|
|
[[ " $seen_sp " == *" $sp "* ]] && continue
|
|
seen_sp="$seen_sp $sp"
|
|
$sp_first && sp_first=false || sp_json="$sp_json,"
|
|
sp_json="$sp_json\"$sp\""
|
|
done
|
|
sp_json="$sp_json]"
|
|
fi
|
|
|
|
# Build font-size array
|
|
fs_json="[]"
|
|
if [[ -n "$current_fontsize" ]]; then
|
|
fs_json="["
|
|
fs_first=true
|
|
for fs in $current_fontsize; do
|
|
$fs_first && fs_first=false || fs_json="$fs_json,"
|
|
fs_json="$fs_json\"$fs\""
|
|
done
|
|
fs_json="$fs_json]"
|
|
fi
|
|
|
|
# Build classes array
|
|
cls_json="["
|
|
cls_first=true
|
|
for cls in $current_classes; do
|
|
$cls_first && cls_first=false || cls_json="$cls_json,"
|
|
cls_json="$cls_json\"$cls\""
|
|
done
|
|
cls_json="$cls_json]"
|
|
|
|
$first_entry && first_entry=false || printf ",\n"
|
|
printf ' {"surface":"%s","component":"%s","classes":%s,"file":"%s","line":%d,"responsive_breakpoints":%s,"spacing_tokens_used":%s,"font_size_tokens_used":%s}' \
|
|
"$surface" "$current_selector" "$cls_json" "$relpath" "$current_line" "$bp_json" "$sp_json" "$fs_json"
|
|
|
|
current_selector=""
|
|
current_classes=""
|
|
current_spacing=""
|
|
current_fontsize=""
|
|
current_breakpoints=""
|
|
fi
|
|
continue
|
|
fi
|
|
|
|
# Selector line — contains a class and an opening brace
|
|
if [[ "$trimmed" == *"."* ]] && [[ "$trimmed" == *"{"* ]]; then
|
|
# Extract the selector part (before {)
|
|
sel_part="${trimmed%%\{*}"
|
|
# Extract the property part (after {), may have properties on the same line
|
|
prop_part="${trimmed#*\{}"
|
|
prop_part="${prop_part%\}}"
|
|
|
|
# Get all class names from the selector
|
|
classes=""
|
|
tmp="$sel_part"
|
|
while [[ "$tmp" =~ \.([a-zA-Z_][a-zA-Z0-9_-]*) ]]; do
|
|
cls="${BASH_REMATCH[1]}"
|
|
classes="$classes $cls"
|
|
tmp="${tmp#*${BASH_REMATCH[0]}}"
|
|
done
|
|
|
|
# Use the first class as the component name
|
|
first_cls=""
|
|
for c in $classes; do first_cls="$c"; break; done
|
|
[[ -z "$first_cls" ]] && continue
|
|
|
|
current_selector="$first_cls"
|
|
current_line=$lineno
|
|
current_classes="$classes"
|
|
current_spacing=""
|
|
current_fontsize=""
|
|
current_breakpoints=""
|
|
[[ -n "$media_breakpoint" ]] && current_breakpoints="$media_breakpoint"
|
|
|
|
# Check inline properties (single-line rules like .foo { padding: ...; })
|
|
if [[ -n "$prop_part" ]]; then
|
|
# Extract spacing tokens
|
|
sp_tmp="$prop_part"
|
|
while [[ "$sp_tmp" =~ var\(--sp-([0-9h]+)\) ]]; do
|
|
current_spacing="$current_spacing --sp-${BASH_REMATCH[1]}"
|
|
sp_tmp="${sp_tmp#*${BASH_REMATCH[0]}}"
|
|
done
|
|
# Extract font-size
|
|
if [[ "$prop_part" =~ font-size:[[:space:]]*([^;]+) ]]; then
|
|
fs_val="${BASH_REMATCH[1]}"
|
|
fs_val="${fs_val%"${fs_val##*[![:space:]]}"}"
|
|
current_fontsize="$current_fontsize $fs_val"
|
|
fi
|
|
fi
|
|
|
|
# If the rule is self-closing (has } on the same line), emit now
|
|
if [[ "$trimmed" == *"}"* ]]; then
|
|
bp_json="[]"
|
|
if [[ -n "$current_breakpoints" ]]; then
|
|
bp_json="[\"$current_breakpoints\"]"
|
|
fi
|
|
sp_json="[]"
|
|
if [[ -n "$current_spacing" ]]; then
|
|
sp_json="["
|
|
sp_first=true
|
|
seen_sp=""
|
|
for sp in $current_spacing; do
|
|
[[ " $seen_sp " == *" $sp "* ]] && continue
|
|
seen_sp="$seen_sp $sp"
|
|
$sp_first && sp_first=false || sp_json="$sp_json,"
|
|
sp_json="$sp_json\"$sp\""
|
|
done
|
|
sp_json="$sp_json]"
|
|
fi
|
|
fs_json="[]"
|
|
if [[ -n "$current_fontsize" ]]; then
|
|
fs_json="["
|
|
fs_first=true
|
|
for fs in $current_fontsize; do
|
|
$fs_first && fs_first=false || fs_json="$fs_json,"
|
|
fs_json="$fs_json\"$fs\""
|
|
done
|
|
fs_json="$fs_json]"
|
|
fi
|
|
cls_json="["
|
|
cls_first=true
|
|
for cls in $current_classes; do
|
|
$cls_first && cls_first=false || cls_json="$cls_json,"
|
|
cls_json="$cls_json\"$cls\""
|
|
done
|
|
cls_json="$cls_json]"
|
|
|
|
$first_entry && first_entry=false || printf ",\n"
|
|
printf ' {"surface":"%s","component":"%s","classes":%s,"file":"%s","line":%d,"responsive_breakpoints":%s,"spacing_tokens_used":%s,"font_size_tokens_used":%s}' \
|
|
"$surface" "$current_selector" "$cls_json" "$relpath" "$current_line" "$bp_json" "$sp_json" "$fs_json"
|
|
|
|
current_selector=""
|
|
current_classes=""
|
|
current_spacing=""
|
|
current_fontsize=""
|
|
current_breakpoints=""
|
|
fi
|
|
continue
|
|
fi
|
|
|
|
# Property line inside an open block
|
|
if [[ -n "$current_selector" ]]; then
|
|
# Extract spacing tokens
|
|
sp_tmp="$trimmed"
|
|
while [[ "$sp_tmp" =~ var\(--sp-([0-9h]+)\) ]]; do
|
|
current_spacing="$current_spacing --sp-${BASH_REMATCH[1]}"
|
|
sp_tmp="${sp_tmp#*${BASH_REMATCH[0]}}"
|
|
done
|
|
# Extract font-size
|
|
if [[ "$trimmed" =~ font-size:[[:space:]]*([^;]+) ]]; then
|
|
fs_val="${BASH_REMATCH[1]}"
|
|
fs_val="${fs_val%"${fs_val##*[![:space:]]}"}"
|
|
current_fontsize="$current_fontsize $fs_val"
|
|
fi
|
|
fi
|
|
|
|
done < "$f"
|
|
done
|
|
|
|
echo ""
|
|
echo "]"
|