I mentioned in a recent post that I embarked on a lengthy side-quest while configuring a JavaScript environment in Emacs for a hobby project. It might be time to explain what that was about.
Getting the quest
I picked up the quest when I went looking for a JavaScript LSP, and found that almost all the options require installation via npm. I do have a couple of npm based things installed, but I've been feeling sour about it after some recent supply chain attacks against the packaging system and have tried to work around needing it. I hasten to say that my worries weren't focused around a notion of npm as more of a security risk than other supply chains I use, but around the desultory and inexpert way I interact with it. In the case of my other supply chains, I update regularly and follow sources of on-line news that are relevant to the ecosystems in play. Not so with npm. I worried that I would be late to the party on knowing that I had a problem and taking steps to fix it.
The task at hand
I wanted to install a JavaScript LSP without using npm.
To start with, it's not in the least surprising that most JavaScript language servers are distributed via npm. I mean the protocol specifies JSON, so there is every reason to write them (in whole or in part) in JavaScript, and in that case npm is the default distribution mechanism. But, as I indicated above, I was hoping to avoid adding that window of vulnerability to my systems.
If at first you don't succeed...
So, LSP being a microsoft controlled protocol, one of the few central locations that which a list of available servers is maintained by microsoft, but there are others. In any case, I found only a few currently maintained options:
- typescript-language-server
- flow
- quick-lint-js
- biome-lsp (a part of the greater biome project)
npm
install. The third is available from my distribution's package repository
and works, but there is a lot that it doesn't implement: it really is a linter
than knows the language server protocol (and I don't want to diss
that—being a server gives it flexibility in interaction modes—but it
isn't all that I hoped for). That
leaves biome, and
actually, it's installation instructions also start by calling npm, but further
down the README.md it says "Biome doesn't require Node.js to
function.", and you can
find build-from-source
instructions on the project homepage. Frankly, they're not very good as they
seem to assume that if you're asking that question you can guess your way
through it, but I got it done.
Being my own worst enemy
The last hurdle on my quest was largely of my own making. You see, to run
biome as a server you have to pass it the lsp-proxy
command, which is emphatically not the spelling I would have chosen for
that option if I'd been writing the tool. And for some reason, when I was
configuring Emacs, I kept spelling it differently. I spent an increasingly
frustrating half and hour making variations on the same mistake before noticing
what I had been doing all along. Use the right spelling and it just works.