HubL lint rules
codefmt's HubL linter runs 28 rules on a single template, across six categories. Each is listed below with its rule id and severity; about ten ship one-click safe fixes via the Fix and Fix (unsafe) buttons. Two further rules (tagged needs project context) only fire when codefmt has your whole theme, so they aren't part of the single-template count.
correctness
Structural errors that stop a template from parsing or rendering.
- unclosed block
hubl/unclosed-blockerrorA {% for %}, {% if %}, {% block %}, or other opening tag has no matching {% end... %}.
- mismatched block
hubl/mismatched-blockerrorAn opening and closing tag don't match, e.g. a {% for %} closed by {% endif %}.
- unclosed delimiter
hubl/unclosed-delimitererrorA {% %}, {{ }}, or {# #} is opened but never closed.
- unexpected closer
hubl/unexpected-closererrorA closing tag like {% endif %} appears with no matching opener.
- elif after else
hubl/elif-after-elseerrorAn {% elif %} appears after {% else %}, where it can never run.
- unexpected mid-block tag
hubl/unexpected-miderrorA mid-block tag like {% else %} or {% elif %} appears outside the block it belongs to.
- unclosed raw block
hubl/raw-not-closederrorA {% raw %} block is missing its {% endraw %}.
semantic
Code that parses but means the wrong thing in HubL, including js-mindset traps carried over from JavaScript.
- unknown filter
hubl/unknown-filterinfoA filter name isn't a known HubL filter; codefmt suggests the closest match.
- wrong filter arity
hubl/filter-aritywarningA filter is called with the wrong number of arguments.
- unknown global
hubl/unknown-globalinfoAn identifier isn't a known HubL global or local variable; codefmt suggests the closest match.
- unknown function
hubl/unknown-functioninfoA function call whose name isn't a known HubL function, a locally-defined macro, or an imported name. Method calls and filter calls are excluded.
- || instead of or
hubl/js-trap-orerrorJavaScript's || used where HubL expects the keyword or.
- && instead of and
hubl/js-trap-anderrorJavaScript's && used where HubL expects the keyword and.
- === instead of ==
hubl/js-trap-strict-eqerrorJavaScript's === / !== used where HubL expects == / !=.
- null instead of none
hubl/js-trap-nullerrorThe literal null used in a test where HubL expects none.
- backtick template literal
hubl/js-trap-template-literalerrorA JavaScript backtick template literal, which HubL does not support.
- .forEach instead of a for tag
hubl/js-trap-foreachinfoA JavaScript .forEach() used where HubL expects a {% for %} tag.
inheritance
Mistakes in template inheritance: blocks, extends, and super().
- duplicate block name
hubl/duplicate-block-nameerrorTwo {% block %}s share the same name in one template.
- missing endblock
hubl/missing-endblockerrorA {% block %} has no matching {% endblock %}.
- super() outside a block
hubl/super-outside-blockerror{{ super() }} is called outside any {% block %}.
- extends path not found
hubl/extends-path-not-founderrorneeds project context{% extends %} points to a template path that doesn't exist in the project. Requires project context.
deprecation
Tags HubSpot has renamed; codefmt flags the old name and offers a safe one-click fix.
- deprecated tag
hubl/deprecated-tagwarningA deprecated tag such as widget or widget_block that should be renamed to module or module_block. Ships a safe one-click fix.
module shape
Malformed module / dnd_module declarations and field definitions.
- module missing path
hubl/module-missing-patherrorA {% module %} or {% dnd_module %} is missing its required path parameter.
- unknown field type
hubl/module-unknown-field-typewarningA module field declares a type HubL doesn't recognize.
- malformed module params
hubl/module-malformed-paramserrorA module parameter is malformed, e.g. form_id 7a3 where the = was forgotten.
- unknown module name
hubl/module-unknown-namewarningneeds project contextA {% module %} references a module name not found in the project. Requires project context.
hints
Lower-severity smells: things that work but are worth a second look.
- nested raw block
hubl/nested-rawwarningA {% raw %} block nested inside another {% raw %}, which does not behave as it looks.
- empty block
hubl/empty-blockwarningA block tag with no body content.
- |safe without |escape
hubl/unsafe-without-escapeinfo|safe applied without a preceding |escape, a potential XSS footgun.
- large range loop
hubl/large-rangeinforange() iterating a very large number of times (over 1000), which can slow template rendering.
run these checks in the HubL formatter and linter
related: why standard linters can't read HubL, codefmt vs. HubSpot's VS Code extension