Feat v0.6.12 css isolation (#47)
All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-frontend (push) Successful in 6s
CI/CD / test-go-pg (push) Successful in 2m44s
CI/CD / test-sqlite (push) Successful in 2m47s
CI/CD / build-and-deploy (push) Successful in 1m46s

Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #47.
This commit is contained in:
2026-04-01 11:58:39 +00:00
committed by xcaliber
parent 786bc92768
commit 221ae94f4f
33 changed files with 1385 additions and 1074 deletions

129
scripts/lint-package-css.sh Executable file
View File

@@ -0,0 +1,129 @@
#!/usr/bin/env bash
# lint-package-css.sh — Enforce extension CSS prefix convention
#
# Rule: The FIRST class selector in every rule must start with .ext-{slug}.
# Subsequent classes in compound/descendant selectors are allowed (they're
# scoped under the extension's namespace). Kernel .sw-* classes are always
# allowed. CodeMirror .cm-* classes are allowed as descendants.
#
# Exemptions: :root, @keyframes blocks, @font-face blocks, @media/@supports
# wrappers, CSS comments.
#
# Usage: bash scripts/lint-package-css.sh [packages-dir]
# Exit 0 = all clean, Exit 1 = violations found.
set -euo pipefail
PKG_DIR="${1:-packages}"
VIOLATIONS=0
for css in "$PKG_DIR"/*/css/main.css; do
[ -f "$css" ] || continue
# Derive slug from directory: {pkg_dir}/{slug}/css/main.css
slug="$(basename "$(dirname "$(dirname "$css")")")"
prefix="ext-${slug}"
in_keyframes=0
in_fontface=0
in_comment=0
brace_depth=0
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
# Skip single-line comments
trimmed="${line#"${line%%[![:space:]]*}"}"
[[ "$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
# Skip @-rule wrappers
[[ "$trimmed" =~ ^@ ]] && continue
# Skip :root selectors
[[ "$trimmed" =~ ^:root ]] && continue
# Skip closing braces
[[ "$trimmed" == "}" ]] && continue
# Skip property declarations (contain : but not {)
if [[ "$trimmed" == *":"* ]] && [[ "$trimmed" != *"{"* ]]; then
continue
fi
# Selector line — check if it contains a class
if [[ "$trimmed" == *"."* ]]; then
# Split comma-separated selectors and check each one
# We process the whole line as potentially multiple selectors
IFS=',' read -ra selectors <<< "$trimmed"
for sel in "${selectors[@]}"; do
# Trim whitespace
sel="${sel#"${sel%%[![:space:]]*}"}"
sel="${sel%"${sel##*[![:space:]]}"}"
# Remove trailing { if present
sel="${sel%\{}"
sel="${sel%"${sel##*[![:space:]]}"}"
# Skip if empty
[[ -z "$sel" ]] && continue
# Find the FIRST class selector in this selector
if [[ "$sel" =~ \.([a-zA-Z_][a-zA-Z0-9_-]*) ]]; then
first_cls="${BASH_REMATCH[1]}"
# Allow kernel .sw-* as first class
[[ "$first_cls" == sw-* ]] && continue
# Allow [data-ext=...] scoped selectors
[[ "$sel" =~ ^\[data-ext ]] && continue
# Must start with ext-{slug}
if [[ "$first_cls" != ${prefix}* ]]; then
echo "VIOLATION: $css:$lineno — .${first_cls} (expected .${prefix}-*)"
VIOLATIONS=$((VIOLATIONS + 1))
fi
fi
done
fi
done < "$css"
done
if (( VIOLATIONS > 0 )); then
echo ""
echo "Found $VIOLATIONS violation(s). First class selector in each rule must start with .ext-{slug}-"
exit 1
else
echo "All package CSS files pass prefix enforcement."
exit 0
fi