DEV Community

Dmitry Titov
Dmitry Titov

Posted on

My First Vite Plugin: vite-plugin-module-alias

A small tool I built to automate alias config in Vite. Also, my first dev post — feedback is welcome!

🧠 Background

While working on a Telegram bot using a Web App, I chose a familiar stack: Vite + React + Zustand + TypeScript.

Coming from a Webpack background, I found Vite’s resolve.alias handy. But there was a catch: to make TypeScript and my IDE understand the aliases, I also had to configure tsconfig.json. And that wasn’t all — HTML imports didn’t recognize aliases either.

💡 Why Do We Need Aliases in HTML?

Using path aliases in JavaScript/TypeScript (like @components/Button instead of ../../components/Button) makes your code:

  • More readable
  • Less fragile to file structure changes
  • Easier to maintain

In Vite, even if you configure aliases in vite.config.ts and tsconfig.json, these aliases don’t work in index.html — for example, in <script type="module"> tags — because the browser doesn’t know anything about your bundler’s internal setup.

That’s where import maps come in. They allow you to declare aliases directly in HTML so the browser can resolve them correctly:

<script type="importmap">
{
  "imports": {
    "@utils/": "/src/utils/"
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

This way, your aliases work not only in your source code but also in HTML — no hacks or fragile absolute paths.

🔁 Repeating Myself, Again and Again…

Every time I spun up a new Vite project, I found myself writing the same boilerplate alias configs.

So I thought: why not automate it?

That’s how the idea for a plugin was born — simple, reusable, and driven by a single source of truth.

🗂️ Why an import-map.json?

I wanted something dead simple:

  • Easy to replicate across projects
  • No need to configure aliases in 3 different files
  • Just drop in one config file and you’re good to go

I started with a minimal setup that read from import-map.json, applied aliases via resolve.alias, and injected the <script type="importmap"> tag into HTML — making aliases work even in the browser.

🤔 Why Not Use Existing Plugins?

vite-tsconfig-paths is a popular plugin that automatically picks up aliases from your tsconfig.json.

At first glance, it sounds perfect — but there are a few limitations:

  • ❌ It does not support aliases in HTML — so your index.html scripts still break unless you manually fix the paths.
  • ❌ It depends on tsconfig.json as the single source of truth:
    • Breaks the idea of centralized config if you also need vite.config.ts and import maps
    • Limits flexibility, especially in template projects or shared configurations
  • ❌ There’s no built-in way to auto-update tsconfig.json when your aliases change — you still have to maintain it manually.

What I really wanted was:

  • a single config file (like import-map.json),
  • consistent aliasing in both dev and production,
  • HTML support out of the box,
  • and zero setup.

That’s exactly what I built with vite-plugin-module-alias.

🚀 Publishing a Plugin for the First Time

I’d never published a plugin to npm before. Honestly, I didn’t think anyone would care.

But this time, I thought — what if someone finds it useful?

So I cleaned up the code, wrote a quick README, and pushed it to npm.

To my surprise, within a day or two I saw 80+ downloads.

Cool! …Except it had two critical bugs 🙃

🧯 Lesson Learned: Don’t Ship at Night

I made a rookie mistake — released the plugin in the evening with the classic “I’ll fix the rest tomorrow” mindset.

Turns out, “tomorrow” was too late.

Spent 8–10 hours cleaning it up, fixing bugs, improving error messages and docs.

Pro tip: Don’t publish unless it’s rock solid.

🔄 Second Iteration: tsconfig.json Auto-Sync

The next logical step was syncing aliases to tsconfig.json automatically.

I experimented with Vite’s server.restart() to reload the dev server, but it wasn’t reliable. So I added two modes:

  • Auto – plugin attempts to restart the server
  • Manual – shows a warning to restart it yourself

I also had to deal with relative vs absolute paths, JSON formatting, and edge cases.
Big thanks to my small group of early testers (you know who you are!).

🧩 My First GitHub Issue 🎉

One day I got a GitHub notification — my first issue!

It felt like a milestone.

Luckily, it was a small bug and only took 10 minutes to fix. But seeing someone using the thing — and caring enough to give feedback — was amazing.

🧱 The Unresolved Bit: JSON Comments

One challenge remains: preserving comments in tsconfig.json.

Standard JSON parsers strip them, and I haven’t found a reliable workaround yet. I’m still experimenting with different solutions.

If you know of a solid fix — PRs welcome 😉

🔭 What’s Next?

I’m planning to:

  • Improve documentation
  • Add better error handling and validation
  • Maybe support YAML configs?

Links

🔗 GitHub: vite-plugin-module-alias
📦 npm: vite-plugin-module-alias

Top comments (0)