vite and deno: an experiment
Tags: javascript, vite, deno
Published: 2021-03-02 03:10:26

---
title: vite and deno: an experiment
published: true
description: vite and deno together because why not?
tags: #javascript #vite #deno
//cover_image: https://direct_url_to_image.jpg
---

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í](https://dev.to/vonheikemen/vite-y-deno-un-experimento-1ejb).

Just hear me out. [vite](https://vitejs.dev/) is a development server (also kind of a build tool) and [deno](https://deno.land/) 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.

```js
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](https://deno.land/manual@v1.7.5/linking_to_external_code/import_maps#import-maps), this would make it a lot better.

> as of version 1.8.0 import-maps in deno are stable. [Release notes](https://deno.land/posts/v1.8#import-maps-are-now-stable).

Anyway, I want this.

```js
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](https://www.npmjs.com) (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.

```sh
npm install ramda@0.27.1
```

Now let's create script. We will call it `main.js`.

```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`.

```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`.

```sh
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.

```js
[ 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.

```sh
export DENO_DIR="$PWD/.cache"
```

> 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.

```sh
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`.

```diff
  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.

```sh
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`.

```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.

```sh
deno bundle "http://localhost:3000/main.js?t=$RANDOM" dist.js
```

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

```sh
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 reading. If you find this article useful and want to support my efforts, [buy me a coffee ☕](https://www.buymeacoffee.com/vonheikemen).

[![buy me a coffee](https://res.cloudinary.com/vonheikemen/image/upload/v1618466522/buy-me-coffee_ah0uzh.png)](https://www.buymeacoffee.com/vonheikemen)