Использование вкладышей

djLint включает в себя множество правил для проверки стиля и валидности ваших шаблонов. Используйте все преимущества линтера, настроив его на использование предустановленного профиля для выбранного вами языка шаблонов.

djlint /path/to/templates --lint

# with custom extensions
djlint /path/to/templates -e html.dj --profile=django

# or to file
djlint /path/to/this.html.j2  --profile=jinja

Включение или отключение правил

Большинство правил включены по умолчанию. Правила могут быть отключены в командной строке с помощью флага --ignore. Правила могут быть включены с помощью флага --include.

Например:

djlint . --lint --include=H017,H035 --ignore=H013,H015

Это также можно сделать через Конфигурация файл.

Правила

Код Значение По умолчанию
D004 (Django) Статические урлы должны следовать шаблону {% static path/to/file %}. ✔️
D018 (Django) Внутренние ссылки должны использовать шаблон {% url ... %}. ✔️
H005 Html-тег должен иметь атрибут lang. ✔️
H006 Тег img должен иметь атрибуты height и width. -
H007 <!DOCTYPE ... > должен присутствовать перед тегом html. ✔️
H008 Атрибуты должны быть заключены в двойные кавычки. ✔️
H009 Имена тегов должны быть в нижнем регистре. ✔️
H010 Имена атрибутов должны быть в нижнем регистре. ✔️
H011 Значения атрибутов должны быть заключены в кавычки. ✔️
H012 Вокруг атрибута = не должно быть пробелов. ✔️
H013 Тег img должен иметь атрибуты alt. ✔️
H014 Более 2 пустых строк. ✔️
H015 После тегов h следует перевод строки. ✔️
H016 Отсутствие тега title в html. ✔️
H017 Пустые теги должны быть самозакрывающимися (противоречит правилу H018). -
H018 Пустые теги по своей природе являются самозакрывающимися и должны заканчиваться символом «>», а не «/>» (конфликт с: H017). -
H019 Замените javascript:abc() на событие on_ и реальный url. ✔️
H020 Найдена пустая пара тегов. Рассмотрите возможность удаления. ✔️
H021 Следует избегать инлайн-стилей. ✔️
H022 Используйте HTTPS для внешних ссылок. ✔️
H023 Не используйте ссылки на сущности. ✔️
H024 Опускайте тип в скриптах и стилях. ✔️
H025 Тег кажется бесхозным. ✔️
H026 Пустые теги id и class могут быть удалены. ✔️
H029 Рассмотрите возможность использования строчных значений метода формы. ✔️
H030 Рассмотрите возможность добавления мета-описания. ✔️
H031 Рассмотрите возможность добавления мета-ключевых слов. -
H033 В действии формы обнаружен лишний пробел. ✔️
J004 (Jinja) Статические урлы должны следовать шаблону {{ url_for('static'...)}}. ✔️
J018 (Jinja) Внутренние ссылки должны использовать шаблон {% url ... %}. ✔️
T001 Переменные должны быть заключены в пробел. Например: {{ this }} ✔️
T002 В тегах следует использовать двойные кавычки. Ex {% extends "this.html" %} -
T003 Конечный блок должен иметь имя. Например: {% endblock body %}. -
T027 В синтаксисе шаблона найдена незакрытая строка. ✔️
T028 Рассмотрите возможность использования тегов без пробелов внутри значений атрибутов. {%- if/for -%} ✔️
T032 В тегах шаблона обнаружены лишние пробелы. ✔️
T034 Вы намеревались использовать {% … %} вместо {% … }%? ✔️
H035 Meta должны быть самозакрывающимися. -
H036 Избегайте использования тегов br. -
H037 Найдено дублирование атрибута. ✔️
T038 Блочный тег не имеет соответствующего закрывающего тега. ✔️
T039 Найден незакрытый тег шаблона. ✔️
T040 Отсутствующее или пустое имя шаблона в теге extends или include. ✔️
H041 Тег закрывается в другом блоке шаблона, чем тот, в котором он был открыт. ✔️
H042 Атрибут for у label не имеет соответствующего id в этом файле. ✔️

Кодовые шаблоны

Первая буква кода соответствует схеме:

  • D: применяется специально для Django
  • H: применяется к html
  • J: применяется специально для Jinja
  • M: применяется специально для Handlebars
  • N: применяется специально для Nunjucks
  • T: применяется в целом к шаблонам

Подробное описание правил

T001

Переменные должны быть заключены в пробел. Например: {{ this }}

Синтаксис шаблона вроде {{user.name}} без внутренних пробелов труднее читать и сравнивать в diff, а непоследовательные пробелы по кодовой базе делают рефакторинг через grep (поиск переменной или тега) ненадёжным: одно и то же выражение существует в нескольких написаниях. Руководства по стилю и Django, и Jinja пишут {{ var }} и {% tag %} с одиночными пробелами.

Не применяется к профилям handlebars и golang.

Неправильно:

{{user.name}}

Правильно:

{{ user.name }}

T002

В тегах следует использовать двойные кавычки. Ex {% extends "this.html" %}

Отключено по умолчанию; включается флагом --include=T002.

Смешение одинарных и двойных кавычек в тегах шаблона ({% extends %}, {% include %}, {% with %}, {% trans %}, {% now %}) приводит к тому, что одно и то же имя шаблона встречается в двух написаниях, и поиск или массовое переименование пропускает половину вхождений. Стандартизация на двойных кавычках делает аргументы тегов согласованными с кавычками HTML-атрибутов в остальной части файла.

Одинарные кавычки внутри значений HTML-атрибутов (например, <span title="{% trans 'x' %}">) не помечаются, так как двойные кавычки атрибута вынуждают использовать там одинарные.

Неправильно:

{% extends 'base.html' %}

Правильно:

{% extends "base.html" %}

T003

Конечный блок должен иметь имя. Например: {% endblock body %}.

Когда {% block %} занимает много строк или блоки вложены, безымянный {% endblock %} не даёт понять, какой блок он закрывает, поэтому при правках легко закрыть не тот блок, и дочерние шаблоны переопределят не то содержимое. Имя у endblock документирует парность и позволяет как djLint, так и Django (который выбрасывает TemplateSyntaxError при несовпадающем имени endblock) поймать блок, закрытый не в том месте. Ошибки парности (незакрытые блоки, осиротевшие endblock и несовпадающие имена) проверяются правилом T038.

Отключено по умолчанию; включается флагом --include=T003.

Имя не требуется, когда блок открывается и закрывается на одной строке, например {% block title %}``{% endblock %}.

Неправильно:

{% block content %}
<p>hello</p>
{% endblock %}

Правильно:

{% block content %}
<p>hello</p>
{% endblock content %}

D004

(Django) Статические урлы должны следовать шаблону {% static path/to/file %}.

Жёстко прописанные пути /static/ обходят тег Django {% static %}: шаблоны ломаются при изменении STATIC_URL (например, при переносе статики на CDN или развёртывании по под-пути) и никогда не подхватывают хэшированные имена файлов из ManifestStaticFilesStorage, что приводит к ошибкам 404 или устаревшим закэшированным ресурсам в продакшене.

Неправильно:

<link rel="stylesheet" href="/static/css/style.css">

Правильно:

<link rel="stylesheet" href="{% static 'css/style.css' %}">

J004

(Jinja) Статические урлы должны следовать шаблону {{ url_for('static'...)}}.

Жёстко прописанные пути /static/ обходят url_for(‘static’, …) из Flask/Jinja: при монтировании приложения под URL-префиксом или изменении каталога/хоста статики ресурсы отдают 404, а добавляемые фреймворком query-строки для сброса кэша теряются.

Неправильно:

<link rel="stylesheet" href="/static/css/style.css">

Правильно:

<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">

H005

Html-тег должен иметь атрибут lang.

Без атрибута lang на <html> скринридеры угадывают правила произношения и могут читать страницу не на том языке, а браузеры не могут корректно предложить перевод, расстановку переносов или локальные кавычки. Объявление языка страницы является критерием успеха 3.1.1 WCAG 2.1 (уровень A).

Неправильно:

<!DOCTYPE html>
<html>
</html>

Правильно:

<!DOCTYPE html>
<html lang="en">
</html>

H006

Тег img должен иметь атрибуты height и width.

Отключено по умолчанию; включается флагом --include=H006.

Когда у <img> нет width и height, браузер не может зарезервировать место до загрузки изображения, и окружающий контент прыгает по мере загрузки картинок. Этот сдвиг вёрстки ухудшает Cumulative Layout Shift (метрику Core Web Vitals) и может приводить к промахам по кликам, пока страница не стабилизировалась.

Неправильно:

<img src="cat.png" alt="Cat">

Правильно:

<img src="cat.png" alt="Cat" width="120" height="80">

H007

<!DOCTYPE ... > должен присутствовать перед тегом html.

Без <!DOCTYPE> перед тегом <html> браузеры рендерят страницу в режиме совместимости (quirks mode), эмулируя устаревшую блочную модель и поведение вёрстки, поэтому CSS отображается в браузерах по-разному. Теги шаблона и комментарии перед doctype допустимы; он должен предшествовать только самому тегу <html>.

Неправильно:

<html lang="en">
</html>

Правильно:

<!DOCTYPE html>
<html lang="en">
</html>

H008

Атрибуты должны быть заключены в двойные кавычки.

Смешанные стили кавычек затрудняют чтение и поиск значений атрибутов, а значения в одинарных кавычках ломаются, как только в содержимом появляется апостроф. Двойные кавычки являются соглашением, принятым в спецификациях HTML, форматерах и большинстве руководств по стилю, поэтому стандартизация на них держит шаблоны в согласии с остальной экосистемой.

Неправильно:

<div class='content'></div>

Правильно:

<div class="content"></div>

H009

Имена тегов должны быть в нижнем регистре.

HTML-парсеры принимают имена тегов в верхнем регистре, но сериализации XHTML и XML чувствительны к регистру и отвергают их, а смешанный регистр делает текстовый поиск и ревью diff ненадёжными (grep по <h1> не найдёт <H1>). Имена тегов в нижнем регистре сохраняют переносимость и единообразие шаблонов.

Неправильно:

<H1>Welcome</H1>

Правильно:

<h1>Welcome</h1>

H010

Имена атрибутов должны быть в нижнем регистре.

Имена атрибутов в верхнем регистре недопустимы в сериализациях XHTML/XML и ломают текстовый поиск по шаблонам (grep по src= не найдёт SRC=). DOM всё равно нормализует имена HTML-атрибутов к нижнему регистру, поэтому написание в верхнем регистре добавляет разнобой без всякой пользы.

Неправильно:

<img SRC="cat.png" alt="Cat" width="120" height="80">

Правильно:

<img src="cat.png" alt="Cat" width="120" height="80">

H011

Значения атрибутов должны быть заключены в кавычки.

Значение атрибута без кавычек заканчивается на первом пробеле, поэтому значение вроде class=btn primary молча теряет всё после пробела (браузер считает “primary” отдельным булевым атрибутом). Особенно хрупки значения из переменных шаблона: любой отрендеренный пробел, “=” или “>” ломает тег. Кавычки делают границы значения явными и безопасными.

Неправильно:

<div class=test></div>

Правильно:

<div class="test"></div>

H012

Вокруг атрибута = не должно быть пробелов.

С пробелами вокруг “=” тег читается как три отдельных токена и находится в одной правке от развала: перенос строки или обрезка посередине оставляет голый булев атрибут и обрывок текста. Слитная запись name=“value” является ещё и тем, чего ожидают простые текстовые инструменты (grep, поиск с заменой), поэтому разнобой в пробелах мешает надёжно находить и рефакторить атрибуты.

Неправильно:

<div class = "test"></div>

Правильно:

<div class="test"></div>

H013

Тег img должен иметь атрибуты alt.

Без атрибута alt скринридеры произносят имя файла изображения или вообще ничего, что нарушает WCAG 1.1.1 (Non-text Content). Текст alt является ещё и тем, что видят пользователи, когда изображение не загрузилось. Декоративным изображениям следует давать явно пустой alt=“”, чтобы вспомогательные технологии знали, что их нужно пропустить; это тоже удовлетворяет правилу.

Неправильно:

<img src="cat.jpg" height="200" width="300">

Правильно:

<img src="cat.jpg" height="200" width="300" alt="A sleeping cat">

H014

Более 2 пустых строк.

Серии пустых строк никак не влияют на отрендеренную страницу (HTML схлопывает пробелы), но раздувают шаблоны и создают шумные diff при изменении соседних строк. Форматер djLint по умолчанию удаляет их полностью (оставляя не более max_blank_lines пустых строк, по умолчанию 0), поэтому оставшиеся серии указывают на неотформатированный код.

Неправильно:

<div>one</div>


<p>two</p>

Правильно:

<div>one</div>

<p>two</p>

H015

После тегов h следует перевод строки.

Заголовки являются блочными ориентирами, задающими структуру документа; когда следующий элемент лепится на ту же строку, что и закрывающий h-тег, эта структура прячется в исходнике, а правки любого из элементов выглядят в diff как изменения обоих. Перевод строки после каждого заголовка держит визуальную структуру шаблона в соответствии со структурой отрендеренного документа.

Неправильно:

<h1>Heading</h1><p>Intro text.</p>

Правильно:

<h1>Heading</h1>
<p>Intro text.</p>

H016

Отсутствие тега title в html.

Спецификация HTML требует элемент title в каждом документе. Без него вкладки браузера, закладки и история показывают голый URL вместо названия страницы, поисковые системы теряют основную метку страницы, а пользователи скринридеров теряют первое, что объявляется при загрузке, что нарушает WCAG 2.4.2 (Page Titled, уровень A).

Срабатывает только на файлах с полным документом <html></html>, поэтому фрагменты и дочерние шаблоны, расширяющие базовый, никогда не помечаются. Оболочкам SPA, задающим заголовок на клиенте, статический <title> всё равно нужен: именно он виден при первой отрисовке, краулерам и при отказе JavaScript.

Неправильно:

<html lang="en">
<body>Content</body>
</html>

Правильно:

<html lang="en">
<head>
<title>My page</title>
</head>
<body>Content</body>
</html>

H017

Пустые теги должны быть самозакрывающимися (противоречит правилу H018).

Шаблоны, которые должны также разбираться как XML/XHTML (или скармливаться XML-инструментам), не принимают пустые (void) элементы без закрывающего слэша, а смешение <br> и <br /> по кодовой базе даёт непоследовательные diff. Это правило вводит соглашение в стиле XHTML, чтобы каждый пустой элемент закрывался одинаково.

Отключено по умолчанию; включается флагом --include=H017. Взаимоисключимо с H018: включайте только одно из двух соглашений.

Неправильно:

<br>
<meta charset="utf-8">

Правильно:

<br />
<meta charset="utf-8" />

D018

(Django) Внутренние ссылки должны использовать шаблон {% url ... %}.

Жёстко прописанные внутренние URL незаметно устаревают, когда путь маршрута меняется в urls.py, порождая битые ссылки и нерабочие action у форм, которые не поймает ни один тест URLconf. {% url %} строит путь по имени маршрута, поэтому переименование пути обновляет все ссылки сразу.

Неправильно:

<a href="/accounts/login">Login</a>

Правильно:

<a href="{% url 'login' %}">Login</a>

H018

Пустые теги по своей природе являются самозакрывающимися и должны заканчиваться символом «>», а не «/>» (конфликт с: H017).

В живом стандарте HTML завершающий слэш у пустого (void) элемента ничего не значит (парсер его игнорирует), поэтому запись <br /> намекает на XML-подобное самозакрытие, которого в HTML нет, и может подтолкнуть читателей ставить слэши у непустых тегов, где лишний / молча отбрасывается и маскирует ошибки с незакрытыми тегами. Это правило требует, чтобы пустые элементы заканчивались простым >.

Отключено по умолчанию; включается флагом --include=H018. Взаимоисключимо с H017: включайте только одно из двух соглашений. SVG <path /> исключён: SVG является XML, и слэш там обязателен.

Неправильно:

<br />
<meta charset="utf-8" />

Правильно:

<br>
<meta charset="utf-8">

J018

(Jinja) Внутренние ссылки должны использовать шаблон {% url ... %}.

Жёстко прописанные внутренние URL незаметно ломаются, когда меняется путь маршрута или приложение монтируется под префиксом, оставляя мёртвые ссылки и формы, отправляющие данные на 404. url_for() строит URL по имени эндпоинта, поэтому изменения маршрутов автоматически распространяются на все шаблоны.

Неправильно:

<a href="/accounts/login">Login</a>

Правильно:

<a href="{{ url_for('login') }}">Login</a>

H019

Замените javascript:abc() на событие on_ и реальный url.

URL вида javascript: ломают клик средней кнопкой и открытие в новой вкладке, не работают при отключённом или не загрузившемся JavaScript, блокируются строгими Content Security Policy и являются классическим каналом XSS-инъекций. Используйте в href настоящий URL, а поведение подключайте обработчиком события. При строгой CSP инлайновые обработчики on* тоже блокируются. Показанный onclick лишь минимальная правка внутри шаблона; предпочтительнее вешать слушатель через addEventListener из файла скрипта.

Неправильно:

<a href="javascript:openPopup()">Open popup</a>

Правильно:

<a href="{% url 'popup' %}" onclick="openPopup(event)">Open popup</a>

H020

Найдена пустая пара тегов. Рассмотрите возможность удаления.

Пустая пара тегов не рендерит контент, но всё равно создаёт DOM-узел, который может подхватить из стилей отступы, рамки или промежутки flex/grid, порождая фантомные зазоры, которые трудно отследить; обычно это остаток разметки от прежней правки. Теги, которые в обычной разметке законно бывают пустыми (td, th, li, dt, dd, slot), исключены. Теги с любым атрибутом (точки монтирования JS вроде <div id="app">``</div>, элементы иконочных шрифтов вроде <i class="fa fa-user">``</i>) тоже не помечаются; правилу соответствуют только совершенно пустые пары без атрибутов.

Неправильно:

<p>Saved.</p>
<span> </span>

Правильно:

<p>Saved.</p>

H021

Следует избегать инлайн-стилей.

Инлайн-стили имеют более высокую специфичность, чем любой селектор в таблице стилей, поэтому переопределять их потом приходится через !important; они блокируются Content Security Policy без ‘unsafe-inline’ в style-src; и они размазывают оформление по шаблонам, так что смена темы или дизайна означает правку разметки вместо одной таблицы стилей. Перенесите объявление в CSS-класс. Одно законное исключение составляют шаблоны HTML-писем: многие почтовые клиенты вырезают блоки <style>, и инлайн-стили там стандартный приём, поэтому исключите каталоги почтовых шаблонов или отключите для них это правило.

Неправильно:

<div style="color: red;">Wrong username or password.</div>

Правильно:

<div class="error">Wrong username or password.</div>

H022

Используйте HTTPS для внешних ссылок.

Обычные http:// подресурсы на странице, отдаваемой по HTTPS, являются смешанным контентом: скрипты, стили и iframe браузеры блокируют сразу, а изображения автоматически апгрейдят или показывают предупреждение. Ссылка <a> на http:// страницу смешанным контентом не является, но всё равно отправляет посетителей по незашифрованному соединению, открытому для перехвата и подмены. Ссылки на внутренние хосты, у которых действительно нет TLS, тоже будут помечены: глушите такие места блоком {# djlint:off H022 #}, а не отключайте правило целиком.

Неправильно:

<a href="http://example.com">Example</a>

Правильно:

<a href="https://example.com">Example</a>

H023

Не используйте ссылки на сущности.

Документы HTML5 используют UTF-8, поэтому литеральный символ работает везде, и именно его реально читают ревьюеры; опечатка в ссылке на сущность (например, &mdsah;) браузером не ловится и выводится как есть, битым текстом. djLint разрешает только сущности, несущие синтаксический смысл или невидимые на экране, такие как &lt;, &gt;, &amp;, &quot;, &nbsp; и &shy;.

Неправильно:

<p>Dates 1900 &mdash; 2000</p>

Правильно:

<p>Dates 1900 — 2000</p>

H024

Опускайте тип в скриптах и стилях.

text/javascript и text/css являются значениями по умолчанию в HTML5 для <script> и <style>, так что этот атрибут представляет собой мёртвый груз, который браузер игнорирует; спецификация WHATWG прямо предписывает его опускать. Отказ от него также избавляет от устаревших MIME-строк, ломающих элемент при копировании на модульные скрипты (где type=“module” действительно важен).

Неправильно:

<script type="text/javascript" src="app.js">

Правильно:

<script src="app.js"></script>

H025

Тег кажется бесхозным.

Тег без парного открывающего или закрывающего тега заставляет механизм восстановления после ошибок в браузере угадывать, где кончается элемент, и последующая разметка проглатывается не тем элементом: вёрстка, CSS-селекторы и DOM-запросы JavaScript ломаются молча и по-разному в разных браузерах. H025 также сообщает об <ol> или <ul>, открытом внутри <p>: HTML-парсер закрывает абзац перед списком, поэтому разметка никогда не вкладывается так, как написана.

Неправильно:

<div>
  <p>Hello</p>

Правильно:

<div>
  <p>Hello</p>
</div>

H026

Пустые теги id и class могут быть удалены.

Пустой атрибут id или class ничего не делает (на него не могут нацелиться ни стили, ни скрипты), а пустой id ещё и невалидный HTML (значение id не может быть пустой строкой). Обычно это признак ошибки в шаблоне, где предполагалась подстановка переменной, поэтому удаление или заполнение атрибута не даёт этой ошибке прятаться на виду.

Неправильно:

<div id="" class="">content</div>

Правильно:

<div>content</div>

T027

В синтаксисе шаблона найдена незакрытая строка.

Кавычка, открытая, но не закрытая внутри {% ... %} или {{ ... }}, заставляет шаблонизатор неверно разобрать тег: Django и Jinja либо выбрасывают TemplateSyntaxError при рендеринге, либо молча поглощают остаток аргументов тега как строковое содержимое: страница отвечает ошибкой 500 или рендерится с пропавшими аргументами.

Неправильно:

{% trans "Welcome %}

Правильно:

{% trans "Welcome" %}

T028

Рассмотрите возможность использования тегов без пробелов внутри значений атрибутов. {%- if/for -%}

Теги шаблона внутри значения атрибута выводят в итоговый атрибут окружающие их пробелы и переводы строк, поэтому href или src, собранный обычными тегами {% if %}/{% for %}, может содержать лишние пробелы и давать битые URL. Теги управления пробелами Jinja/Nunjucks ({%- ... -%}) убирают эти окружающие пробелы, и атрибут рендерится одним чистым значением. Атрибут class исключён: лишние пробелы между именами классов безвредны.

Не применяется к профилю django: теги шаблонов Django не поддерживают управление пробелами {%- -%}.

Неправильно:

<a href="{% if x %}/home{% endif %}"></a>

Правильно:

<a href="{%- if x -%}/home{%- endif -%}"></a>

H029

Рассмотрите возможность использования строчных значений метода формы.

Спецификация HTML определяет ключевые слова метода формы в нижнем регистре (get, post); варианты в верхнем регистре браузеры принимают лишь за счёт регистронезависимого сопоставления. Каноническая запись в нижнем регистре делает шаблоны единообразными и удобными для grep и избавляет от претензий строгих валидаторов и XHTML-инструментов.

Неправильно:

<form method="POST"></form>

Правильно:

<form method="post"></form>

H030

Рассмотрите возможность добавления мета-описания.

Поисковые системы используют meta description как сниппет под заголовком страницы в выдаче; без него они синтезируют сниппет из произвольного текста страницы, что снижает кликабельность и даёт плохие превью ссылок, когда страницей делятся.

Срабатывает только на файлах с полным документом <html></html>. Аргумент про сниппет относится к публично индексируемым страницам; для приложений за авторизацией или интранета это правило часто отключают.

Неправильно:

<html lang="en">
  <head><title>Home</title></head>
  <body>Welcome</body>
</html>

Правильно:

<html lang="en">
  <head>
    <title>Home</title>
    <meta name="description" content="A short summary of this page.">
  </head>
  <body>Welcome</body>
</html>

H031

Рассмотрите возможность добавления мета-ключевых слов.

Отключено по умолчанию; включается флагом --include=H031.

Метаданные с ключевыми словами до сих пор используются некоторыми инструментами поиска по сайту, интранет-индексаторами и старыми краулерами, поэтому страница без <meta name="keywords"> может быть для них невидимой. Крупные публичные поисковики, впрочем, его игнорируют, поэтому команды, не зависящие от таких инструментов, часто отключают это правило.

Срабатывает только на файлах с полным документом <html>...</html>.

Неправильно:

<!DOCTYPE html>
<html lang="en">
<head>
<title>Home</title>
<meta name="description" content="A short summary.">
</head>
</html>

Правильно:

<!DOCTYPE html>
<html lang="en">
<head>
<title>Home</title>
<meta name="description" content="A short summary.">
<meta name="keywords" content="django, templates">
</head>
</html>

T032

В тегах шаблона обнаружены лишние пробелы.

Последовательности пробелов или табов между аргументами тега шаблона являются невидимым шумом: они прячут реальные отличия в diff, мешают заметить пропущенный аргумент и расходятся с одиночными пробелами, которые выдаёт форматер djLint, вызывая бессмысленный цикл переформатирований. Пробелы внутри строк в кавычках сохраняются и не помечаются.

Неправильно:

{% static  'css/style.css' %}

Правильно:

{% static 'css/style.css' %}

H033

В действии формы обнаружен лишний пробел.

Начальные и конечные пробелы внутри значения action формы становятся частью итогового URL. Браузеры убирают их при разборе, но небраузерные клиенты и тесты, обращающиеся к литеральному значению, могут этого не делать, а лишний пробел вокруг тега {% url %} почти всегда указывает на опечатку, из-за которой URL отправки формы не проходит сопоставление маршрутов на сервере.

Неправильно:

<form action="{% url 'search' %} " method="get">
    <button>Search</button>
</form>

Правильно:

<form action="{% url 'search' %}" method="get">
    <button>Search</button>
</form>

T034

Вы намеревались использовать {% ... %} вместо {% ... }%?

}% почти всегда опечатка вместо %}. Шаблонизатор не распознаёт }% как разделитель тега, поэтому тег вообще не разбирается: сырой текст {% … }% утекает в отрендеренный HTML, либо движок выбрасывает синтаксическую ошибку, наткнувшись на незакрытый тег.

Неправильно:

{% include "footer.html" }%

Правильно:

{% include "footer.html" %}

H035

Meta должны быть самозакрывающимися.

В чистом HTML5 завершающий слэш у <meta> необязателен, но шаблоны, которые дополнительно проходят через XML/XHTML-инструменты (XML-валидаторы, почтовые конвейеры, XSLT), не разбираются, если пустые элементы не самозакрыты. Включение этого правила держит теги <meta> в XHTML-совместимой форме <meta ... />, чтобы одна и та же разметка переживала оба парсера.

Отключено по умолчанию; включается флагом --include=H035. Подмножество H017 (которое требует завершающий слэш у всех пустых тегов, включая meta): включайте H035 отдельно, только если XHTML-форма нужна лишь для meta. Взаимоисключимо с H018; не включайте оба.

Неправильно:

<meta name="viewport" content="width=device-width">

Правильно:

<meta name="viewport" content="width=device-width" />

H036

Избегайте использования тегов br.

<br> кодирует оформление в разметке: использование его для отступов или имитации абзацев ломает перенос текста на узких экранах и вредит доступности, так как скринридеры объявляют принудительные разрывы вместо естественной паузы между блоками. Отдельным мыслям место в отдельных блочных элементах, а вертикальным отступам место в CSS-margin. Учтите, что <br> уместен там, где разрыв строки является частью самого содержимого (почтовые адреса, стихи, тексты песен), а правило не умеет отличать такие случаи от презентационных: оно помечает каждый <br>. Оставьте его выключенным, если ваши шаблоны рендерят подобный контент.

Отключено по умолчанию; включается флагом --include=H036.

Неправильно:

<p>Shipping is free.<br>Delivery takes 3 days.</p>

Правильно:

<p>Shipping is free.</p>
<p>Delivery takes 3 days.</p>

H037

Найдено дублирование атрибута.

Дублирующиеся атрибуты являются невалидным HTML: браузеры оставляют только первое вхождение и молча отбрасывают остальные, поэтому второе значение class или style никогда не применяется, что скрывает реальные ошибки. Проверка учитывает шаблоны: атрибут, повторённый во взаимоисключающих ветках ({% if %}/{% else %}), не помечается, поскольку отрендерится только одна копия.

Неправильно:

<div class="card" id="profile" class="active">...</div>

Правильно:

<div class="card active" id="profile">...</div>

T038

Блочный тег не имеет соответствующего закрывающего тега.

Блочный тег вроде {% if %}, {% for %} или {% macro %} без парного закрывающего тега означает гарантированный TemplateSyntaxError в Django и Jinja: страница не рендерится в момент запроса, а это правило ловит проблему до деплоя. Оно также помечает осиротевшие закрывающие теги без открывающего и неправильно переплетённые блоки (например, {% if %}``{% for %}``{% endif %}).

Парность {% block %}/{% endblock %} и несовпадающие имена endblock проверяются этим правилом; T003 (отключено по умолчанию) дополнительно требует имя у каждого многострочного {% endblock %}. Пользовательские блочные теги, зарегистрированные через custom_blocks, тоже проверяются, включая их самозакрывающуюся форму / %}.

Неправильно:

{% if user.is_authenticated %}
<p>Welcome back!</p>

Правильно:

{% if user.is_authenticated %}
<p>Welcome back!</p>
{% endif %}

T039

Найден незакрытый тег шаблона.

Тег шаблона, открытый {{ или {%, но не закрытый парным }} или %}, не разбирается как тег: Django/Jinja либо выбрасывают TemplateSyntaxError, либо выводят сырые фигурные скобки на страницу, а всё до следующего разделителя может быть молча поглощено. Такие опечатки (одна пропущенная скобка, несовпадающий разделитель) легко пропустить на ревью, потому что шаблон может частично рендериться.

Неправильно:

<p>{{ user.name }</p>

Правильно:

<p>{{ user.name }}</p>

T040

Отсутствующее или пустое имя шаблона в теге extends или include.

Тег {% extends %} или {% include %} с отсутствующим, пустым или состоящим из одних пробелов именем шаблона не может ничего загрузить: Django выбрасывает TemplateSyntaxError, когда имя отсутствует совсем, и TemplateDoesNotExist при рендеринге, когда оно пустое; страница отвечает 500 в продакшене, хотя сам файл шаблона выглядит синтаксически правдоподобно.

Неправильно:

{% extends "" %}

Правильно:

{% extends "base.html" %}

H041

Тег закрывается в другом блоке шаблона, чем тот, в котором он был открыт.

Когда HTML-тег открыт в одном {% block %}, а закрыт в другом, дочерний шаблон, переопределяющий только один из этих блоков, наследует половину элемента, и в отрендеренной странице появляется несбалансированная разметка: браузеры непредсказуемо автозакрывают или перевкладывают элементы, ломая вёрстку и CSS-селекторы далеко от фактически отредактированного шаблона. Если каждый элемент открывается и закрывается в одном и том же блоке, любой блок можно безопасно переопределять независимо.

Неправильно:

{% block content %}
<div class="wrapper">
{% endblock content %}
{% block footer %}
</div>
{% endblock footer %}

Правильно:

{% block content %}
<div class="wrapper">
</div>
{% endblock content %}
{% block footer %}
{% endblock footer %}

H042

Атрибут for у label не имеет соответствующего id в этом файле.

Отключено по умолчанию; включается флагом --include=H042.

Проверка выполняется только для файлов, которые можно анализировать надёжно: если файл содержит что-либо, способное отрендерить id, которого в этом файле не видно (вывод {{ ... }} вроде виджета формы, {% include %} или {% extends %}, либо неизвестный тег шаблона), правило молчит для этого файла. Там, где оно работает, каждое срабатывание означает действительно сломанную связь.

Неправильно:

<label for="email">Email</label>
<input id="username">

Правильно:

<label for="email">Email</label>
<input id="email">

Добавление правил

Мы приветствуем запросы с новыми правилами!

Хорошее правило состоит из

  • Name
  • Code
  • Message - Сообщение для отображения при обнаружении ошибки.
  • Flags - Флаги регекса. По умолчанию используется re.DOTALL. например: re.I|re.M
  • Patterns - regex-выражения, которые найдут ошибку.
  • Exclude - Необязательный список профилей, из которых нужно исключить правило.

Пожалуйста, включите тест для проверки правила.

Пользовательские правила

Создайте файл .djlint_rules.yaml рядом с вашим pyproject.toml. Правила могут быть добавлены в этот файл, и djLint подхватит их.
Файл правил в другом месте можно указать с помощью CLI-опции --rules.

Хорошее правило выглядит следующим образом:

- rule:
    name: T001
    message: Найти трихотилломанию
    flags: re.DOTALL|re.I
    patterns:
      - трихотилломанию