Skip to content

Proposal to Add Djlint and Prettier formatter#2151

Open
julhoang wants to merge 18 commits intodevelopfrom
jh/code-formatting-and-linting
Open

Proposal to Add Djlint and Prettier formatter#2151
julhoang wants to merge 18 commits intodevelopfrom
jh/code-formatting-and-linting

Conversation

@julhoang
Copy link
Copy Markdown
Collaborator

@julhoang julhoang commented Mar 10, 2026

Add Django HTML template formatter (djlint) and frontend formatter (Prettier)

Summary

The codebase had no automated formatting or linting for HTML templates, JS, or CSS. This made allowed inconsistent style to accumulate over time. This PR establishes a consistent baseline by introducing Prettier (JS/CSS) and djLint (HTML templates) with pre-commit integration and CI enforcement.

Changes

  • Prettier configured via .prettierrc and .prettierignore for JS and CSS files. Vendor/generated bundles in static/js/vendor/ are excluded.
  • Vendor JS reorganized — third-party and generated JS bundles moved from static/js/ into static/js/vendor/ subfolders
    • This includes: highlight, marked, wysiwyg-editor, boost-gecko.
    • This keeps static/js/*.js as the convention for custom project code only.
  • djLint configured in pyproject.toml for all template directories (templates/, users/templates/, reports/templates/) — includes auto-reformat and lint hooks.
  • Pre-commit hooks updated in .pre-commit-config.yaml — replaces DjHTML with djLint for Django template formatting and linting.
  • CI enforcement via .github/workflows/actions.yml — now installs requirements-dev.txt and runs yarn format:check + pre-commit run -a.
  • justfile and package.json commands for local use (just format, just format-check, etc.)
  • All existing files reformatted to conform to the new rules. Large diffs on HTML, CSS, and JS files in this PR are purely formatting — no logic changes. Thanks to the git-blame-ignore solution (credits to @herzog0), these files are reformatted but the git history is preserved.

Known djLint Quirks

  • Please see the updated README.md

What Developers Should Expect

Existing developers — after pulling, reinstall dependencies to pick up both tools:

yarn install          # picks up Prettier
just build            # picks up djlint (in requirements-dev.txt)
pre-commit install    # updates pre-commit hooks
git config blame.ignoreRevsFile .git-blame-ignore-revs # so you can see the true git history before the reformatting occurred

Key commands for your regular usage:

just format          # reformat everything (Prettier + djLint)
just format-check    # check formatting without writing changes (used in CI)

‼️ Risks and Considerations ‼️

  • Template rendering — djlint reformats HTML indentation and whitespace, which could theoretically affect whitespace-sensitive content (e.g. <pre> blocks, inline elements).
    • ✅ I manually checked this: style-guide page that has <pre> blocks and no rendering changes observed.
  • Alpine.js bindings — djlint could subtly reformat Alpine attributes like @click handlers.
    • ✅ I manually checked the interactive components (dropdowns, mobile menu, install cards) – all work as expected.
  • Vendor JS path changes — The static/js/vendor/ move updates paths referenced via {% static %} and
    dynamic import(). If any path was missed, it would be a runtime 404 (not a build error).
    • ✅ I looked up all the moved JS files by keyword searches and verified all paths have been updated.
  • ❗️I noticed that we have both yarn.lock (from yarn) and package-lock.json (from npm) in the repo – I only ran yarn install since it seems to be the latest preference as per the project set-up documentation. If package-lock.json is truly a legacy artifact, we should consider removing it.
  • A number of djlint rules are currently ignored (see full list below). Enabling them would touch many files and risk breaking templates, so each rule should be enabled in its own PR where the changes can be reviewed and tested in isolation.

Djlint Rules we're currently ignoring

The following djlint rules are not yet enforced (existing violations need cleanup first). They're listed in pyproject.toml under ignore:

djlint Ignored Codes & Descriptions (open for details!)
Code Description
D018 Internal links should use {% url %} tag
H005 <html> tag should have a lang attribute
H006 <img> tag should have height and width attributes
H008 Attributes should be double quoted
H013 <img> tag should have an alt attribute (accessibility)
H014 Too many consecutive blank lines
H016 Missing <title> tag in HTML
H020 Empty pair of tags found
H021 Inline styles should be avoided
H022 Use relative path for link
H023 Do not use entity references
H025 Tag appears to be an orphan
H026 Empty id and class attributes can be removed
H029 lang attribute should be lowercase
H030 Missing meta description
H031 Missing meta keywords
H037 Avoid type attribute on <script> tags
T002 Tag names should be lowercase
T003 End block should have name (e.g. {% endblock body %})
T027 Use {% url %} instead of hardcoded path
T032 Filter formatting should be {{ foo | bar }} (spaces around pipe)

@julhoang julhoang marked this pull request as ready for review March 10, 2026 19:05
Copy link
Copy Markdown
Collaborator

@herzog0 herzog0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @julhoang!! I think this is absolutely necessary and will improve our daily work by a lot.
Just a couple of things:

  1. When I ran yarn install while on your branch, my yarn.lock got updated, but those changes are not included in your branch. Would you please make sure that your lockfile doesn't have any pending changes that should be included in the PR? Kinda weird though, as you do have updates on your lockfile, so I'm not sure why we're seeing differences.
  2. Nice touch adding the formatter as a pre-commit hook! I think it'd be nice too if we were able to manually trigger the linting, as I'd much rather have a local keystroke to apply the formatting rather than waiting until the hook process it. A yarn add djlint to the project might be enough, and running djlint . --reformat is the command I used locally. Something curious is that running this command also touched a few files in core/tests/content that are not in your PR.
  3. A while back I was looking into ways to solve a common issue when applying these large lint updates, which is that after linting this many files, git blame points to you as the author of all of those files/lines of code, and that's not really what we want. Check below a way to solve this and see if makes sense to you!

Solving the Git Blame issue when committing several lint fixes

Since 2019, Git has a built-in feature specifically for this! You can tell Git to completely ignore "style-only" commits when someone runs a blame.

Step 1: Create the ignore file

After you commit massive linting changes, get the commit hash of that change (in your case, it'll be from multiple commits). Create a file in the root of the repo namef .git-blame-ignore-revs and paste the hashes there (just a hash example there):

# Avoid blaming the commit author for the massive lint/reformat
# (Add the full 40-character SHA-1 hash)
c1891b8ffb74c87ebd11bc42e03f86e6384fd5d5 

Step 2: Configure Git to use it

Run this command:

git config blame.ignoreRevsFile .git-blame-ignore-revs

Step 3: Share it with the team

You'll have to commit the .git-blame-ignore-revs file, and everyone in the team can run the same command in step 2 on their end. There's no harm if they don't run it, it's totally optional.. But they'll keep seeing you as the author of those lines of code until they run it.

Caveat

Git is "blind" to the context of your changes, it doesn't know whether a change is a lint or an actual logic change, so these kinds of changes must live in completely separate commits so you don't mess with the correct authoring of your own actual logic changes (i.e. adding some new dependency to package.json).

Thanks a ton for this!

@julhoang julhoang force-pushed the jh/code-formatting-and-linting branch from c1891b8 to 5e923d5 Compare March 11, 2026 23:22
@julhoang
Copy link
Copy Markdown
Collaborator Author

Thanks a ton for all of your suggestions @herzog0 !! The .git-blame-ignore-revs solution, in particular, is super neat 🙌. Would you mind checking this out again and see if the git history is better now, amongst the other changes (new just command, add djlint to requirement-dev, etc)? Thanks so much!

Copy link
Copy Markdown
Collaborator

@herzog0 herzog0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking just perfect @julhoang, thanks so much! I tested and the ignoring is working flawless. The other commands are working and they provide a great dev experience, thanks for that too!

I'm just selecting "Request changes" because for some reason yarn format:check exists with an error:

Image

Buut, what's weird is that yarn format doesn't actually format anything.. 🤔 sorry I didn't have the time to go deeper into why this is happening

@julhoang
Copy link
Copy Markdown
Collaborator Author

Thanks for raising that @herzog0 – I notice that the linter is parsing through 1093 files, which is a lot more than the number on my end (I think around max 200 files). Also another clue is that javascript-intro.html is not part of our repository. Can you please help me where you're running this command?

@herzog0
Copy link
Copy Markdown
Collaborator

herzog0 commented Mar 12, 2026

Hmm sorry @julhoang, didn't even notice that file wasn't part of the project! It is however part of Python's virtual environment created for the project. Are you running these yarn commands from the root of project too? Maybe it's a matter of inclusion/exclusion settings from djlint?

Like, I see that the pre-commit hook looks at files in paths like (^|/)templates/.*\.html$, and the file that errored out in my case follows that same pattern, but you haven't configured that pattern anywhere else but the hook definition, and since this error happened when running the command by hand I wonder if this is a default path search from djlint.

Even if that's the case, we need a folder/file exclusion list, maybe in a separate file if djlint allows or inline in the package.json command. I'm assuming you don't have a .venv folder on your end, right?

Base automatically changed from v3 to develop March 18, 2026 20:41
@julhoang julhoang marked this pull request as draft March 20, 2026 19:47
@julhoang julhoang force-pushed the jh/code-formatting-and-linting branch 2 times, most recently from 8477082 to 09313f8 Compare March 27, 2026 20:19
@julhoang julhoang requested a review from herzog0 March 27, 2026 21:55
@julhoang julhoang marked this pull request as ready for review March 27, 2026 21:55
@herzog0
Copy link
Copy Markdown
Collaborator

herzog0 commented Apr 2, 2026

Hey @julhoang quick feedback on this: now the .venv folder is being successfully ignored 🥳
Let me know once you fix the conflicts here and I'll approve it right away

@julhoang julhoang force-pushed the jh/code-formatting-and-linting branch from 3270c64 to af029dd Compare April 2, 2026 20:52
@julhoang julhoang linked an issue Apr 2, 2026 that may be closed by this pull request
@julhoang julhoang requested a review from gregjkal April 2, 2026 21:03
@julhoang julhoang force-pushed the jh/code-formatting-and-linting branch from af029dd to ef08651 Compare April 3, 2026 00:19
@julhoang julhoang dismissed herzog0’s stale review April 3, 2026 00:43

All changes requests have been resolved ✅

Copy link
Copy Markdown
Collaborator

@gregjkal gregjkal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of minor requests in the GHA, and one real bug created by djlint.

Comment thread templates/versions/detail.html Outdated
Comment thread .github/workflows/actions.yml Outdated
Comment thread .github/workflows/actions.yml Outdated
@julhoang julhoang force-pushed the jh/code-formatting-and-linting branch from a944dc4 to 2438c53 Compare April 7, 2026 19:19
@julhoang julhoang requested a review from gregjkal April 7, 2026 19:54
Copy link
Copy Markdown
Collaborator

@gregjkal gregjkal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making those changes - looks good!

Move third-party and generated JS bundles into static/js/vendor/ so
that only custom project JS lives at static/js/*.js.

Moved files:
- highlight.js → vendor/highlight/
- marked.js + source map → vendor/marked/
- wysiwyg-editor.js → vendor/wysiwyg-editor/
- boost-gecko → vendor/boost-gecko/
Wrap the multi-line Alpine.js x-data attribute with djlint:off/on
comments to prevent djlint from mangling the JavaScript object.
Add Prettier for JS/CSS and djlint for Django HTML template formatting.
Configure pre-commit hooks, CI checks, and justfile commands.
Scope djlint to templates/ directories and Prettier to static/js/*.js
to avoid scanning vendor and virtual environment files.
@julhoang julhoang force-pushed the jh/code-formatting-and-linting branch 2 times, most recently from b0e4e3e to b40227a Compare April 14, 2026 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Set up code formatting and linting

4 participants