DEV Community

Cover image for Configure Helix for Typescript + ESLint + Prettier/dprint
Zisulin Morbrot
Zisulin Morbrot

Posted on

Configure Helix for Typescript + ESLint + Prettier/dprint

The new 23.10 version of Helix dropped the other day and I thought it was finally time that I got my favourite editor set up for web development with linting and auto-formatting of TS/TSX, JS/JSX, HTML, CSS, JSON and MD files.

I set out to do this a couple of times before but was never able to actually get it to work. Now that multi-LSP support is stable though, I can finally report that I got it up and running.

This repo has an example Typescript + ESLint + Prettier/dprint project with a corresponding Helix config. We are currently discussing if we should add support for web development to Helix's default config over here.

Currently, auto-formatting on save with vscode-eslint-language-server is not yet working so please let me know if you have a working setup for this!

One thing that I struggled with during this endeavour was that I was never sure if something didn't work because my Helix config wasn't right or because I used the wrong magic incantation of commands to set up Typescript and ESLint in my project correctly. Here's a full rundown of how to do that:

Prerequisites

For completeness' sake and because it's never mentioned anywhere else, here are all the langservers you need to have installed.

Language Servers

Install the typescript-language-server. This is used by default by Helix.

npm install -g typescript-language-server typescript
Enter fullscreen mode Exit fullscreen mode

The vscode-langservers-extracted package provides language servers for Html, CSS, Json and ESLint.

npm i -g vscode-langservers-extracted
Enter fullscreen mode Exit fullscreen mode

I'm also using emmet-ls for convenience:

npm install -g emmet-ls
Enter fullscreen mode Exit fullscreen mode

Formatters

dprint

We all know that a tool being written in Rust is enough reason for any true Rustacean to immediately switch over to it so in my personal setup I'm using dprint as my formatter:

curl -fsSL https://dprint.dev/install.sh | sh
Enter fullscreen mode Exit fullscreen mode

In all seriousness though, I found dprint to be slightly faster than Prettier and liked its defaults better.

Prettier

If you want to go for the canonical Typescript + ESLint + Prettier setup, install Prettier (globally or locally) and comment/uncomment a few lines in the .helix/languages.toml file.

npm i -g prettier
Enter fullscreen mode Exit fullscreen mode

Bonus: Just use Deno?

For my personal projects, I'm currently exploring simply using Deno for both linting and formatting as I like its defaults and the simplicity of using just one langserver.

curl -fsSL https://deno.land/x/install/install.sh | sh
Enter fullscreen mode Exit fullscreen mode

Here's how the languages.toml looks like for that:

[language-server]
deno = { command = "deno", args = [ "lsp" ]}

[[language]]
name = "tsx"
language-servers = [ "deno", "emmet-ls" ]
auto-format = true
Enter fullscreen mode Exit fullscreen mode

Simple and effective, right?

If I could just have it my way, I would use Deno even for non-Deno projects just for its CLI, it is just such a pleasure to use and honestly defines a standard for modern web development that other tools should take as an example (Shameless plug: Have you read my article on Deno's full-stack Fresh framework yet?).

Helix Setup

Here's the full languages.toml:

[language-server]
deno = { command = "deno", args = [ "lsp" ]}
emmet-ls = { command = "emmet-ls", args = [ "--stdio" ]}

[language-server.eslint]
command = "vscode-eslint-language-server"
args = ["--stdio"]

[language-server.eslint.config]
codeActionsOnSave = { mode = "all", "source.fixAll.eslint" = true }
format = { enable = true }
nodePath = ""
quiet = false
rulesCustomizations = []
run = "onType"
validate = "on"
experimental = {}
problems = { shortenToSingleLine = false }

[language-server.eslint.config.codeAction]
disableRuleComment = { enable = true, location = "separateLine" }
showDocumentation = { enable = false }

[language-server.vscode-json-language-server.config]
json = { validate = { enable = true }, format = { enable = true } }
provideFormatter = true

[language-server.vscode-css-language-server.config]
css = { validate = { enable = true } }
scss = { validate = { enable = true } }
less = { validate = { enable = true } }
provideFormatter = true


[[language]]
name = "typescript"
language-servers = [ "typescript-language-server", "eslint", "emmet-ls" ]
# formatter = { command = "prettier", args = [ "--parser", "typescript" ] }
formatter = { command = "dprint", args = [ "fmt", "--stdin", "typescript" ] }
auto-format = true

[[language]]
name = "tsx"
language-servers = [ "deno", "eslint", "emmet-ls" ]
# formatter = { command = "prettier", args = [ "--parser", "typescript" ] }
formatter = { command = "dprint", args = [ "fmt", "--stdin", "tsx" ] }
auto-format = true

[[language]]
name = "javascript"
language-servers = [ "typescript-language-server", "eslint", "emmet-ls" ]
# formatter = { command = "prettier", args = [ "--parser", "typescript" ] }
formatter = { command = "dprint", args = [ "fmt", "--stdin", "javascript" ] }
auto-format = true

[[language]]
name = "jsx"
language-servers = [ "typescript-language-server", "eslint", "emmet-ls" ]
# formatter = { command = "prettier", args = [ "--parser", "typescript" ] }
formatter = { command = "dprint", args = [ "fmt", "--stdin", "jsx" ] }
auto-format = true

[[language]]
name = "json"
# formatter = { command = "prettier", args = [ "--parser", "json" ] }
formatter = { command = "dprint", args = [ "fmt", "--stdin", "json" ] }
auto-format = true

[[language]]
name = "html"
language-servers = [ "vscode-html-language-server", "emmet-ls" ]
formatter = { command = 'prettier', args = ["--parser", "html"] }
auto-format = true

[[language]]
name = "css"
language-servers = [ "vscode-css-language-server", "emmet-ls" ]
formatter = { command = 'prettier', args = ["--parser", "css"] }
auto-format = true
Enter fullscreen mode Exit fullscreen mode

Project Setup

I'm using a NextJS starter project just to have something in hand and show off a correctly configured project. Here's what I did to create that.

npx create-next-app@latest helix-webdev-config
Enter fullscreen mode Exit fullscreen mode

Add typescript-eslint to add full Typescript support.

npm i -D @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint typescript
Enter fullscreen mode Exit fullscreen mode

Exclude pure formatting lints from eslint. Formatting is a different concern than linting and should be handled separately.

npm i -D eslint-config-prettier
Enter fullscreen mode Exit fullscreen mode

To quote directly from the above:

Note that even if you use a formatter other than prettier, you can use eslint-config-prettier as it exclusively turns off all formatting rules.

Here's the full but minimal .eslintrc.js for your reference:

module.exports = {
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "next/core-web-vitals",
    "prettier", // This disables rules in presets that conflict with prettier (or other formatters).
  ],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: true, // Find the closest tsconfig.json for each source file.
    tsconfigRootDir: __dirname,
    ecmaVersion: "latest",
    sourceType: "module",
  },
  plugins: ["@typescript-eslint"],
  root: true,
};
Enter fullscreen mode Exit fullscreen mode

Now, when you open a file and save it, you should see the formatter in action. To format the whole project, run:

dprint fmt
Enter fullscreen mode Exit fullscreen mode

You should now have a working linting and auto-formatting setup for web development in Helix, happy hacking!

Parting thoughts

I hope I could help someone in setting up their Helix editor properly and if so, I would appreciate a like and/or comment. I know that I struggled a few times with going through random people's configs and trying to find out what worked and what didn't and would've loved a comprehensive example like the one I hope I created.

Are you already using Helix as your daily driver? I know I am since I first came across it and it's still getting better and better every day.

I'm also interested in other people's experience with somewhat niche tools like dprint and Deno and if they're up to snuff for real-life production usage.

Since I'm using dprint for the first time, do you have any experience with it? Are you using it at work and does it hold up?

I just love the "Deno experience" - have you ever tried or advocated for using Deno for Typescript development? Have you built an app with Fresh?

Let me know in the comments!

Top comments (0)