Skip to content

Use "dprint" as a code formatter, replacing eslint#2997

Merged
arbrandes merged 5 commits intoopenedx:masterfrom
open-craft:braden/dprint
Apr 14, 2026
Merged

Use "dprint" as a code formatter, replacing eslint#2997
arbrandes merged 5 commits intoopenedx:masterfrom
open-craft:braden/dprint

Conversation

@bradenmacdonald
Copy link
Copy Markdown
Contributor

@bradenmacdonald bradenmacdonald commented Apr 13, 2026

Description

We have been testing oxlint in the Authoring MFE and it's been working great. It has been able to catch things that our old eslint config totally missed (e.g. missing key props in React array elements), and once we got the config right, I haven't seen any lint violations that eslint would have caught but oxlint missed.

However, oxlint doesn't do "formatting", so a number of formatting issues have crept in that eslint would have caught, but oxlint considers out of scope.

Example:

src/course-unit/unit-sidebar/unit-info/ComponentInfoSidebar.test.tsx
  148:4  error  Newline required at end of file but not found  eol-last

src/course-unit/unit-sidebar/unit-info/UnitInfoSidebar.test.tsx
  1:8   error  Expected a line break after this opening brace   object-curly-newline
  1:53  error  Expected a line break before this closing brace  object-curly-newline

src/course-unit/unit-sidebar/unit-info/UnitInfoSidebar.tsx
  175:7   error  Expected 'undefined' and instead saw 'void'                  no-void
  183:37  error  Definition for rule 'deprecation/deprecation' was not found  deprecation/deprecation

src/course-unit/xblock-container-iframe/index.tsx
  45:1  error  `@src/course-outline/data/apiHooks` import should occur before import of `../constants`       import/order
  46:1  error  `@src/content-tags-drawer/data/apiHooks` import should occur before import of `../constants`  import/order

src/generic/sidebar/InfoSidebarMenu.tsx
  17:1   error  `@src/data/types` import should occur before import of `./messages`  import/order
  58:33  error  Trailing spaces not allowed                                          no-trailing-spaces

src/generic/sidebar/SidebarTitle.tsx
  34:22  error  Unexpected usage of singlequote  jsx-quotes

src/generic/sidebar/messages.ts
  68:5  error  Trailing spaces not allowed  no-trailing-spaces

To resolve this, we could use oxfmt, the formatter companion to oxlint. However, when I tried this out, I found that it produced a very large diff and many of the changes made the code less readable: https://github.com/openedx/frontend-app-authoring/pull/2973/changes

The reason for this is that oxfmt is new and is initially targeting prettier compatibility, with more configuration options coming later. But for now it still reflects prettier's major problem:

The most frequently criticized feature is Prettier's automatic wrapping and unwrapping of lines based on its printWidth setting (typically 80 or 100 characters). While intended to prevent horizontal scrolling, this behavior often creates significant "noise" in Git diffs. A minor change to a long string or function signature can cause an entire block of code to be reformatted, obscuring the actual logic change and making code reviews more difficult and time-consuming. In certain contexts, such as functional programming pipelines or long, chained method calls, the resulting formatting can be objectively harder to read than the original, manually formatted code. The inability to disable this behavior is a major and long-standing complaint.

So, I found another Rust/wasm-based code formatter, dprint, and it seems great! With this PR, I:

  • installed and configured dprint for this repo, keeping the options very similar to our existing eslint config
  • ran it to format all our files

I think the diff it generates looks really nice, and in general its changes are clear improvements, unlike my test of oxfmt. You can compare both diffs to see.

Formatter diff
oxfmt +29,427 -36,323
dprint (this PR) +18,328 -13,083

Also, both oxlint and dprint are blazing fast, so running oxlint+dprint+stylelint now with npm run lint takes < 3 seconds to run on the whole codebase. Before, running eslint+stylelint took 55s !

Note: I haven't really looked into what stylelint is nor why we have it, but we can probably replace it with dprint as well. Then the lint would be 1s faster (33%).

Private ref MNG-4763

@openedx-webhooks
Copy link
Copy Markdown

Thanks for the pull request, @bradenmacdonald!

This repository is currently maintained by @bradenmacdonald.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

Details
Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@openedx-webhooks openedx-webhooks added open-source-contribution PR author is not from Axim or 2U core contributor PR author is a Core Contributor (who may or may not have write access to this repo). labels Apr 13, 2026
@github-project-automation github-project-automation bot moved this to Needs Triage in Contributions Apr 13, 2026
@bradenmacdonald bradenmacdonald force-pushed the braden/dprint branch 3 times, most recently from e4fa76c to 03411df Compare April 14, 2026 00:00
@bradenmacdonald
Copy link
Copy Markdown
Contributor Author

@arbrandes @brian-smith-tcril @ChrisChV @rpenido @navinkarkera Could I get your opinions on this?

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 14, 2026

Codecov Report

❌ Patch coverage is 94.29429% with 38 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.46%. Comparing base (4980c7a) to head (3b6f0c2).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/CourseAuthoringRoutes.tsx 33.33% 2 Missing and 2 partials ⚠️
src/content-tags-drawer/data/api.mocks.ts 90.00% 4 Missing ⚠️
src/content-tags-drawer/ContentTagsCollapsible.jsx 76.92% 3 Missing ⚠️
src/content-tags-drawer/ContentTagsDrawer.tsx 88.46% 1 Missing and 2 partials ⚠️
src/course-outline/drag-helper/utils.ts 57.14% 3 Missing ⚠️
...nce/sequence-navigation/SequenceNavigationTabs.jsx 40.00% 3 Missing ⚠️
src/container-comparison/ContainerRow.tsx 81.81% 2 Missing ⚠️
src/course-outline/data/api.ts 89.47% 2 Missing ⚠️
...rse-unit/legacy-sidebar/components/SidebarBody.jsx 0.00% 1 Missing and 1 partial ⚠️
src/CourseAuthoringPage.tsx 75.00% 0 Missing and 1 partial ⚠️
... and 11 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2997      +/-   ##
==========================================
- Coverage   95.61%   95.46%   -0.16%     
==========================================
  Files        1378     1378              
  Lines       32289    32596     +307     
  Branches     7374     7479     +105     
==========================================
+ Hits        30873    31117     +244     
- Misses       1352     1410      +58     
- Partials       64       69       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@brian-smith-tcril
Copy link
Copy Markdown
Contributor

My initial reaction was that I'd prefer to stay in one "ecosystem" if possible, so being able to use oxlint and oxfmt seems great in theory. That being said, after looking into this a bit, I agree that oxfmt is likely not the tool we're looking for.

I first looked into the oxfmt docs and found it has more configuration options than prettier, so I decided to see if a more fleshed-out .oxfmtrc.json (generated by claude) could produce a smaller diff. I ended up with +30582, -37728 lines changed.

I completely agree with the comments you left on your oxfmt PR (1, 2). I also don't like that it forces imports to be on one line.

The key difference is that dprint has a concept of "maintain" (used heavily in this PR's dprint.json), which fits with what the dprint homepage says:

Tweak formatting to match your style, not the other way around.


Conclusions

  • I think that we should not use oxfmt.
  • I think dprint is a viable option
  • I'd like to make sure we consider all reasonable alternatives (I know biome is quite popular now, but I believe it has the same issues with being opinionated as prettier and oxfmt), document all alternatives considered, and ensure the tool we land on is well-maintained and supported.

claude conversation summary from exploration

oxfmt vs dprint for frontend-app-authoring

Investigating #2997, where Braden proposes replacing eslint
formatting with dprint. We're exploring whether oxfmt could achieve similar results, keeping
the toolchain in one ecosystem (oxlint + oxfmt).

Context

The repo already uses oxlint (replacing eslint) and it's working well. But oxlint doesn't
handle formatting, so formatting issues have crept in. Braden tested two formatters:

  • oxfmt: produced a massive diff (+29,427 / -36,323) with many changes that hurt readability
  • dprint: produced a cleaner diff (+18,328 / -13,083) with changes that are "clear improvements"

Both are Rust/WASM-based and fast. oxlint + dprint + stylelint runs in <3s vs 55s with eslint.

Braden's oxfmt config was minimal

He only used:

{
  "printWidth": 120,
  "singleQuote": true
}

PR branch: open-craft/frontend-app-authoring@braden/oxfmt

This didn't take advantage of oxfmt's newer options, particularly objectWrap: "preserve".

Braden's dprint config

From open-craft/frontend-app-authoring@braden/dprint (dprint.json):

{
  "lineWidth": 120,
  "indentWidth": 2,
  "useTabs": false,
  "typescript": {
    "quoteStyle": "alwaysSingle",
    "jsx.quoteStyle": "preferDouble",
    "semiColons": "always",
    "trailingCommas": "onlyMultiLine",
    "useBraces": "always",
    "operatorPosition": "maintain",
    "arrowFunction.useParentheses": "maintain",
    "module.sortImportDeclarations": "maintain",
    "importDeclaration.sortNamedImports": "maintain",
    "importDeclaration.preferSingleLine": false,
    "exportDeclaration.sortNamedExports": "maintain"
  },
  "json": {},
  "markdown": {},
  "excludes": [
    "**/node_modules", "**/*-lock.json", "**/dist", "**/coverage",
    "jest.config.js", "env.config.*", "example.env.config.*",
    "module.config.js", "**/messages.{ts,js}"
  ],
  "plugins": [
    "https://plugins.dprint.dev/typescript-0.95.15.wasm",
    "https://plugins.dprint.dev/json-0.21.3.wasm",
    "https://plugins.dprint.dev/markdown-0.21.1.wasm"
  ]
}

A separate dprint.messages.json extends this with lineWidth: 10000 for src/**/messages.{ts,js}.

Mapping dprint settings to oxfmt

Direct matches

dprint setting value oxfmt equivalent value
lineWidth 120 printWidth 120
indentWidth 2 tabWidth 2
useTabs false useTabs false
quoteStyle "alwaysSingle" singleQuote true
jsx.quoteStyle "preferDouble" jsxSingleQuote false
semiColons "always" semi true
trailingCommas "onlyMultiLine" trailingComma "es5" (closest match)

Key oxfmt setting Braden didn't use

objectWrap: "preserve" (which is actually the default now) — this tells oxfmt to respect
existing line breaks rather than aggressively reflowing based on printWidth. This directly
addresses the main complaint about prettier-style formatting noise.

Gaps — dprint settings with no oxfmt equivalent

dprint setting value oxfmt status
operatorPosition "maintain" No equivalent; oxfmt will reformat
arrowFunction.useParentheses "maintain" Only "always" / "avoid"; no maintain
useBraces "always" No equivalent (enforces braces on if/else)
importDeclaration.preferSingleLine false No equivalent
lineWidth (messages) 10000 printWidth max is 320

The pattern here is that dprint supports "maintain" on many settings, meaning "don't change
what the developer wrote." oxfmt forces a binary choice, which creates more diff churn.

Additionally, oxfmt caps printWidth at 320, so the dprint approach of setting lineWidth: 10000
on messages files (to effectively disable line wrapping) can only be approximated. In practice
320 chars is enough for most message lines, but it's a real limitation.

Proposed oxfmt config to test

{
  "$schema": "./node_modules/oxfmt/configuration_schema.json",
  "printWidth": 120,
  "singleQuote": true,
  "jsxSingleQuote": false,
  "semi": true,
  "trailingComma": "es5",
  "objectWrap": "preserve",
  "arrowParens": "always",
  "bracketSpacing": true,
  "insertFinalNewline": true,
  "ignorePatterns": [
    "**/node_modules", "**/*-lock.json", "**/dist", "**/coverage",
    "jest.config.js", "env.config.*", "example.env.config.*",
    "module.config.js"
  ],
  "overrides": [
    {
      "files": ["src/**/messages.ts", "src/**/messages.js"],
      "options": { "printWidth": 320 }
    }
  ]
}

Next steps

  • Test the proposed oxfmt config against the repo and measure the diff size
  • Compare the diff quality (readability of changes) against the dprint branch
  • If the diff is comparable, this strengthens the case for staying in the oxc ecosystem

@bradenmacdonald
Copy link
Copy Markdown
Contributor Author

bradenmacdonald commented Apr 14, 2026

My initial reaction was that I'd prefer to stay in one "ecosystem" if possible, so being able to use oxlint and oxfmt seems great in theory. That being said, after looking into this a bit, I agree that oxfmt is likely not the tool we're looking for.

I think it could be, in the future! For now they're focusing on matching prettier, but as I understand, they're open to adding more configuration options and flexibility once they're finished that initial push.

I decided to see if a more fleshed-out .oxfmtrc.json (generated by claude) could produce a smaller diff. I ended up with +30582, -37728 lines changed.

It's funny, Claude's explanation and options changes make sense, but it actually resulted in an even larger diff 😆

@bradenmacdonald
Copy link
Copy Markdown
Contributor Author

bradenmacdonald commented Apr 14, 2026

For me the two options I've considered strongly are:

  1. Using dprint (until oxfmt is more flexible), which is my recommendation, or
  2. Upgrading our eslint (from 8 -> 10), and using only the ESLint stylistic rules. This could match our previous config exactly, though in my opinion after reviewing the dprint format, I think our previous config could really be improved a lot. I expect that this would be somewhat faster than our current usage of eslint, but I expect it would still be much slower than the rust-based ones (dprint / oxfmt / biome).

I'm fine with option 2 if folks object to dprint, but so far I think it's looking pretty nice. We can also continue to experiment with just this repo if there's more to learn before deciding.

Having been forced to use prettier on a large project last year, I am definitely not a fan of its approach, and the issue with noisy git diffs is very real.

@arbrandes
Copy link
Copy Markdown
Contributor

arbrandes commented Apr 14, 2026

@bradenmacdonald, my reactions.

As I'm sure you're aware, this kind of situation is very common (specially in the Javascript Land) when attempting to use new tools: A) the new thing is fast, fancy and/or sexy, but doesn't do everything we want, so B) let's add this other even newer, fancier, and sexier tool to the stack... GOTO A.

On the other hand, remaining on the same stack forever is not realistic. Eventually the whole industry is going to move on from eslint (and jest, and webpack), and if we don't have a plan to move with it, we'll be left stranded.

Which is all to say: I'm glad you're trying this stuff out, but I'm also glad it's contained to this MFE. I'm not going to stand in the way as long as you agree these are experimental: when this codebase is converted to frontend-base later on (likely this year), we're only going to use oxlint/dprint in the Authoring app if it makes sense to do it for all apps (including opening PRs like this across the board). If it doesn't, we'll have to roll it back. Deal? :)

@bradenmacdonald
Copy link
Copy Markdown
Contributor Author

@arbrandes

let's add this other even newer, fancier, and sexier tool to the stack... GOTO A.

dprint is actually 6 years old, vs. ~17 months for oxfmt, so I wouldn't call it newer, fancier, or sexier :p But I absolutely get your point. We pay a big cost every time we switch tools, so there has to be a clear benefit. In this case though I think the benefits are clear: oxlint is way, way, way faster than eslint while also being able to run "type-aware" rules, so it can catch things like missing await that our old eslint config could not. It's also much easier to configure and upgrade (compare to eslint where we're still 2 major versions behind).

As for dprint, I don't have such a strong opinion, but for the time being we've had pretty minimal code formatting standardization on this repo, and I think the dprint diff looks like a nice step forward.

when this codebase is converted to frontend-base later on (likely this year), we're only going to use oxlint/dprint in the Authoring app if it makes sense to do it for all apps (including opening PRs like this across the board). If it doesn't, we'll have to roll it back.

Yes, that's what I had in mind anyways. Ideally we'd be able to use oxlint+oxfmt by the time that happens, if they're able to add the "maintain" configuration options we need by then. But for now I'd like to try oxlint+dprint just in this repo and see how it goes. I've been very impressed by oxlint so far in our 10 week trial.

And if consensus is to use eslint or some other tool, we'll switch over to that with the frontend-base upgrade.

@ChrisChV
Copy link
Copy Markdown
Contributor

when this codebase is converted to frontend-base later on (likely this year), we're only going to use oxlint/dprint in the Authoring app if it makes sense to do it for all apps (including opening PRs like this across the board). If it doesn't, we'll have to roll it back.

Yes, that's what I had in mind anyways. Ideally we'd be able to use oxlint+oxfmt by the time that happens, if they're able to add the "maintain" configuration options we need by then. But for now I'd like to try oxlint+dprint just in this repo and see how it goes. I've been very impressed by oxlint so far in our 10 week trial.

I agree to try oxlint+dprint. I've also liked how oxlint has performed over the past few weeks. I wish dprint had more settings to lower the diffs in this change, so that the rollback, if it happens, doesn't have too many changes either. I've investigated, and there's not much that can be done beyond what was already done in this PR. @bradenmacdonald thanks for this work!

@arbrandes
Copy link
Copy Markdown
Contributor

the rollback, if it happens

To be clear, I'm hoping it doesn't happen, and also that by then we're on oxfmt already. 🤞🏼

@bradenmacdonald
Copy link
Copy Markdown
Contributor Author

@arbrandes Would you be able to force-merge this, overriding the codecov checks?

@arbrandes arbrandes merged commit a0d5290 into openedx:master Apr 14, 2026
5 of 7 checks passed
@github-project-automation github-project-automation bot moved this from Needs Triage to Done in Contributions Apr 14, 2026
@arbrandes
Copy link
Copy Markdown
Contributor

Done!

@bradenmacdonald bradenmacdonald deleted the braden/dprint branch April 14, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core contributor PR author is a Core Contributor (who may or may not have write access to this repo). open-source-contribution PR author is not from Axim or 2U

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants