| | | 1 | | using System.Text.RegularExpressions; |
| | | 2 | | |
| | | 3 | | namespace AspxLint.Core.Rules; |
| | | 4 | | |
| | | 5 | | public sealed class Tag002TagCase : IRule |
| | | 6 | | { |
| | 215 | 7 | | public string Id => "TAG-002"; |
| | 34 | 8 | | public string Name => "Casse incoherente des balises HTML"; |
| | 31 | 9 | | public Severity Severity => Severity.Warning; |
| | | 10 | | public string Description => |
| | 25 | 11 | | "Les balises HTML doivent etre en minuscules pour respecter HTML5/XHTML. Une casse mixte (<DIV>, <Div>) nuit a l |
| | 46 | 12 | | public bool HasFix => true; |
| | | 13 | | |
| | | 14 | | // Detection : tags HTML standard avec une majuscule (hors controles namespaced asp:Foo, uc:Foo, ...). |
| | 5 | 15 | | private static readonly Regex DetectRegex = |
| | 5 | 16 | | new(@"<\/?([A-Z][a-zA-Z0-9]*)\b", RegexOptions.Compiled); |
| | | 17 | | |
| | 5 | 18 | | private static readonly Regex NamespacedTag = |
| | 5 | 19 | | new(@"^<\/?[a-zA-Z]+:", RegexOptions.Compiled); |
| | | 20 | | |
| | | 21 | | // Whitelist des tags HTML qu'on ose remettre en minuscules (le fix JS limite aussi a une whitelist). |
| | 5 | 22 | | private static readonly string[] HtmlTags = |
| | 5 | 23 | | { |
| | 5 | 24 | | "html","head","body","div","span","p","a","img","br","hr", |
| | 5 | 25 | | "ul","ol","li","table","tr","td","th","thead","tbody","tfoot", |
| | 5 | 26 | | "form","input","button","select","option","textarea","label", |
| | 5 | 27 | | "h1","h2","h3","h4","h5","h6","section","article","header","footer", |
| | 5 | 28 | | "nav","main","aside","meta","link","title","script","style", |
| | 5 | 29 | | "strong","em","b","i","u","small","code","pre","blockquote", |
| | 5 | 30 | | "iframe","canvas","svg" |
| | 5 | 31 | | }; |
| | | 32 | | |
| | | 33 | | public IEnumerable<Issue> Detect(string content, string[] lines, RuleContext ctx) |
| | | 34 | | { |
| | | 35 | | for (int i = 0; i < lines.Length; i++) |
| | | 36 | | { |
| | | 37 | | var line = lines[i]; |
| | | 38 | | foreach (Match m in DetectRegex.Matches(line)) |
| | | 39 | | { |
| | | 40 | | // Skip controles namespaced : asp:Label, uc:Header, etc. |
| | | 41 | | if (NamespacedTag.IsMatch(line[m.Index..])) continue; |
| | | 42 | | yield return new Issue(Id, Name, Severity, |
| | | 43 | | i + 1, m.Index + 1, m.Value, |
| | | 44 | | $"Convertir \"{m.Groups[1].Value}\" en minuscules."); |
| | | 45 | | } |
| | | 46 | | } |
| | | 47 | | } |
| | | 48 | | |
| | | 49 | | public string? Fix(string content, RuleContext ctx) |
| | | 50 | | { |
| | | 51 | | // Pour chaque tag de la whitelist, remplace toutes les occurences case-insensitive |
| | | 52 | | // par sa forme minuscule. Ne touche que les chevrons d'ouverture (`<` ou `</`), |
| | | 53 | | // donc les valeurs d'attributs ne sont jamais affectees. |
| | 23 | 54 | | var fixedContent = content; |
| | 2668 | 55 | | foreach (var tag in HtmlTags) |
| | | 56 | | { |
| | 1311 | 57 | | var re = new Regex(@"<(\/?)(" + tag + @")\b", RegexOptions.IgnoreCase); |
| | 1311 | 58 | | fixedContent = re.Replace(fixedContent, m => "<" + m.Groups[1].Value + tag); |
| | | 59 | | } |
| | 23 | 60 | | return fixedContent; |
| | | 61 | | } |
| | | 62 | | } |