From 569f9112ac1359329e04143351cd588e58a98405 Mon Sep 17 00:00:00 2001 From: Christopher Pickering Date: Wed, 6 Oct 2021 14:20:47 +0200 Subject: [PATCH] closes #67, closes #59 --- docs/djlint/changelog.rst | 6 +- docs/djlint/rules.rst | 6 + src/djlint/formatter/attributes.py | 6 + src/djlint/formatter/indent_html.py | 2 +- src/djlint/rules.yaml | 28 +- src/djlint/settings.py | 394 +++++++++++++--------------- tests/test_config.py | 2 +- tests/test_linter.py | 21 ++ tests/test_nunjucks.py | 11 +- 9 files changed, 248 insertions(+), 228 deletions(-) diff --git a/docs/djlint/changelog.rst b/docs/djlint/changelog.rst index d05f90d..cf5abd4 100644 --- a/docs/djlint/changelog.rst +++ b/docs/djlint/changelog.rst @@ -4,12 +4,16 @@ Changelog Next Release ------------ - Added rule H020 to find empty tag pairs +- Added rule H021 to find inline styles +- Added rule H022 to find http links +- Added rule H023 to find entity references +- Added rule H024 to find type on scripts and styles - Improved attribute formatting - Updated ``blank_line_after_tag`` option to add newline regardless of location - Fixed django ``trans`` tag formatting -- Added linter rule H021 to check for inline styles - Added formatting for inline styles - Added formatting for template conditions inside attributes +- Added srcset as possible url location in linter rules 0.5.3 ----- diff --git a/docs/djlint/rules.rst b/docs/djlint/rules.rst index 281477d..27ae4c7 100644 --- a/docs/djlint/rules.rst +++ b/docs/djlint/rules.rst @@ -53,6 +53,12 @@ Codes +--------+-------------------------------------------------------------------------+ | H021 | Inline styles should be avoided. | +--------+-------------------------------------------------------------------------+ +| H022 | Use HTTPS for external links. | ++--------+-------------------------------------------------------------------------+ +| H023 | Do not use entity references. | ++--------+-------------------------------------------------------------------------+ +| H024 | Omit type on scripts and styles. | ++--------+-------------------------------------------------------------------------+ Adding Rules ------------ diff --git a/src/djlint/formatter/attributes.py b/src/djlint/formatter/attributes.py index 5d3418a..a90f026 100644 --- a/src/djlint/formatter/attributes.py +++ b/src/djlint/formatter/attributes.py @@ -39,6 +39,12 @@ def format_template_tags(config: Config, attributes: str) -> str: ) )[-1] else: + # if we don't know where we are, then return what we started with. + if not re.findall( + re.compile(r"^<\w+[^=\"']\s*", re.M), attributes[: match.start()] + ): + return match.group() + attr_name = list( re.finditer( re.compile(r"^<\w+[^=\"']\s*", re.M), attributes[: match.start()] diff --git a/src/djlint/formatter/indent_html.py b/src/djlint/formatter/indent_html.py index c3422cc..edab227 100644 --- a/src/djlint/formatter/indent_html.py +++ b/src/djlint/formatter/indent_html.py @@ -24,7 +24,7 @@ def indent_html(rawcode: str, config: Config) -> str: is_raw_first_line = False is_block_raw = False - slt_html = config.break_html_tags + slt_html = config.indent_html_tags # here using all tags cause we allow empty tags on one line always_slt_html = config.always_single_line_html_tags diff --git a/src/djlint/rules.yaml b/src/djlint/rules.yaml index 9450529..c331e60 100644 --- a/src/djlint/rules.yaml +++ b/src/djlint/rules.yaml @@ -37,14 +37,14 @@ flags: re.DOTALL # this should be using the static path from django settings patterns: - - <(?:link|img|script)\s[^\>]*?(?:href|src)=[\"\']/?static/? + - <(?:link|img|script|source)\s[^\>]*?(?:href|src|srcset)=[\"\']/?static/? - rule: name: J004 message: (Jinja) Static urls should follow {{ url_for('static'..) }} pattern. flags: re.DOTALL # this should be using the static path from django settings patterns: - - <(?:link|img|script)\s[^\>]*?(?:href|src)=[\"\']/?static/? + - <(?:link|img|script|source)\s[^\>]*?(?:href|src|srcset)=[\"\']/?static/? - rule: name: H005 message: Html tag should have lang attribute. @@ -69,7 +69,7 @@ message: Attributes should be double quoted. flags: re.DOTALL|re.I patterns: - - (?:class|id|src|width|height|alt|style|lang|title)=\'[^\']*' + - (?:class|id|src|width|height|alt|style|lang|title|srcset|media)=\'[^\']*' - rule: name: H009 message: Tag names should be lowercase. @@ -81,7 +81,7 @@ message: Attribute names should be lowercase. flags: re.DOTALL patterns: - - <\w+[^\>]+?(?:CLASS|ID|SRC|WIDTH|HEIGHT|ALT|STYLE|LANG|TITLE)= + - <\w+[^\>]+?(?:CLASS|ID|SRC|WIDTH|HEIGHT|ALT|STYLE|LANG|TITLE|MEDIA|SRCSET)= - rule: name: H011 message: Attribute values should be quoted. @@ -208,7 +208,7 @@ | ul | var | video - | wbr)\s+?[^>]*?(?:class|id|src|width|height|alt|style|lang|title|href|action|method|checked|required)=[a-zA-Z_-]+ + | wbr)\s+?[^>]*?(?:class|id|src|width|height|alt|style|lang|title|href|action|method|checked|required|srcset)=[a-zA-Z_-]+ - <(?:meta)\s+?[^>]*?(?:class|id|src|alt|style|lang|title|href|action|method|name)=[a-zA-Z_-]+ - rule: name: H012 @@ -281,3 +281,21 @@ flags: re.I patterns: - <\w+\s[^>]*style(?=[^>]*>) +- rule: + name: H022 + message: Use HTTPS for external links. + flags: re.I + patterns: + - <\w+\s[^>]*?(?:href|data-url|action|src|url|srcset)=[\"|']http://[^>]*?> +- rule: + name: H023 + message: Do not use entity references. + flags: re.I + patterns: + - '&.{5};' +- rule: + name: H024 + message: Omit type on scripts and styles. + flags: re.I + patterns: + - <(?:script|style)[^>]*?type=["|'][^>]*?> diff --git a/src/djlint/settings.py b/src/djlint/settings.py index 0ca81e0..4d8df6c 100644 --- a/src/djlint/settings.py +++ b/src/djlint/settings.py @@ -232,6 +232,132 @@ class Config: | {%[ ]djlint:on[ ]%} """ + # all html tags possible + self.indent_html_tags: str = r""" + a + | abbr + | acronym + | address + | applet + | area + | article + | aside + | audio + | b + | base + | basefont + | bdi + | bdo + | big + | blockquote + | body + | br + | button + | canvas + | caption + | center + | cite + | code + | col + | colgroup + | data + | datalist + | dd + | del + | details + | dfn + | dialog + | dir + | div + | dl + | dt + | em + | embed + | fieldset + | figcaption + | figure + | font + | footer + | form + | frame + | frameset + | h1 + | h2 + | h3 + | h4 + | h5 + | h6 + | head + | header + | hr + | html + | i + | iframe + | icon + | img + | input + | ins + | kbd + | label + | legend + | li + | link + | main + | map + | mark + | meta + | meter + | nav + | noframes + | noscript + | object + | ol + | optgroup + | option + | output + | p + | path + | param + | picture + | progress + | q + | rp + | rt + | ruby + | s + | samp + | script + | section + | select + | small + | source + | span + | strike + | strong + | style + | sub + | summary + | sup + | svg + | table + | tbody + | td + | template + | tfoot + | th + | thead + | time + | title + | tr + | track + | tt + | u + | ul + | var + | video + | wbr + """ + # the contents of these tag blocks will be indented, then unindented self.tag_indent: str = ( r""" @@ -258,117 +384,29 @@ class Config: ) | (?:< (?: - html - | head - | body - | div - | a - | nav - | ul - | ol - | dl - | dd - | dt - | li - | table - | thead - | tbody - | tr - | th - | td - | blockquote - | select - | i - | form - | option - | cache - | optgroup - | fieldset - | legend - | label - | header - | main - | section - | aside - | footer - | figure - | figcaption - | video - | span - | p - | g - | svg - | h\d - | button - | img - | path - | script - | style - | details - | summary + """ + + self.indent_html_tags + + """ ) ) """ ) - self.tag_unindent: str = r"""^ + self.tag_unindent: str = ( + r"""^ (?: (?:\{\{\/) | (?:\{%-?[ ]*?end) ) | (?: None: assert "H021 1:" in result.output +def test_H022(runner: CliRunner, tmp_file: TextIO) -> None: + write_to_file(tmp_file.name, b'') + result = runner.invoke(djlint, [tmp_file.name]) + assert result.exit_code == 1 + assert "H022 1:" in result.output + + +def test_H023(runner: CliRunner, tmp_file: TextIO) -> None: + write_to_file(tmp_file.name, b"—") + result = runner.invoke(djlint, [tmp_file.name]) + assert result.exit_code == 1 + assert "H023 1:" in result.output + + +def test_H024(runner: CliRunner, tmp_file: TextIO) -> None: + write_to_file(tmp_file.name, b'