Update 2022-09: Deno is going to add first class support for npm packages. Feel free to ignore this article.
Pueden leer la versión español aquí.
Just hear me out. vite is a development server (also kind of a build tool) and deno is a javascript runtime that wants to act like a browser. Come on. It's right there.
Quick recap
Let's take a quick look at these tools, for the sake of completeness.
vite
In the official website it is described as "Next Generation Frontend Tooling." That's because it does quite a few things. It has a development server that relies heavily on ES modules to offer fast reloads and startup times. One of the things that make it fast (like insanely fast) is the fact that it only deals with one file at a time, and on demand, instead of bundling your entire project. By default it can handle typescript, jsx and css modules, so you can start doing stuff right away. The other thing, if you want to build your app for production it can also do that.
deno
deno
is a secure runtime for JavaScript and TypeScript. In other words, it can execute javascript and typescript without a browser. It says it's secure because the code you execute runs in an environment with restricted access to your system. If you want to enable certain features you need to give it explicit access. The other thing that makes it interesting is the fact that it comes bundled with some useful tools like a bundler, a formatter, a linter, a language server, and some others. This thing is a development environment.
Why would I want to mix those two?
Because of the way deno
handles third-party dependencies. You see, when you want to use a package you have to do it in the same way you would in a browser, by using ES modules and a url (nudge nudge wink wink). Something like this.
import * as R from 'https://cdn.skypack.dev/ramda@0.27.1';
This is completely fine... until is not.
For single file scripts is perfect. For more complex projects there is a convention of putting everything in a deps.ts
file, it's not the best, but it's ok. There is also an experimental feature called import-maps, this would make it a lot better.
as of version 1.8.0 import-maps in deno are stable. Release notes.
Anyway, I want this.
import * as R from 'ramda';
And I want to get that thing, ramda
, using a "real" package manager. At the moment it would mean using npm (in deno land this is a sin). Problem is, deno doesn't like npm
.
vite to the rescue
Let's say we want to use ramda
. Again, we want to use npm
to grab the source, so we do this.
npm install ramda@0.27.1
Now let's create script. We will call it main.js
.
import * as R from 'ramda';
const increment = R.map(x => x + 1);
console.log(increment([1, 2, 3]));
Isn't that nice? That's what we want to work with. Now its time install vite
.
npm install -D vite@2.0.4
Let's make a test drive. Create an index.html
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="module" src="/main.js"></script>
</body>
</html>
Now use vite
.
npx vite
If everything went well you should have this on your screen.
vite v2.0.4 dev server running at:
> Local: http://localhost:3000/
> Network: http://192.168.0.000:3000/
ready in 347ms.
Visit http://localhost:3000/
and check the browser's console, you should have this.
[ 2, 3, 4 ]
Cool. Great. Now how do we get this to deno
? I did mention deno
wants to act like a browser. You know what browsers do? They pull stuff from a url.
Just hold it one second. Before you do anything, in case you don't want to "pollute" the global cache deno
uses in your system I suggest setting the DENO_DIR
environment variable. In unix shells you would do something like this.
export DENO_DIR="$PWD/.cache"
windows users, I apologize, I do not know the
cmd.exe
orpowershell
equivalent of this.
Where were we? Using deno
. This is what we do, instead of running main.js
on the filesystem we run the main.js
vite
is serving.
deno run "http://localhost:3000/main.js"
deno
should show you this.
Download http://localhost:3000/main.js
Download http://localhost:3000/node_modules/.vite/ramda.js?v=2e8a2ea4
[ 2, 3, 4 ]
It works! We have successfully used an npm package with deno
. This is a big deal. But don't celebrate to much just yet. Just for fun, run it again.
[ 2, 3, 4 ]
It should show you just that. No "download http://...". It's all cool. Now change something in main.js
.
import * as R from 'ramda';
const increment = R.map(x => x + 1);
-
- console.log(increment([1, 2, 3]));
+ console.log('hello');
Run main.js
again.
Did you get the hello
? I bet you didn't, and now you wonder why.
Because deno
is grabbing main.js
from a server (localhost) it saves the source in the cache folder (DENO_DIR) and it won't try to download it again unless the url changes. How do we work around this? I could only come up with this.
deno run "http://localhost:3000/main.js?t=$RANDOM"
In here I'm using a querystring t
to attach a random number to the url, this technically creates a "new" url everytime you execute the command.
This is the same approach vite
uses to break the cache. Let's pretend we have a file called other.js
and we use it in main.js
.
import { other } from './other.js';
When we change the content of other.js
vite
will attach a timestamp to the url of the file. So when you have a change deno
will show something like this.
Download http://localhost:3000/other.js?t=1614653342379
And there you have it, a development environment with vite
and deno
.
What about after?
I know, you would want to use this "app" you're developing without vite
. The solution is fairly simple. We use the command deno bundle
, this will grab everything deno
needs and it will put it in a single file.
deno bundle "http://localhost:3000/main.js?t=$RANDOM" dist.js
Now if you run dist.js
you should get the expected result.
deno run dist.js
A word of caution
Just in case this needs to be said. Even thought we can download anything we want from npm
does not mean it will work on deno
. Sometimes a package it's just not compatible.
Conclusion
This silly experiment actually works, at least the trivial examples I tried. I don't encourage the use of this combo to develop mission critical apps, just don't. Side projects and others experiments are totally fine.
Thank you for your time. If you find this article useful and want to support my efforts, consider leaving a tip in ko-fi.com/vonheikemen.
Top comments (4)
Holy crap, this is cool! I'm currently toying with deno (with scripts and stuff), and I didn't know that you can use it like this.
I feel like we've all only scratched the surface of this "browser for your terminal" thing in deno.
I'm glad you liked it.
Indeed. That's why I still keep track of deno's development.
Try out the
--reload
flag :-)Funny that you mention that. When I was trying this experiment with snowpack the only way I could get it working was with that flag.
I figure that the output could get rather crowded if you have too many dependencies and those in turn have a lot more. So, at some point you'll have to add the
--quiet
flag too.