DEV Community

Heiker
Heiker

Posted on • Updated on

vite and deno: an experiment

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';
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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]));
Enter fullscreen mode Exit fullscreen mode

Isn't that nice? That's what we want to work with. Now its time install vite.

npm install -D vite@2.0.4
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Now use vite.

npx vite
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

Visit http://localhost:3000/ and check the browser's console, you should have this.

[ 2, 3, 4 ]
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

windows users, I apologize, I do not know the cmd.exe or powershell 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"
Enter fullscreen mode Exit fullscreen mode

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 ]
Enter fullscreen mode Exit fullscreen mode

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 ]
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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 viteuses 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';
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Now if you run dist.js you should get the expected result.

deno run dist.js
Enter fullscreen mode Exit fullscreen mode

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 reading. If you find this article useful and want to support my efforts, buy me a coffee ☕.

buy me a coffee

Top comments (4)

Collapse
 
vinliao profile image
Vincent Liao

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.

Collapse
 
vonheikemen profile image
Heiker

I'm glad you liked it.

I feel like we've all only scratched the surface of this "browser for your terminal" thing in deno.

Indeed. That's why I still keep track of deno's development.

Collapse
 
lucacasonato profile image
Luca Casonato

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.

Try out the --reload flag :-)

Collapse
 
vonheikemen profile image
Heiker • Edited

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.