pypi djlint 1.45.0

5 hours ago

Feature

  • New rule H043 reports a <button> written without a type. A type written inside a template block, as in <button {% if a %}type="button"{% endif %}>, counts.
  • New rule H044 reports a header row holding both th and td cells. The empty td that opens a two-axis table's header row is allowed.
  • New rule H045 reports an <iframe> with no accessible name. title, aria-label and aria-labelledby all count, as does a name written by a template tag.
  • New rule H046 reports a positive tabindex. 0 and -1 are left alone, as is a value written by a template tag.
  • New rule H047 reports aria-hidden="true" on an element that takes focus. Hiding a decorative icon is not reported, nor is a disabled control or one with tabindex="-1".
  • New rule H048 reports an aria- attribute that ARIA does not define, such as a misspelled aria-lable. Framework bindings like :aria-label are left alone.
  • New rule H049 reports a viewport that stops the page being zoomed, by user-scalable=no or a maximum-scale below 2.
  • New rule H050 reports an element html no longer defines, such as <center>, <font>, <marquee> or <strike>. Only the opening tag is reported, and a custom element whose name merely starts with one, such as <font-picker>, is left alone.
  • New rule H051 reports a role ARIA does not define for markup, such as role="buton". The roles DPUB-ARIA and GRAPHICS-ARIA add count, abstract ones such as landmark do not, and a value written by a template tag or a framework binding such as :role is left alone.
  • New rule H052 reports a <meta http-equiv="refresh"> that reloads or redirects on a timer, which fails WCAG 2.2.1. A delay of zero is an immediate redirect rather than a timer and is not reported.
  • New option --quote-style / quote_style sets the quotes djLint writes inside template tags, double (the default) or single, and T002 follows it, so {% include 'a.html' %} is no longer reported in a project that writes single quotes. Html attributes stay with H008.
  • New option --sort-attributes / sort_attributes orders a tag's attributes by name, with id first and class second. A tag whose attributes are guarded by a template tag, as in <div {% if x %}a="1"{% endif %} b="2">, keeps the order it was written in.
  • New option --name-endblocks / name_endblocks writes the block's name into the {% endblock %} that closes it, where the block spans several lines, making T003 fixable by running the formatter. A block opened and closed on one line is left alone.
  • New option --no-indent-inner-html / no_indent_inner_html leaves <head> and <body> at the same indent as the <html> that holds them, matching the default VS Code html formatter.
  • New option --keep-br-inline / keep_br_inline keeps <br> on the line of the text it breaks instead of giving it a line of its own. <hr> is unaffected, and the default is unchanged.
  • New option --prefer-configuration lets the file named by --configuration override the project's own pyproject.toml or .djlintrc, which have always won where the two set the same thing. The default is unchanged.
  • Single quoted attribute values are rewritten to double quotes, as H008 asks, so <div class='a'> becomes <div class="a">. Only the names the rule reports are touched, and a value holding a double quote of its own keeps its single quotes.
  • Attribute names that H010 reports are lowercased by the formatter, so <div CLASS="a"> is fixed rather than only reported. A name the rule does not know, such as an svg viewBox, keeps its case, and --ignore-case turns it off.
  • Formatting writes an entity reference as the character it names, so &copy; becomes ©, which is what H023 asks for. Entities that carry syntax or template meaning are left as written, as are <pre>, <textarea>, <script> and <style> bodies, and --no-entity-formatting turns it off.
  • The type that html5 already assumes is dropped, as H024 asks, so <script type="text/javascript"> becomes <script>, and the same for <style> and a stylesheet <link>. A type that means something, such as type="module", is kept.
  • A form's method is lowercased, so method="POST" becomes method="post", which is what H029 asks for. A tag written inside a template tag's string is left alone.

Changed

  • H031 is gone: the keywords meta tag it asked for no longer affects ranking, and bing treats it as a spam signal.
  • H035 is gone; H017 already covers meta.
  • H036 reports only two uses of <br>: a run of two or more, and a break against the inside edge of a block element. Breaks that are part of the content, such as in a postal address, are left alone, and the rule is on by default.
  • T002 is on by default, and --reformat now writes the quotes it asks for, rewriting tag arguments and conditions such as {% if x == 'a' %} to the --quote-style in force. A string already holding the quote it would be rewritten to is left alone.
  • T028 is off by default: the whitespace it strips can be whitespace that renders, so alt="{%- if brand -%}Acme{%- endif -%} logo" comes out as Acmelogo. It stays available with --include=T028.
  • H014 counts blank lines the way the formatter does, so --max-blank-lines 2 and --preserve-blank-lines no longer produce files the linter then rejects. A line holding only whitespace counts as blank, and the report points at the first blank line rather than the content line above it.
  • H020 leaves alone an element whose empty form carries meaning, such as a blank <option> holding a select open.
  • H023 no longer reports an entity for an invisible character, such as &zwnj;, in named, decimal or hex form.
  • --ignore-case applies to the linter as well as the formatter, so H009 and H010 stay quiet when it is set.
  • --profile=all honours every profile's exclude list, so T028 no longer fires on django markup to recommend {%- if -%}, which django rejects.
  • A GitHub annotation names the column as well as the line, so it lands on the tag rather than the start of the line.

Fix

  • Markup inside an attribute value is no longer read as a tag, so <p title="a<br>b"> is not reported: affects D004, J004, H006, H011, H013, H017, H018, H019, H020, H021, H022, H036 and H043.
  • D004, H019 and H021 see attributes written after a template tag containing a >, so the inline style in <div {% if n > 5 %}id="a"{% endif %} style="color:red"> is checked.
  • Rules check the opening tag of <script>, <style>, <pre> and <textarea>, which used to be skipped along with the body, so H024, D004, H008, H010, H011, H012 and H037 can see their attributes. Only the body is skipped now, and its contents are never read as markup.
  • D018 and J018 no longer report a link that already uses a url tag, such as <a href="{% url 'profile' %}" data-src="lazy">; a value now has to look like a path. /static/ and /media/ assets are left to {% static %} rather than reported as routes.
  • H005 reports <html lang="">, and its message asks for a non-empty lang.
  • H005 and H007 ignore custom elements whose name starts with html, such as <html-midi-player>.
  • H007 reports a missing doctype in a file that opens with something it does not recognise, such as an <?xml ... ?> declaration.
  • H008 leaves single quoted values holding a double quote alone, such as title='{% translate "Delete" %}'. An attribute that merely ends in a known name, such as data-title, is no longer treated as that attribute.
  • H009 reports exactly the elements the formatter lowercases, so --reformat always clears it: <IMG>, <INPUT> and <NAV> are now reported, and G, PATH, NAME and CACHE are not. An uppercase name inside an attribute value, as in <p title="x <DIV y">, is no longer read as a tag.
  • H012 sees attributes written after a {{ ... }} in the same tag, so the spaced = in <div {{ attrs }} class = "x"> is reported.
  • H013 accepts a valueless alt, so <img src="a.png" alt> passes as the decorative image alt="" already did.
  • H016 recognises a <title> however it is written, so <title id='page-title'> no longer reads as a missing title and an svg <title> no longer counts as the document's. The report points at the <html> tag.
  • H022 no longer reads an http:// inside another attribute's value as a link, and no longer reports loopback or private hosts: localhost, 127.0.0.1, 192.168.x, 10.x and .local or .test names.
  • H024 covers <link rel="stylesheet" type="text/css">.
  • H025 no longer calls a tag an orphan in a handlebars or go template: a wrapper opened in one branch of {{#if}}...{{else}}...{{/if}} and closed in the other is accepted, as in django and jinja.
  • H029 reads only the form's own method attribute, so <form data-method="POST"> is not reported.
  • H030 stays quiet for a base layout that fills its description in per page, so a head using {% block meta %} is no longer reported.
  • H033 sees a form whose earlier attribute holds a >, as in <form x-show="count > 0" action=" /x/">.
  • H037 counts an attribute written without a value, so <input required required> is reported as the duplicate it is.
  • H042 leaves a label alone in a partial that holds no form control. A control that is commented out still counts, so the label above it is checked.
  • T001 and T027 no longer read a delimiter inside a string as the end of the tag, so {{ x|default('}}') }} is reported neither as needing padding nor as holding an unclosed string. A string that really does run past the tag is still reported.
  • T032 flags extra whitespace in tags that hold a |, (, ), :, ? or %, which were previously skipped. A run of spaces inside a string is still content and is left alone.
  • T034 no longer flags a }% written inside a string, so {% trans "Save 50}% today" %} is left alone.
  • T039 no longer reports a valid block tag holding a nested mapping such as {% set a = {"x": {"y": 1}} %}, a shape --reformat itself produces.
  • T040 allows an empty literal that only feeds a filter, as in {% extends ""|default:"base.html" %}.
  • Markup written as text inside <script>, <style> or <textarea> is left alone; var s = "<DIV CLASS=x>" is no longer rewritten to "<div CLASS=x>".
  • An attribute value written without quotes is now quoted, as H011 asks, so class=one becomes class="one". A value carrying a quote of its own, such as a=b'c, is still left alone.
  • Strings inside a {% set %} or a function call keep their own quotes when they hold the other kind, so {% set s = 'say "hi"' %} is left as written instead of escaped.
  • Arguments spread over their own lines inside {{ }} stay where they are; the first is no longer pulled up against the opening bracket.
  • Spacing is no longer added before a filter that follows a function call: {{ _("test")|upper }} keeps its shape.
  • Handlebars sections such as {{#if}} are no longer taken for the start of a Jinja comment, which left everything up to the next #} unformatted.
  • Jinja's + whitespace control marker is read wherever - is, so {%+ if x %} counts as a block tag, T038 no longer misreports these blocks and the formatter indents them.
  • A {% that opens nothing no longer hides the markup after it: a page quoting template syntax in prose, as in <code>{%-</code>, is formatted rather than skipped down to the next %}.
  • {% written inside javascript is no longer read as a template tag; a <script> holding '{%' and '%}' in its code used to leave its whole line unformatted.
  • A template block opened and closed on one line no longer indents everything after it, and tags beyond the built in set, such as {% language 'de' %}text{% endlanguage %}, are recognised as balanced on a line too.
  • Extra spaces between attributes no longer count towards a tag's length: <input id="a" type="checkbox"> used to flip between spread and joined on every run.
  • Formatting a file twice gives the same result when a <script> or <style> is followed by text. Text sitting directly against the element, as in a<script>x</script>b, is still left alone, since a line break there would render as a space.
  • Indentation is kept for an element opened on the same line as a <script>, <style> or <pre>, so the children of <a class="x">text<style> now sit inside it, and a closing tag written after the block's end tag gives back the level it took.
  • A <pre>, <textarea>, <script> or <style> opened and closed on one line no longer cancels a block opened earlier: <pre>x</pre><script> left the script's body indented as markup, and took a second run to settle.
  • Text inside <pre> and <textarea> that looks like a djLint marker, such as {# djlint:on #} shown as sample code, no longer ends the verbatim block and walks its contents a level further right on every run.
  • Repeated formatting with --preserve-leading-space no longer moves a line further right on every run, as it did for a multi-line {# ... #} comment and every other line inside an indented block.
  • --preserve-leading-space no longer keeps a blank line on one run and drops it on the next when a <pre> or <textarea> is written with leading indentation; --blank-line-after-tag had the same drift from the second tag in its list onwards.
  • With --preserve-leading-space, the contents of a {{ if }} or {{ range }} block no longer lose an indent level when an already formatted go template is formatted again.
  • --blank-line-before-tag no longer splits a line to place its blank line, so a tag written after other content, such as the {% block %} inside a one line {% set %}...{% endset %}, stays where it is.
  • --blank-line-before-tag no longer inserts a blank line into an attribute value, which grew the file on every run. --blank-line-after-tag already left attributes alone.
  • A djlint:off block placed between a tag's attributes no longer crashes the formatter when it contains a backslash, and its text comes back exactly as written.
  • On free-threaded Python, --format-css and --format-js no longer indent a <style> or <script> body by the wrong amount when several files are formatted at once.
  • djlint - --reformat --lint no longer writes its report into the file: the lint findings, the --statistics block and the --github-output annotations go to stderr when stdout is carrying the formatted code. A lint-only run is unchanged.
  • --github-output annotations no longer land in the file either: in CI, where the flag turns itself on from $GITHUB_ACTIONS, cat f.html | djlint - --reformat > f.html wrote ::warning lines into f.html.
  • --statistics prints its summary alongside GitHub annotations. It was dropped whenever the GitHub output was on, which happens by itself in a workflow.
  • Line endings survive djlint -: a CRLF buffer piped to --reformat came back LF throughout. Input that --require-pragma skips is handed back byte for byte.
  • U+2028, U+2029, U+0085, form feed and vertical tab are no longer treated as line breaks; one inside a <script> string was rewritten as a newline and broke the script.
  • Console output is utf-8 on stdout and stderr alike, so a warning no longer reaches a Windows console as \U0001f622 rather than an emoji.
  • Input on stdin that is not valid utf-8 is reported as bad input rather than as a djLint failure.
  • A closed pipe, as in djlint . | head -1, is no longer a failure: it printed a traceback and exited 120.
  • A file reached through more than one of the given paths, as in djlint . templates/, is checked once, so it is no longer reported and counted twice or written twice under --reformat.
  • A trailing or empty entry in custom_blocks, ignore_blocks, custom_html, blank_line_after_tag or blank_line_before_tag, as in ignore_blocks = "raw,", is ignored instead of turning off template indentation altogether or padding every template tag in the file.
  • .gitignore is read only when --use-gitignore or use_gitignore asks for it, so a line djLint cannot parse, such as one ending in a backslash, no longer aborts every run in the project with a traceback.
  • --require-pragma skips a file whose first line it cannot decode instead of aborting the whole run. A file that is checked and turns out undecodable is still reported as a failure.
  • --indent-js and --indent-css override the indent alone, leaving other beautifier settings in the config file's js or css block, such as wrap_line_length, in place.
  • --ignore and --include accept a space after the comma, so --ignore "H011, H013" no longer reports H013.

Performance

  • Linting a large file is many times faster: a 120KB page went from 13 seconds to 0.15, and a 1.7MB run of 60 files from 28 seconds to 1.6. Linting a file with many findings is several times faster too.
  • Formatting is about a quarter faster, for around a tenth of a megabyte more memory: 575 templates from a large Django project took 2.6 seconds of processor time under 1.44.2 and take 1.9 now.

Docs

  • The H026 page no longer claims an empty class or id cannot be targeted: an attribute presence selector such as div[class] does match one, so removing the attribute can change how a page is styled.
  • The GitHub Actions integration is documented: djLint annotates a pull request's diff with findings by itself, and --github-output / --no-github-output override that.
  • --allow-empty-input appears in the command line reference, which had been missing it.
  • H042 is no longer documented as off by default, which it stopped being in 1.42.1.
  • The before-and-after example on the formatter page has been corrected: it showed a template tag with single quotes, which the formatter rewrites now that T002 is on by default.
  • Code samples on the site are highlighted in full again, after attribute names, punctuation and other tokens had lost their colours.
  • The playground tells a browser without web workers that it needs one, instead of failing first.

Don't miss a new djlint release

NewReleases is sending notifications on new releases.