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
The vscode-langservers-extracted
package provides language servers for Html, CSS, Json and ESLint.
npm i -g vscode-langservers-extracted
I'm also using emmet-ls
for convenience:
npm install -g emmet-ls
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
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
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
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
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
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
Add typescript-eslint
to add full Typescript support.
npm i -D @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint typescript
Exclude pure formatting lints from eslint. Formatting is a different concern than linting and should be handled separately.
npm i -D eslint-config-prettier
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,
};
Now, when you open a file and save it, you should see the formatter in action. To format the whole project, run:
dprint fmt
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)