spotifyovercastrssapple-podcasts

elm-tooling with Simon Lydell

elm-tooling helps you manage versions of tools like elm-format and elm. It downloads them more efficiently and securely. We discuss with the author, Simon Lydell.
January 18, 2021
#22

Elm tooling in ci

Transcript

[00:00:00]
Hello Jeroen. Hello Dillon. And today we've got another special guest to greet. Simon,
[00:00:07]
hello, welcome to the show. Hello, thanks for letting me be on the show. Our pleasure. So for
[00:00:14]
those who don't know you, this is Simon Lydell. And do you want to give a quick introduction and
[00:00:20]
tell us who you are and a little bit about what you do? Yes, so I'm Simon and I'm from Sweden and
[00:00:27]
I'm a developer and I love Elm. Awesome. That sounds very good to us. And you've got a tool,
[00:00:36]
a meta tool. So, you know, that's near and dear to our hearts. Jeroen and I love tools for helping
[00:00:43]
you use tools. So you've got a tool called Elm Tooling CLI. And so, well, let's get into it. So
[00:00:51]
what the heck is Elm Tooling CLI? Like what actually is it? Let's define what it is before
[00:00:58]
we dive into the details of how to use it. Yeah. So Elm Tooling is a command line program that
[00:01:05]
manages your tools. It's a drop in replacement for installing Elm, Elm format and Elm JSON using NPM,
[00:01:13]
but it does it in a faster and more secure way. That does make sense. Although the more secure
[00:01:22]
part was not on my radar. What do you mean by that? So if you've ever installed Elm or Elm
[00:01:29]
format with NPM, you might know that you don't actually install Elm. You just install some
[00:01:35]
JavaScript code that eventually downloads Elm for you. That code doesn't actually verify what you
[00:01:41]
downloaded. It just downloads a binary from a URL and hopes that it's what you thought it was. So
[00:01:48]
Elm Tooling also downloads the same URLs, but it has SHA256 hashes for everything. So it should be
[00:01:58]
able to verify that you got the Elm binary and not some Elm binary with a virus in it or whatever.
[00:02:04]
That's very interesting. And it also is able to cache the binaries in a way like if you have Elm
[00:02:12]
in your dev dependencies in your package.json in an Elm project, which is a common practice. You've
[00:02:18]
got Elm, Elm format, Elm test all in your package.json dev dependencies. Whenever you do NPM install,
[00:02:26]
it's going to go download those, whether it's installed that exact Elm binary version, Elm
[00:02:32]
format binary version in a different project that you NPM installed. Is that right?
[00:02:37]
Yeah, exactly. So the point of installing things with NPM locally is that you want versions for
[00:02:44]
every project, but it feels very wasteful to me to have a copy of the entire Elm executable over
[00:02:51]
and over on your hard drive. So what Elm Tooling does is that it saves all of the executables in
[00:02:58]
one place in your home folder and only links to them in each project. This way you save a lot of
[00:03:05]
the space. It saves them in the Elm home directory, right? Wherever that is.
[00:03:10]
Yeah, exactly. So the Elm home directory is usually in your home folder and called.elm.
[00:03:18]
And the Elm compiler saves all the Elm packages that it downloads in there. And I know that
[00:03:25]
another tool called Elm JSON also saves a couple of things in there. So Elm Tooling follows that
[00:03:31]
pattern and also saves the Elm binary and the Elm format binary and stuff like that in there.
[00:03:36]
I think Elm Review also downloads things in there, but I don't remember actually. But yeah,
[00:03:42]
that's usually where any Elm data goes.
[00:03:45]
Right. And then if you're caching that in your CI process, however, whether it's Netlify or
[00:03:51]
custom CI or whatever it is, if you cache that directory, which you should on your CI,
[00:03:57]
then it's going to work. You don't have to have specific caching rules for these specific tools.
[00:04:02]
Yeah, that's one of the things I liked with that approach. I'm thinking that people are
[00:04:07]
already caching that directory, so they don't need any extra work if they want to use Elm Tooling.
[00:04:13]
So what do you gain when you cache those binaries? Maybe let's go over that.
[00:04:17]
Yeah, you gain two things. It's performance and reliability. So performance in that it's faster
[00:04:27]
to restore things from a cache than going out on the internet and downloading things over again.
[00:04:33]
And reliability, that's maybe more for Elm packages than for Elm Tooling. So if you were
[00:04:39]
to install Elm packages every time in your CI, that means your CI is talking to the Elm package
[00:04:47]
site every time. And if that site goes down, then your CI won't run. But if you have them cached,
[00:04:53]
there's no need to talk to the server. You already have everything. So you can just restore the cache,
[00:04:58]
your CI keeps working until the site is up again. But for Elm Tooling, well, I guess a lot of people
[00:05:04]
are using GitHub actions these days, and the binaries are also downloaded from GitHub releases.
[00:05:10]
So if one is down, maybe the other one is down too. I guess that if you have another CI, it could
[00:05:18]
probably help. But on the other hand, you might have your code on GitHub, so your CI might not
[00:05:23]
run anyway. Okay, so we'll get more into some best practices and all of that. Let's make sure we've
[00:05:30]
gotten all our definitions here. So Elm Tooling CLI is an NPM package that you install. So to
[00:05:39]
install Elm Tooling CLI, you can't manage that tool with Elm Tooling CLI yet. Maybe someday.
[00:05:49]
No, that wouldn't work very well. What comes first, Elm Tooling or Elm Tooling?
[00:05:54]
It's definitely a chicken and egg problem. So you have to get that tool. So you NPM install that.
[00:06:03]
Most likely, you're going to want to NPM install Elm Tooling into your dev dependencies so you can
[00:06:10]
manage the version. And this is all this philosophy of locking in specific versions
[00:06:17]
in your environment. So somebody, they don't have their machine specific setup where they're saying,
[00:06:23]
oh, why isn't the code compiling on my machine and it is on another person's? Well, one person has
[00:06:30]
Elm 19.0 installed and another has 0.19.1. And it's locked in through that specification. And
[00:06:38]
you can also lock in the specific version of the Elm Tooling CLI in your dev dependency. So that
[00:06:44]
would be the recommended practice there, right? Yeah. Another point about the versions, you talked
[00:06:48]
about Elm and having different Elm versions in different projects. So that hasn't mattered in
[00:06:55]
quite some time now because Elm 19.1 came out over a year ago. But something that does matter more
[00:07:02]
is Elm format, actually. So if someone is on Elm format 0.8.3 and then one product has updated to
[00:07:10]
0.8.4, you wouldn't want two contributors being on different versions and just keep formatting
[00:07:16]
the code back and forth all the time. But as you said, it's also important to lock your Elm Tooling
[00:07:24]
version in every project because the Elm Tooling program actually contains all the links to all Elm
[00:07:33]
tools and all Elm tool versions that it knows about, which might sound strange at first,
[00:07:40]
if you think about it. What if NPM worked that way? What if the NPM program itself had the entire
[00:07:47]
registry in it? That wouldn't work because the registry has tens of millions of packages
[00:07:53]
and you would need to update that every day, I guess. Every second? Yeah, probably every second.
[00:08:01]
But for Elm, the tooling changes so seldomly that there was actually no need to complicate the tool
[00:08:09]
and try to have an online registry of all versions and all tools. I just put it all into the code
[00:08:16]
instead, which is a very reliable way of doing it, if you can, because you don't have a dependency
[00:08:23]
on a registry and you don't need a lock file because the tool itself contains those
[00:08:29]
SHA256 hashes that I was talking about. So maybe let's go through how Elm Tooling chooses
[00:08:37]
one version or another. How do you say, please Elm Tooling, can you install Elm 0.19.1 or 0.19.0?
[00:08:46]
How does it do that? Yeah, that's the second part of this project. I said that Elm Tooling
[00:08:55]
was all about managing your tools. That doesn't mean just installing them. It also means managing
[00:09:02]
the metadata around your tool. So you can run Elm Tooling in it to create a file called elmtooling.json.
[00:09:10]
And this is a file where tools can share configuration, but it also contains the versions
[00:09:18]
of the tools that you use. So if you run Elm Tooling in it, it's going to have a field in there
[00:09:23]
that says tools. And in there, there's going to be Elm 0.19.1 and Elm format 0.8.4 or whatever.
[00:09:34]
So you have this elmtooling.json file that specifies all the tools that you use in this
[00:09:41]
project and their versions. So when you then go ahead and run Elm Tooling install, it looks for
[00:09:47]
an elmtooling.json, finds the tools and the versions you use and installs those. Yeah, and
[00:09:54]
there are specific versions that are not version ranges like Elm usually does. Yeah, exactly. It's
[00:10:00]
specific versions because you depend on one version in your project and that's it. Right, and there's
[00:10:07]
this thing that we see often with npm packages where it says there are 271 security vulnerabilities
[00:10:14]
in your npm package configuration. And so the npm binaries wrapping these Elm tools, which are really
[00:10:24]
just a simple shim that download and executable, which is actually like Haskell binary in the case
[00:10:32]
of Elm format and Rust, I think in the case of Elm JSON. So it's not JavaScript code like regular
[00:10:40]
npm binaries. It's just wrapping, downloading them and you get these security vulnerability
[00:10:44]
warnings because it's npm and you have a different sort of technique that you use to
[00:10:52]
download it. And so you get these versions like the Elm binary on npm, I think it's like elm
[00:11:00]
at version 0.19.1 dash something, dash one, dash two. And sometimes people get confused and they
[00:11:11]
think is there a patch release for Elm? Why is the npm binary changing? But it's actually not. It's
[00:11:16]
just the wrapper for downloading it and there's a new best practice for avoiding the security
[00:11:22]
warnings on npm. So using the Elm tooling CLI bypasses those warnings and really just lets you
[00:11:30]
do it in a much more direct way. Yeah. And as a side bonus so that if you've ever installed Elm,
[00:11:37]
you might have noticed that it said like installed 70 packages from 69 different people. And when I
[00:11:43]
see that, I'm like, why? I just wanted Elm and you gave me 70 packages. What are all these packages
[00:11:51]
doing? And none of them including Elm itself, because it's just an installer for Elm, right?
[00:11:57]
Oh yeah, yeah, exactly. Yeah. When you npm install Elm, there's like a message that says,
[00:12:03]
this is just downloading this binary for you. You can also download it from this URL or use these
[00:12:10]
instructions to set it up on your machine without npm. So it's almost like recommending that you not
[00:12:15]
use npm to do it because it's not great. But it is really important to lock in the specific versions
[00:12:22]
because I got my first development job out of college doing like a Ruby on Rails job back just
[00:12:29]
as Bundler, this sort of Ruby library version manager started coming into use. And it was like
[00:12:37]
a big deal because it was like, it was absolute chaos. It was the wild west in the days before
[00:12:44]
that version management system. Because what would happen is you would globally install these Ruby
[00:12:50]
gems, these Ruby libraries on your development machine. And you'd say, why is there this bug?
[00:12:56]
Why is this SQL query blowing up? And it's like, oh, you're using the wrong version of active record.
[00:13:02]
You have to install this one, check out the dev instructions for this. And then somebody installs
[00:13:07]
the latest version on their machine. Everybody has different versions. There's a different version on
[00:13:11]
the actual server that's running your production code. It's absolute madness. So it was a big deal
[00:13:18]
when Bundler came out and there was a way to lock in the versions. It was like a revolution.
[00:13:22]
And so I don't know, people who are coming onto the scene these days and starting to program might
[00:13:28]
be like not aware of the absolute madness that is working with globally installed binaries that can
[00:13:36]
be different on different machines, including the production machine. It is not good and you do not
[00:13:41]
want to be there. So you need some way to manage binaries and just globally installing versions
[00:13:46]
is at some point going to bite you and it's not going to be fun. And so you've got to manage it
[00:13:52]
somehow. Managing it through a package JSON works and it's ugly in some ways, but it locks in the
[00:13:59]
versions. But if you can do it in an even simpler way and more performant, all the better. End PSA.
[00:14:04]
This message brought to you by. That's what a PSA means. Public service announcement.
[00:14:11]
So I think we've sort of laid out what Elm tooling CLI is. It's an NPM package. There's a
[00:14:19]
specification for sort of how it looks up where binaries are. It finds them in a file called elmtooling.json.
[00:14:27]
You can initialize that file using Elm tooling init on the CLI, elm dash tooling space init,
[00:14:34]
and that's going to pull binaries from your package.json. It's going to see that you have
[00:14:39]
elm version 0.19.1 dash four installed and figure out that that represents elm version 0.19.1.
[00:14:48]
Put that in your elmtooling.json file and now you're using the same binaries across machines
[00:14:54]
and your production environment. You don't specify the dash four in Elm tooling, do you?
[00:14:59]
No, what Dillon meant was that Elm tooling init looks at your package.json to figure out what
[00:15:07]
versions to put in your elmtooling.json. But if you want to be really pedantic, it doesn't actually
[00:15:15]
look in your package.json. It looks in your node modules folder and sees if there's an Elm in there
[00:15:22]
and what version it is on. And if you can't find that, it actually falls back to looking at your
[00:15:27]
elm.json because you specify the Elm version in there too. Oh, that's a cool detail. I didn't
[00:15:34]
realize that. I like that. I think, yeah, these solutions are very elegant. I think you've put
[00:15:39]
a lot of thought into these details and it makes it a really smooth experience. So I think those
[00:15:44]
are sort of like the key building blocks, but there's one last thing that we haven't touched on
[00:15:49]
that I think is really essential, which is how once you've used elmtooling.json to
[00:15:55]
init your Elm tooling, once you've used Elm tooling CLI to init your elmtooling.json file,
[00:16:01]
you've got the binaries, it's downloaded them, you know, so you run elm dash tooling space
[00:16:07]
install to install the binaries. Now, how do you run the correct version that it has downloaded?
[00:16:14]
Great question. So the way it works is that Elm tooling install, it mimics how NPM works.
[00:16:22]
So if you install a command line tool locally with NPM, it's going to create links to those
[00:16:31]
command line tools in your local node modules slash dot bin folder and elmtooling does the
[00:16:38]
same. So it creates a link in, for example, node modules slash dot bin slash Elm to dot Elm in
[00:16:47]
your home folder slash Elm slash the version you want and slash Elm again. And the reason it does
[00:16:55]
this is because this way everything just works out of the box. All tools that support local
[00:17:03]
installations of Elm and Elm format by looking in that node module slash dot bin folder, they're
[00:17:09]
going to find the links that Elm tooling made, but being none the wiser, they won't know that
[00:17:15]
it was an NPM that created them. And this means that if you want to run the tools from the command
[00:17:21]
line, we can also reuse the way you run command line tools installed by NPM, which is using
[00:17:27]
NPX. So you can type in the terminal NPX space Elm space make or something. And this NPX tool
[00:17:37]
will look in that local node module slash dot bin and see if there's an Elm in there. And if it is,
[00:17:43]
it will execute that. So Elm tooling is not only piggybacking on the NPM way of installing local
[00:17:53]
command line tools. It's also using its execution program or whatever you should call it, NPX.
[00:18:00]
I really like that approach. So NPX essentially, is it as simple as NPX just looks in your node
[00:18:08]
modules slash dot bin slash. I guess there's the additional step of if it's not installed,
[00:18:17]
by default, NPX will attempt to install something. So sometimes people will say,
[00:18:22]
run this command. If you want to set up a new Gatsby website, then you do NPX Gatsby init.
[00:18:29]
And it will actually, if there's no Gatsby in your package dot JSON dependencies or dev dependencies,
[00:18:36]
it will actually install it and then execute the version that it locally installed.
[00:18:42]
Right. That changed in NPM version seven. Now it doesn't do that anymore. You need to do dash dash.
[00:18:49]
Yes, I think to for to install something from the internet. Oh, that's great. I didn't know about
[00:18:54]
that. Yeah, I'm still using NPM six and some colleagues starting using NPM seven. They were
[00:18:59]
like, hey, this is not working for me. That is confusing behavior because you use NPX
[00:19:06]
to just say, I want to run the locally installed version of this binary in this projects package
[00:19:13]
JSON. And then it's like, OK, I'm installing this thing. You're like, what? No, I didn't mean to do.
[00:19:18]
So I noticed in Simon's instructions and examples for the Elm tooling CLI, you use the flag NPX
[00:19:27]
space dash dash no dash install to say, which I do not mean. That's really interesting. I didn't
[00:19:34]
realize that. I wonder what would happen in your build system, though. Is it going to stall out
[00:19:40]
because it's waiting for an interactive prompt or is it going to fail because it knows it's in a
[00:19:44]
non interactive environment? Might have to look that up. But very good question. But I've never
[00:19:49]
liked the behavior that NPX first tries to find something locally and globally and then tries to
[00:19:56]
download it. It often happens to me that I type NPX space something and I'm in the wrong folder.
[00:20:03]
So it starts downloading something and I have to press control C. And I wonder, like, what did it
[00:20:10]
download now? So it's great that they they're changing that. But for now, I'm using NPX dash
[00:20:19]
dash no install, just like you said, in CI, just to be sure that it's not trying to download
[00:20:25]
anything, which makes the CI a lot slower. So you mentioned another detail there that I
[00:20:31]
wasn't aware of. You said that it looks locally then globally, then tries to install it. So you're
[00:20:37]
saying if I had let's say I had like let's say I'm not using Elm tooling CLI and I have globally
[00:20:45]
installed Elm. So I did npm install dash dash global or dash g Elm. So I have it installed in
[00:20:52]
my global npm. And then if I do NPX Elm in a project which does not have Elm in the dev dependencies,
[00:21:01]
do you know is it going to then try to run my globally installed one and not tell me that it's
[00:21:07]
not using a local version? Because I don't like that. I'm pretty sure that's the way it works.
[00:21:12]
Yeah, it's confusing because if you make a mistake, you might think that you're running
[00:21:17]
your local version, but actually it's running something else. Well, I quite like it for some
[00:21:22]
things like for instance, I install Elm review globally, and then I run it on projects where
[00:21:27]
it's not installed yet. So I just do NPX Elm review all the time. And I don't have to think
[00:21:33]
about it a second time. It would be nice if there was at least like a flag to say like,
[00:21:38]
I expect this to happen to exist locally. If it doesn't, don't install it. Don't look for it
[00:21:44]
globally, fail. I wonder if there's an option for that because that's what I want it. I want to be
[00:21:50]
able to do that at least. So, okay. So I did have one question, Simon, around this NPX. So I really
[00:21:56]
like the way that the Elm tooling CLI uses NPX, I think. And I really like the way you've sort of
[00:22:03]
piggyback off of these existing standards and tools to sort of build something simple that can
[00:22:10]
fit into existing workflows. I'm curious, what do you recommend if you're running a tool that expects
[00:22:17]
to find the Elm executable, the Elm binary on the path? How do you get it on the path for that tool?
[00:22:24]
Because it might not be expecting to look it up with NPX. It might be just expecting Elm on the
[00:22:31]
path. Great question. So that's another benefit from using NPX actually, because when it executes
[00:22:37]
the command that you wrote on the command line, it also adds everything in that node modules
[00:22:43]
slash dot bin folder to the path of just that command, which means that if you say NPX,
[00:22:52]
let's say Elm review as an example, then it will add Elm to the path if you have it locally as
[00:23:00]
well. So Elm review can just pretend as if Elm was installed globally because it's going to look
[00:23:07]
like that to Elm review, but your global path variable does not contain Elm. And so because
[00:23:16]
it also looks at the global packages, like if you haven't installed Elm in this project, then
[00:23:22]
Elm will still be available. So this is also quite useful for NPX. Very interesting. And what if
[00:23:29]
you're running a tool that... So you're saying you could run something that's an NPM binary with NPX
[00:23:37]
and then it will include the entire node modules slash dot bin in the path. What if it's not a tool
[00:23:46]
that is installed as an NPM package? What would you recommend for making sure that that's in your
[00:23:53]
path? So what if Elm JSON needed to have access to Elm, but Elm JSON isn't written in node and
[00:24:00]
doesn't use NPM? Is that what you mean? Yeah. And for example, I have a GitHub action that I've
[00:24:08]
published called Elm publish action, which is for publishing an Elm package in your GitHub action
[00:24:15]
script. So you can run your test suite, you can run Elm review, you can do whatever steps you
[00:24:22]
need to make sure things check out and then publish it automatically at the end if you've
[00:24:27]
bumped the version. And I provided a way to pass in a path to the Elm executable. So you could
[00:24:35]
pass in the version, you could pass in the path to node module slash dot bin slash Elm,
[00:24:41]
but it looks on your path. Or actually do I use NPX? I don't remember, but that's one of the
[00:24:46]
confusing things is when you use a tool, you don't know if it's looking in the NPM installs or if
[00:24:52]
it's looking on the path. You could be running a tool that you installed through NPM or you could
[00:24:57]
be running a different tool like this GitHub actions Elm publish action that I have is not
[00:25:05]
something that you are running through the typical NPX, it's running through your GitHub action. So
[00:25:10]
are there other practices you can use to get it onto your path or have you not encountered that
[00:25:15]
use case? I don't think I've encountered this use case, but if you need to execute something and
[00:25:22]
that thing needs to find what's in your node modules dot bin, for example, and nothing is
[00:25:28]
doing that for you, you could always extend the path when you execute that thing in whatever way
[00:25:36]
you want, I guess. Right. Yeah. You could say path equals dollar path, colon node modules slash dot
[00:25:43]
bin. Yeah. I guess something like that should work. I guess there will be like tricky cross platform
[00:25:49]
things. Think about there if that's important to you, but I'm sure there are ways to do it.
[00:25:55]
Yeah. I was just running something that uses the node Elm compiler package. It's an NPM package
[00:26:02]
that allows you to compile Elm code locally. And I was testing a little script that I have that
[00:26:08]
actually compiles and runs some Elm code and it's a JavaScript file, but I was running it through a
[00:26:13]
local file directly. And if I ran it directly, it doesn't look in the node modules slash dot bin
[00:26:21]
folder for that executable. It looks on the path. And so what I was doing for that was I was adding
[00:26:27]
a little script in the package dot JSON. There's a script section. You can say, you know, you can
[00:26:33]
define a script for test is Elm test and it will you don't have to put NPX in front of Elm test
[00:26:39]
in your package dot JSON scripts because similar to NPX, it includes all those NPM executables
[00:26:46]
in your path. And so I was able to use it to create a little script that I run as an NPM script. And
[00:26:52]
that that solved that problem well. But it's it's sort of a there are so many different variables,
[00:26:58]
you know, it's hard to find the best practices. Yeah, that's that is definitely an intuitive
[00:27:04]
behavior of NPX until you learn it. You think it's just a shortcut for executing a binary,
[00:27:10]
but it's also adding all these things to the path. And once you know that things start to click,
[00:27:16]
but if you don't know that it's it's a bit magic almost how it can find things that are installed
[00:27:22]
locally. Yeah, I actually had no idea. I thought it was just looking in your node modules slash dot
[00:27:29]
bin for I thought it was equivalent to just, you know, besides trying to install it and looking
[00:27:34]
for things globally, I thought it was equivalent to just doing like dot slash node modules slash
[00:27:41]
dot bin slash executable name. But that's that's a really important distinction. So what one benefit
[00:27:47]
of having everything in your package, Jason, like having Elm and formats installed and Elm test is
[00:27:53]
that whenever you do NPM install, all of those are installed at once. But if I go through Elm
[00:28:00]
tooling, I'm tooling is installed, but it won't install Elm and Elm formats for you. So how do
[00:28:06]
you make that work seamlessly? Yeah, correct. So to solve that, you can add a script in the script
[00:28:15]
section in your package dot Jason, which is called a post install. And NPM will automatically run
[00:28:21]
this script post install. And in there you can put Elm tooling install. So that means that anytime
[00:28:29]
you write a type NPM install, it's going to install all of your dependencies and then execute
[00:28:34]
the post install script, which runs Elm tooling install. This way you can use Elm tooling, but you
[00:28:41]
don't really need to tell anyone because they won't notice any difference. Yeah. Yeah. So even
[00:28:47]
my colleagues who don't work with Elm, if Elm somehow needs to be installed and run, as long
[00:28:53]
as there is a post install step, they won't notice that it's installed differently. Right. Exactly.
[00:29:01]
Unless your colleagues have listened too much to Richard Feldman. I know you're referring to.
[00:29:09]
Yeah. Because Richard, he likes to talk about the setting in NPM, which is called ignore scripts.
[00:29:17]
And if you enable that setting, NPM won't run any scripts automatically anymore. And the point of
[00:29:23]
that is that you don't want to trigger post install scripts for any random NPM package that
[00:29:28]
you install, which could do almost anything. It's a bit of a security thing. Because post install
[00:29:34]
does two things. It installs. So whenever you do NPM install, it runs the post install script
[00:29:40]
that you have defined in your package JSON. Yeah. Package JSON, it's like a JSON field.
[00:29:46]
And then there's the, then there's the script key. And then you have a list of key value pairs. It's
[00:29:52]
test colon test script post install colon some arbitrary command that you do for your post install.
[00:29:58]
Yeah. But then there's, it also runs the post installs for every package that was just installed,
[00:30:04]
right? Yeah. And that's like the scary part. Yeah. But it's arbitrary script execution for any NPM
[00:30:11]
package that you have installed. It's weird that there's no two different ones for the package when
[00:30:17]
you install it and one for your own project. Exactly. They should be separate because you,
[00:30:24]
of course you trust your own scripts. Why wouldn't you want them to be run?
[00:30:28]
We should, we should just send this podcast episode directly to the NPM team and say,
[00:30:33]
here are our feature requests. I really hope they're aware of this problem.
[00:30:38]
Yeah. But I, I've tried to document this Scotch as good as I can. So there are some tips on how you
[00:30:46]
can work around this problem. Yeah. Yeah. So you just created this website, which has a lot of
[00:30:53]
great tips and some more details on a lot of the things we're discussing. So this is the,
[00:30:57]
we'll have a link in the show notes, but it's elmtooling.github.io slash elmtooling CLI.
[00:31:04]
And there's like a quirks page that describes the ignore scripts and post install and all those
[00:31:09]
details. And this is, this is, this is a really good conversation. I'm glad, you know, I'm glad
[00:31:14]
that we've got you here to discuss this because you've thought through all these ugly details of
[00:31:20]
the, you know, sort of NPM binary execution ecosystem. And, and there's a lot, there's a lot
[00:31:27]
to wrap your head around, but I definitely have some like key takeaways here. And I think just
[00:31:33]
having some of these, you know, some, some of these gotchas and some of these sort of concepts
[00:31:38]
in mind is really helpful. Like the idea that NPX puts the node modules binaries into the path, or
[00:31:46]
even, you know, for, for some people who may, who may not know the, the facts that there is a node
[00:31:51]
module slash dot bin folder is a really good thing to know because we rely on that all the time. And
[00:31:58]
it's just like this little bit of magic that if you understand that things sort of fall into place
[00:32:02]
a little bit better. So it's, it's really nice discussing all these details with you. So one,
[00:32:08]
one other topic. So I think we've sort of covered using the elmtooling CLI pretty well when it
[00:32:15]
comes to local development. Let's talk a little bit about what that looks like in a build environment
[00:32:21]
or in a GitHub action. And, and how does it differ the way that you would run your elmtooling
[00:32:28]
install step in a, in a CI process? Yeah. So I mentioned that you could put a post install
[00:32:34]
script in your package.json to make it easy for local development. You just need to run
[00:32:40]
npm install and you get all your tools at once, but in CI, it's actually better to split them up
[00:32:46]
to install npm packages in one step and your elmtooling in another step. And that's because
[00:32:53]
of caching. So what I recommend is caching both your node modules folder and your dot elm folder
[00:33:00]
in, in, in your home folder, the elm home. And these will have different cache keys. The node
[00:33:06]
modules folder, it depends on your package lock.json. Package lock.json changes, then your
[00:33:12]
node modules will change. So that means that you cannot use your cache anymore, but the dot elm
[00:33:19]
home folder, it does not depend on package lock.json. It depends on your elm.json and your
[00:33:25]
elmtooling.json. So in order to be able to have these separate cache keys, you also need to
[00:33:32]
install them separately. So for this reason, I added a bit of a hack. You can set an environment
[00:33:40]
variable called no elmtooling install. And that disables the elm install command that you probably
[00:33:47]
have in your post install script, which means that you can run npm install without triggering
[00:33:53]
elmtooling install. And once that's done, you can move on to the next step and run elmtooling install
[00:33:59]
separately. And that's for the optimum caching strategy. If this is super difficult for you to
[00:34:04]
set up, you can still install both in one step, but you won't get the perfect caching anymore.
[00:34:13]
Right. So essentially, it may overfetch these binaries that elmtooling.json manages rather
[00:34:20]
than fetching them from cache? Yeah. For example, your package.json is much more likely to change
[00:34:27]
than your elmtooling.json because you have a lot more stuff in there and npm packages change all
[00:34:32]
the time. And you don't actually need to reinstall all the elmtools just because of some npm package
[00:34:39]
update. So by splitting them, you can allow for installing just the npm packages and keep using
[00:34:46]
the cache for the elmtools. So it's an optimization to get more cache hits, but you're not going to
[00:34:52]
have any sort of incorrect cache fetches where it gets the wrong thing from the cache. You would
[00:34:58]
just have cache misses that could have been cache hits where you didn't have to go redownload it.
[00:35:04]
That's good to know. So okay, so to summarize, because this is a lot to wrap your head around,
[00:35:09]
but essentially, first of all, you have an example GitHub action script, which I found really helpful
[00:35:16]
for setting this up on my projects. And you have some nice comments in there that sort of describe
[00:35:21]
some of the reasoning here. So you can find that in the show notes. You recommend using the npmci
[00:35:27]
command, which is a slightly different version of npm install. Do you want to talk a little bit
[00:35:33]
about why you recommend using that instead and how it differs? It's basically because npm themselves
[00:35:39]
are recommending it. It's called npmci because it's optimized for CI. And npm install is a bit of a
[00:35:47]
mixed bag command. It looks at your package.json and sees if anything needs updating. And it might
[00:35:57]
update your package.lock.json. But npmci, it says, I'm just going to read package.lock.json
[00:36:03]
and install everything that's in there. And if there's anything wrong, I'm just going to error
[00:36:07]
out. So yeah, it avoids unnecessary work. And it's a little bit faster. Which the sort of dependency
[00:36:15]
version resolution is one of the most expensive parts of an npm install. So it completely bypasses
[00:36:21]
that and just looks for what's in the package.lock.json. So that's a performance boost in itself.
[00:36:29]
And then you pair that with being able to use the cache key of the package.lock.json. And if you do
[00:36:36]
that, whether it's with GitHub actions or whatever else you do, you specify that your package.lock
[00:36:42]
is the cache key. And if the package.lock changes, then you use a fresh cache. And that means you're
[00:36:49]
going to have to run a fresh npmci. It's going to do all the installs. But otherwise, you don't have
[00:36:55]
to pay that cost. And you can fetch it from the cache. So that's a super performant way of doing
[00:36:59]
that. So OK, so first step, you do npmci. And as npm recommends, you use a cache key of your
[00:37:07]
package.lock.json. So you don't need to do a fresh npmci install. You don't need to run that command.
[00:37:13]
Or you don't need to fetch those binaries or executables at all if it's a cache hit. You
[00:37:19]
bypass running the elmtoolingcli postinstall step when you're running that step in your CI.
[00:37:26]
And then as a separate step, you run elmtooling install explicitly. And you run that with a cache
[00:37:33]
key of your elm.json and your elmtooling.json files. There's a lot going on there. But I mean,
[00:37:42]
essentially, you're just separately caching and executing npmci and elmtooling install to make
[00:37:50]
sure that you optimize the cache hits. And there's documentation about this on the website. And the
[00:37:56]
example GitHub actions workflow, like you mentioned. So it might sound very complicated, but you can
[00:38:02]
look at the examples and remember that we have the word cache in this discussion, which of course
[00:38:08]
makes it much more difficult to reason about. Great point. And then the sort of last point on
[00:38:15]
running elmtooling in your build step is if you, again, look in this example GitHub workflow script,
[00:38:23]
you can see that it's just running elm make with npx. And then you use dash dash no install to
[00:38:30]
make sure it doesn't do any of that funny business. And then you just run these regular commands,
[00:38:35]
elm make, elm test, elm review. You're running all of those with npx. Elm review is not currently
[00:38:40]
managed by elmtooling. But in general, you're just running everything through npx, which could also
[00:38:47]
be an npm script. You could have a script for each of those things in your package.json. And then it
[00:38:54]
would have the same effect where it's executing those with the right path that includes all of
[00:39:00]
the executables from node module slash dot bin. Yeah, exactly. The key to success is to use either
[00:39:06]
npx or npm run. Then you get the path set up for you. It's just that I like using npx if possible,
[00:39:14]
because then you have less indirection. Instead of having to look at your GitHub actions and see that
[00:39:19]
it runs npm run lint and then go to your package.json and see that lint runs elm format,
[00:39:26]
you can just see that it runs elm format directly. And also as a bonus, you don't get these super
[00:39:32]
long npm messages at the end. If you've ever noticed that when a command fails, you don't just
[00:39:40]
get the output from the command, you also get like five or 10 lines of information from npm.
[00:39:47]
Yeah, I actually prefer to have npm scripts, because that way I can run the same test locally
[00:39:54]
and in the CI. And to remove the noise you were just talking about, you can run
[00:40:01]
npm run dash s or dash dash silent, and that removes all of those. Yeah, that's a great trick.
[00:40:08]
Yeah, those 10 lines of like, this is not an issue with npm, please look at this,
[00:40:13]
please don't blame us and file issues with us. I swear it's not my fault. It looks like you.
[00:40:19]
Yeah, npm run dash s dash dash silent is a very good trick that you're in shared with me. Yeah,
[00:40:28]
I think that I use npx quite a lot as well, but I also do kind of like using the script
[00:40:34]
section in a package.json to manage sort of the knowledge of how do I build this, how do I start
[00:40:42]
up a server. I really like having like for all of my projects, I like to be able to run npm test,
[00:40:50]
which is sort of weird because for start and test, and there may be a couple of others,
[00:40:56]
you don't have to do npm run script name, you can do npm test or npm start. But piggybacking on those
[00:41:02]
conventions, I really like to have a script in any project. And I'm talking about Elm projects here
[00:41:08]
for start and for test. So I always run my test suite and I want it to be, you know,
[00:41:15]
this is what my CI is going to run. So if I run npm test, then I want my CI to succeed. And if I
[00:41:23]
run npm start, I want a dev server. And if I run npm run build, then I want my production build
[00:41:31]
step. That's just the conventions I follow. But I also use NPX heavily, both locally and sometimes
[00:41:38]
in my CI and it's very useful as well. I usually duplicate everything that the GitHub actions
[00:41:45]
workflow runs into my npm test, which feels so so because there's a risk they'll run out of sync.
[00:41:52]
But my dream setup would be to have a tool that runs the GitHub workflow locally. So yes,
[00:41:59]
just say execute whatever GitHub actions would do, but locally and quickly.
[00:42:05]
Would you like it to do the caching too?
[00:42:07]
No, I would like to skip over things like choosing the operating system, caching,
[00:42:15]
stuff like that, installing things, just run like my own things like linting tests and building.
[00:42:22]
You sure don't want it to install Windows for you real quick?
[00:42:25]
That's useful.
[00:42:27]
Yeah, I mean, I think, you know, the common thread, the common thread in all the things
[00:42:31]
we're discussing here is sort of having a declarative environment agnostic way to run
[00:42:39]
scripts. Like I want, you know, my production build to end up the same, no matter where or how
[00:42:47]
I run it. I want like one way to run it in different environments and I'll get the same thing. And I
[00:42:53]
want one way to run my tests and I'll get the same result.
[00:42:56]
I think that some people will tell you that's what make files are for.
[00:42:59]
They don't work on Windows.
[00:43:01]
And to that I have nothing to say. They don't really.
[00:43:07]
I might be misremembering. It's been a long time since I actually developed on Windows. But back
[00:43:12]
then it was so annoying when some NPM product used make file because I couldn't use it. So
[00:43:18]
for Elm tooling, I've really gone out of the way to make sure that everything works perfectly on
[00:43:24]
Windows as well.
[00:43:25]
From the community, thank you.
[00:43:28]
Yeah, yeah. It's annoying to do.
[00:43:30]
And yeah, it is very interesting to be, you know, be in this position, you know, as a community
[00:43:37]
where we have these lovely tools that we all love. You know, we I mean, Elm format, Elm review,
[00:43:45]
Elm compiler, there are so many awesome tools and we live in this happy little bubble of the pure
[00:43:51]
Elm space. And some of some of us venture outside of that bubble to build really cool things to
[00:43:56]
make it more awesome working in that bubble. You know, we build stuff in JavaScript and in
[00:44:00]
these ecosystems. But we leverage the JavaScript ecosystem so much. And I'm very grateful to have
[00:44:07]
all these resources because I mean, imagine if Elm had to figure out the story for how do you
[00:44:14]
install binaries? We've got a huge thriving community. You know, it may have some sort of
[00:44:20]
design choices that aren't in line with sort of the style that that, you know, as sort of pure
[00:44:27]
functional aficionados we might we might want, right? We might want we might be more more our
[00:44:33]
values might align better with tools like Nix that, you know, give us declarative pure builds
[00:44:39]
and stuff. But the fact is, we've got this huge vast ecosystem. It's well documented, it's well
[00:44:45]
supported, it's very feature rich. It's got, you know, all these executables, you want to run
[00:44:50]
multiple scripts in parallel and fail on the first failure or continue on the first failure,
[00:44:55]
whatever, there's some like cross script NPM package for that. So it's it's definitely a
[00:45:00]
love hate relationship where the design decisions sometimes aren't aligned with with what we would
[00:45:07]
want in a very like declarative pure world. But there are so many great tools out there at our
[00:45:13]
disposal. Yeah, definitely. So I was wondering, why do you not look at the version in the Elm
[00:45:19]
JSON file? So when you so to install Elm, the applications they have Elm version 0.19.1. Why
[00:45:29]
don't you use that one? I know it's not available for packages. Is that the reason? Yeah, I've been
[00:45:33]
thinking about that a lot. It's like one of those all details and it's so hard to make up your mind
[00:45:40]
what way do you want it to be. But ultimately, because of that, it wouldn't work for packages,
[00:45:46]
like you said, because in a package Elm.JSON, you just you don't say Elm version 0.19.1, you say
[00:45:53]
0.19.1 up to but not including 0.20.0. So you get a range, which means that the tool wouldn't know
[00:46:02]
which version you want. And so I decided let's put all of the versions in one place in Elm tooling
[00:46:09]
dot JSON. And you will always find them there. It means a tiny bit of duplication, but I think it's
[00:46:14]
fine because the Elm versions don't change very often. No, for better or worse, they don't change
[00:46:20]
often. All right, so there are other fields in that Elm tooling dot JSON than tools. Yeah,
[00:46:28]
exactly. Which are they? So far, it's just one other field. And it's called entry points. And
[00:46:35]
this is used by the Elm language server, which powers the VS code extension and the vim extension.
[00:46:42]
And what the language server uses it for is to find which files to compile to compile your entire
[00:46:49]
project. And that is your entry points. So sometimes you just have one entry point, maybe
[00:46:55]
in main dot Elm. And if you compile that, then it will import other files which imports further
[00:47:01]
files and so on until you have gone through every file in your whole project. But if you have another
[00:47:07]
entry point, maybe you have like two apps or two separately built pages or something, then you
[00:47:13]
would actually need to compile both those entry points to cover all of your files. So this way,
[00:47:18]
you can specify all of your entry points. So tools can know how to find errors in your entire project.
[00:47:25]
And other tools than the language server are free to use this field, which is kind of the point with
[00:47:32]
this Elm tooling dot JSON. It's supposed to be a shared place to put all of your configuration,
[00:47:38]
both to like keep it all in one place. And so that tools can collaborate on reusing the same
[00:47:45]
configurations. You don't have to specify it several times. So if there's any tool that wants
[00:47:52]
to put something in the Elm tooling dot JSON, just open a discussion and we will try to figure out
[00:47:59]
what we need and what it should look like. Yeah, I really like that entry points field and that
[00:48:05]
general philosophy of having like a common, you know, specification and place to store this
[00:48:12]
project metadata that tools can share. I find myself pretty often, I mean, we may all be sort
[00:48:19]
of unique here in that we're very focused on building tools. So our use cases may be different
[00:48:25]
than the standard use case. But I do find myself frequently having a lot of entry points for,
[00:48:31]
you know, my examples folder in an Elm package and, you know, some test case. And it's really
[00:48:39]
a pain right now and a little bit scary in case I might miss compiling something. I might have an
[00:48:47]
example that isn't compiling and publish a version of a package or push a commit and not know that
[00:48:53]
it's broken. So I try really hard to write scripts in my GitHub actions CI where I, you know, compile
[00:49:00]
every entry point. But I would love to have like a more declarative way that tools can share. So
[00:49:05]
instead of it just being, I mean, I think Simon, this is sort of the sort of vision that you were
[00:49:10]
getting at that wouldn't it be nice if there was a declarative what you were talking about? Wouldn't
[00:49:15]
it be nice if we could just run our GitHub actions so we could share everything. But the more
[00:49:19]
declarative you can be about things, the more simple you can be. So if you just declare, these
[00:49:24]
are my entry points, then tools, you don't, it's more imperative to say, okay, go into this
[00:49:29]
directory, come run this command to compile this module, then go into this directory entry points
[00:49:35]
is declarative. It's just saying these are my entry points. And if a tool says, Oh, I'll look at your
[00:49:39]
entry points and make sure they all compile, it can figure out how it wants to actually execute that.
[00:49:44]
So you can, you can share something among tooling, the more declarative it is, the easier it is to
[00:49:50]
share between tools because imperative stuff, it's hard to get to the bottom of what is it actually
[00:49:56]
trying to do. There are all these low level details of how it does it. It's hard for tools to share
[00:50:01]
that because it's not like a simple specification that different tools can leverage. So, so I really
[00:50:06]
like that concept. Yeah. I might actually use it for Elm review because Elm review doesn't know
[00:50:12]
which files you're going to compile. So that could be interesting. I'll have to think about use cases
[00:50:16]
for that first, but that's definitely interesting. And then there's another, another thing you've
[00:50:23]
added to the Elm tooling CLI project, which is there's so as we've discussed, it is an NPM package,
[00:50:31]
which gives you an executable called Elm tooling, but it's also a node package and you can
[00:50:37]
programmatically as a tooling author, you can programmatically invoke Elm tooling to execute
[00:50:43]
binaries that are installed with Elm tooling. You want to tell us like a little bit about that?
[00:50:48]
Yeah, sure. So both Elm test and Elm review use Elm JSON when they compile things to figure out
[00:50:58]
how the dependencies work for your project. That's pretty complicated to calculate. So they use this
[00:51:05]
tool called Elm JSON, which you might've used yourself when you update your Elm.json file.
[00:51:10]
But that means that that Elm JSON tool needs to be available, which is a REST executable that you
[00:51:16]
can install with NPM install. Exactly. So in order to not put the burden on the user to install
[00:51:25]
Elm JSON themselves, what the Elm review does is that it depends on the Elm JSON NPM package.
[00:51:32]
And my goal has been kind of to get rid of all these Elm binary NPM packages,
[00:51:39]
but I still want to use Elm review. Thank you.
[00:51:42]
So it annoys me that it depends on Elm JSON, the NPM package, because now I get all of the
[00:51:47]
dependencies and slower installation time whenever I install Elm review. And this is why I made this
[00:51:53]
API in Elm tooling. So Elm review can import Elm tooling slash get executable, which is a function
[00:52:01]
and you say to the function, what tool you want Elm JSON in this case, and which version you want.
[00:52:07]
And then you can say, I want correct sign 0.2.8. And the correct sign is just like in NPM,
[00:52:15]
it gives you a semver range and the Elm tooling get executable function will then try to find
[00:52:22]
a matching version, see if it's installed. If it isn't, it downloads the binary and then it returns
[00:52:27]
the absolute path to this tool, which Elm review then can execute. Yeah, that's it.
[00:52:34]
A way for NPM packages to use Elm tooling to depend on some Elm binary.
[00:52:40]
If the user had happened to install that in their local dev machine and they're running
[00:52:46]
Elm review, which is using this Elm tooling API, then it would not need to install that as with
[00:52:52]
any other Elm tooling install, or if it needed to, it could install that.
[00:52:56]
Exactly. So if the user themselves has a Elm tool, they can use that Elm tool.
[00:53:03]
If the user themselves has installed Elm JSON using Elm tooling before, get executable does not need to do much really.
[00:53:10]
Or if you've run Elm review once and it installed Elm JSON then, you probably will never need to
[00:53:17]
download Elm JSON anymore because it's already in that shared.elm home folder.
[00:53:23]
Oh, that's wonderful. So I have run into a similar use case in Elm GraphQL.
[00:53:28]
The command line tool for Elm GraphQL, which runs the code generation,
[00:53:31]
it would be very difficult. I tried to get it perfectly formatted without Elm format,
[00:53:36]
not to mention the fact that people might use different versions of Elm format for their project,
[00:53:41]
which I can't target arbitrary versions in the code generation tool.
[00:53:46]
At some point, it just became clear that I need to just run Elm format on the generated code rather
[00:53:51]
than trying to get the perfect output that will succeed on people's Elm format steps or look the
[00:53:56]
way they want it when they're browsing the code. And so now would that use case apply to the Elm
[00:54:02]
tooling node API? Would I be able to say, give me whatever Elm format version the user has?
[00:54:10]
I mean, I guess if I needed to, I could read their elmtooling.json and look for that or otherwise
[00:54:15]
fall back to a specific version. I think that like your goal here is that generated code is
[00:54:21]
supposed to be readable, right? Because you sometimes go into it to find out what the types
[00:54:27]
look like and stuff like that. So in an ideal world, you should be able to use whatever Elm
[00:54:33]
format version you want. But I guess that it's good if you use the same version as the user
[00:54:41]
themselves do, just in case they run Elm format on all of their files, included your generated files,
[00:54:48]
then you wouldn't want Elm format to say like, up, up, up, you need to format all of these files,
[00:54:55]
according to my style. So it could be beneficial to use the same version as the user. So they don't
[00:55:01]
need to like ignore those files. And that's not what this API is for. So then you should try to
[00:55:08]
invoke the user's Elm format binary. And I think this is what Elm review does, right?
[00:55:15]
Uh, yeah, yeah, we try to get the format and the Elm compiler that the user chose.
[00:55:22]
And the way that works, I think is that Elm review or Elm GraphQL in your case,
[00:55:28]
it just assumes that Elm format is on your path and tries to execute that. And if you have run
[00:55:34]
Elm GraphQL or Elm review with MPX, then Elm format will be on your path because MPX adds
[00:55:40]
the local node module stop in to your path. And as a fallback, it's good to have a flag that says
[00:55:47]
like dash dash Elm format path or something. So you can pass in your own as a last resort.
[00:55:54]
Yeah. Elm review tries both with MPX and without MPX and hopefully it will be there. Not a lot of
[00:56:01]
people have complained, so I'm guessing that works well. Interesting. In Elm GraphQL, I run Elm
[00:56:06]
format just with MPX and the, you know, for a short time I got like one person saying, hey,
[00:56:13]
it doesn't work. And I said, Hey, you have to install at least NPM version 5.7.2 or something,
[00:56:20]
because that's when MPX was introduced. But otherwise that hasn't been problematic, but it,
[00:56:25]
it's, it's good to discuss these things and these, these sort of best practices, because I think that
[00:56:31]
both for tooling authors like ourselves and for tooling consumers, I think it's good to sort of
[00:56:36]
understand a little bit of the magic behind how these things work. So if something doesn't seem
[00:56:40]
to be working correctly, you can say, Oh, maybe I should be running NPX and things would magically
[00:56:46]
be on the path using the versions that I've specified in my Elm tooling dot json or my
[00:56:52]
package dot json. So it's good to sort of look at how the sausage is made a little bit,
[00:56:57]
even though it's messy and there are so many best practices and so many things to think about.
[00:57:01]
So I think a lot of people wonder why is there Elm and Elm formats, but not Elm tests and Elm
[00:57:08]
review as we already mentioned. Yeah. You mean like why Elm tooling does not support installing
[00:57:14]
Elm tests and Elm review. Yeah. Elm tooling only supports tools that compile to a single executable
[00:57:21]
that are cross, no, there aren't cross platform while Elm test and Elm review are Node.js packages
[00:57:31]
and they depend on other NPM packages. And like, there's no way to beat NPM or YARN for installing
[00:57:38]
Node.js tools. So I think it's important to use the right tool for the right job. So it's much
[00:57:44]
better to install Node.js based tools using NPM and let the Elm tooling deal with the stuff that
[00:57:52]
NPM is bad at, which is installing different binaries for different operating systems.
[00:57:58]
Right. That's a great point. That's the specific problem that it's solving is there's a single
[00:58:03]
binary that Elm, the Elm Haskell code base compiles to a single binary for different
[00:58:09]
platforms. There's a Mac 64 bit binary, there's a Linux binary and Elm tooling manages fetching
[00:58:17]
the correct version of that single binary, which is not a bunch of JavaScript NPM packages. It's
[00:58:23]
a single binary and it manages fetching it for the correct platform and that's the specific task
[00:58:29]
it does. So, okay. So I think is there anything else that people should know to understand what
[00:58:37]
the heck is going on when they're installing things to have best practices? Are there any
[00:58:43]
tips or concepts that are important that will help people sort of master their tooling setup better?
[00:58:51]
Not sure if there's much more to say there. Maybe that if you already use NPM to install
[00:58:58]
an Elm format, you should be able to just switch to Elm tooling and get all the benefits without
[00:59:05]
much work. Right. So, okay. So let's kind of talk a little bit about how someone would get started
[00:59:12]
and just summarize. I think we've sort of covered it, but if somebody wants to start using Elm
[00:59:17]
tooling CLI right now, what are their first steps? What are some resources that are going to help
[00:59:22]
them? What are some things they should keep in mind? Yeah. So there's a getting started section
[00:59:27]
on the website and it mentions like a super quick start if you already feel comfortable with setting
[00:59:34]
things up and just want something quick. And then what you basically do is that you say NPM install
[00:59:40]
dash dash save dev Elm tooling to get a local copy of Elm tooling. And then you run npx Elm tooling
[00:59:47]
init to create an Elm tooling dot json and then npx Elm tooling install to install everything in
[00:59:54]
your Elm tooling dot json. And then you're done. Then you can start using like npx Elm help or
[00:59:59]
npx Elm format to format something so on. And apart from that quick start guide, there are two
[01:00:06]
more detailed sections, one for adding Elm tooling to an existing project and one for making a
[01:00:13]
product from scratch with Elm tooling. And it's basically still the same steps. I've just fleshed
[01:00:19]
out with some more details and like tips that you need to go into your package dot json and remove
[01:00:25]
Elm and Elm format from there if you have installed them with NPM previously. But it should be a pretty
[01:00:32]
streamlined experience to do it. That's the goal at least. Yeah, the documentation is very thorough
[01:00:37]
here. Would you recommend uninstalling all of your global Elm executables, your global Elm
[01:00:46]
binary and your global Elm format binary to ensure that you don't accidentally use the global one by
[01:00:52]
mistake? That's a very good question. So there's nothing saying that you have to do that. It's
[01:00:58]
perfectly fine to have them as they are. But as you say, you can accidentally call your global
[01:01:04]
version by mistake. But on the other hand, right now the Elm ecosystem is so stable. So even if
[01:01:11]
you accidentally call the global version, it's likely going to be the same, the correct version.
[01:01:17]
So yeah, it's unclear if you're used to having a global version and you use that for like most of
[01:01:24]
your projects, then by all means continue doing that if you like. And you can use Elm tooling just
[01:01:29]
for certain projects. Myself, I have global versions of Elm and Elm format, but I've noticed
[01:01:35]
that I've almost never used them. Right. And it's kind of nice to know that you're not by mistake
[01:01:42]
using those, right? Because you want things to fail if you do something unexpected, just like
[01:01:48]
when you run NPX, you don't want it to be like, oh, I didn't find that. Let me start installing
[01:01:53]
this thing for you. And you're like, what? What are you doing? So when I'm thinking about what
[01:01:58]
would you use a global Elm binary for besides running Elm test and it needs to have an Elm
[01:02:04]
version, which we've kind of talked about, if you do NPX Elm test, it will find the version that Elm
[01:02:08]
tooling has installed for you. If you're running a REPL, the thing is a REPL depends on a specific
[01:02:15]
Elm.json file because it's going to, you can't just run an Elm REPL anywhere. It's going to
[01:02:21]
actually look for the Elm package dependencies in your Elm.json. So more and more, I think of
[01:02:27]
an Elm project as an atomic unit that has a package.json and that has an Elm tooling.json.
[01:02:33]
Like to me, that is like an atomic thing that Elm does not exist outside of that. And so I more and
[01:02:39]
more lean towards, I don't want to accidentally fall back to a global binary. I don't want to
[01:02:45]
run NPX and think that it's using a locally installed version, but it's actually reaching
[01:02:50]
out to the global binary. I want the global binary to not exist. So more and more, I'm thinking that's
[01:02:56]
the way to go for me. But yeah, all good tips here. Cool. Any last tips or resources for getting
[01:03:03]
started? This should be it, I think. So if people have problems or questions or want to contribute,
[01:03:11]
where do they go to do that? Yeah, you could always open an issue on the GitHub repo. There's
[01:03:18]
also a Discord channel in Dillon's Discord. And I guess that we could also make a Slack channel
[01:03:26]
eventually. So you can choose whatever you prefer, I'd say. Yeah, Simon jumps on and gives a lot of
[01:03:35]
thoughtful details if you ask him a question. Not to invite people to bombard you with questions,
[01:03:41]
but I've appreciated that. Same here. I want to really thank you, Simon, for coming on the podcast
[01:03:48]
and talking to us about this. It's been very illuminating. And I want to thank you for the
[01:03:53]
thought you've put into this tool. It really shows. It's one of these things where you can see
[01:03:58]
how much work it took to put in all this thought and consider all these options to then go to the
[01:04:04]
simplest possible path, which then looks like, oh, you run it with NPX. So it wasn't much work,
[01:04:09]
right? But you can see all the thought that went into, oh, if you leverage this one thing,
[01:04:15]
all these things fall into place. And it really shows. And discussing it with you,
[01:04:19]
it shows even more. And it was great to sort of pick your brain on that. Yeah, Simon was the one
[01:04:23]
who made the pull request for Elm Review to use Elm tooling. And I had a lot of questions, and
[01:04:30]
he answered all of them flawlessly. And there was no problem at all. I raised so many issues,
[01:04:37]
and he was like, no, that's fine. This is working this way, and it's working great. And well, okay,
[01:04:42]
I just merged it then. Yeah, as a tooling author, as fellow tooling authors, I think we can really
[01:04:51]
appreciate what a feat it is to accomplish to get to a point where it's like, wow, that lines up
[01:04:57]
really nicely. And you know that it's not luck. It's a lot of careful thought and design detail
[01:05:03]
attention. So thank you, Simon. And it's been great chatting with you. Thanks for having me
[01:05:08]
at a great time. Our pleasure. And Jeroen, until next time. Until next time.