Setting the file. One moment.
Analyze · Web Quality Audit · addyosmani/web-quality-skills · Skills Docs
ContentsBack to the top of the page (opens in a new tab)
scripts/ analyze.sh
Shell · 113 lines · 4 KB
=
"
$3
"
11 if command -v jq > /dev/null 2>&1 ; then
12 jq -n \
13 --arg type " $type " \
14 --arg msg " $msg " \
15 --arg suggestion " $suggestion " \
16 '{success: false, error: {type: $type, message: $msg, retryable: false, suggestion: $suggestion}}'
17 else
18 printf '{"success":false,"error":{"type":"%s","message":"%s","suggestion":"%s","retryable":false}}\n' \
19 " $type " " $msg " " $suggestion "
20 fi
21 exit 1
22 }
23
24 command -v jq > /dev/null 2>&1 || \
25 fail "missing_dependency" "jq is required for safe JSON output" "Install: brew install jq"
26
27 [ $# -ge 1 ] || fail "invalid_input" "No target provided" "Usage: $0 <file_or_directory>"
28 TARGET = " $1 "
29 [ -e " $TARGET " ] || fail "invalid_input" "Target not found: $TARGET " "Pass an existing file or directory path"
30
31 ISSUES = ()
32 WARNINGS = ()
33
34 analyze_html () {
35 local file = " $1 "
36 echo "Analyzing: $file " >&2
37
38 grep -qi "<!doctype html>" " $file " || ISSUES += ( " $file :0: Missing HTML5 doctype" )
39 grep -qi 'charset.*utf-8' " $file " || WARNINGS += ( " $file :0: Missing or non-UTF-8 charset" )
40 grep -qi 'name="viewport"' " $file " || ISSUES += ( " $file :0: Missing viewport meta tag" )
41 grep -qi '<html[^>]*lang=' " $file " || ISSUES += ( " $file :0: Missing lang attribute on <html>" )
42 grep -qi '<title>' " $file " || ISSUES += ( " $file :0: Missing <title> tag" )
43
44 # <img> without alt — two-pass replaces broken PCRE lookahead
45 local alt_count = 0
46 while IFS = : read -r ln tag ; do
47 if grep -qE 'alt=' <<< " $tag " ; then continue ; fi
48 if [ " $alt_count " -ge " $MAX_PER_CATEGORY_PER_FILE " ]; then
49 WARNINGS += ( " $file :0: <img>-without-alt findings truncated (>${ MAX_PER_CATEGORY_PER_FILE } in this file)" )
50 break
51 fi
52 WARNINGS += ( " $file : $ln : <img> without alt attribute" )
53 alt_count = $(( alt_count + 1 ))
54 done < <( grep -noE '<img[^>]*>' " $file " || true )
55
56 # Non-HTTPS URLs with line numbers
57 local http_count = 0
58 while IFS = : read -r ln _ ; do
59 if [ " $http_count " -ge " $MAX_PER_CATEGORY_PER_FILE " ]; then
60 WARNINGS += ( " $file :0: Non-HTTPS URL findings truncated (>${ MAX_PER_CATEGORY_PER_FILE } in this file)" )
61 break
62 fi
63 WARNINGS += ( " $file : $ln : Non-HTTPS URL" )
64 http_count = $(( http_count + 1 ))
65 done < <( grep -noE 'http://[^"' \' '[:space:]>]*' " $file " || true )
66 }
67
68 # Process substitution keeps arrays in main shell (fixes v1 subshell bug)
69 if [ -d " $TARGET " ]; then
70 while IFS = read -r -d '' file ; do
71 analyze_html " $file "
72 done < <( find " $TARGET " \( -name "*.html" -o -name "*.htm" \) -print0 )
73 elif [ -f " $TARGET " ]; then
74 analyze_html " $TARGET "
75 else
76 fail "invalid_input" "Target is not a regular file or directory: $TARGET " "Pass a path to an .html/.htm file or a directory"
77 fi
78
79 issue_total = ${ # ISSUES[ @ ]}
80 warning_total = ${ # WARNINGS[ @ ]}
81
82 to_json_array () {
83 printf '%s\n' " $@ " | jq -Rs 'split("\n") | map(select(length > 0))'
84 }
85
86 if [ " $issue_total " -gt 0 ]; then
87 issues_json = $( to_json_array "${ ISSUES [ @ ] : 0 : $MAX_FINDINGS }" )
88 else
89 issues_json = '[]'
90 fi
91
92 if [ " $warning_total " -gt 0 ]; then
93 warnings_json = $( to_json_array "${ WARNINGS [ @ ] : 0 : $MAX_FINDINGS }" )
94 else
95 warnings_json = '[]'
96 fi
97
98 echo "Scanned. $issue_total issues, $warning_total warnings." >&2
99
100 jq -n \
101 --argjson issues " $issues_json " \
102 --argjson warnings " $warnings_json " \
103 --argjson issue_total " $issue_total " \
104 --argjson warning_total " $warning_total " \
105 --argjson max " $MAX_FINDINGS " \
106 '{
107 success: true,
108 issues: $issues,
109 warnings: $warnings,
110 issueCount: $issue_total,
111 warningCount: $warning_total,
112 truncated: (($issue_total > $max) or ($warning_total > $max))
113 }'