跪拜 Guibai
← All articles
Vite · Vue.js · Frontend Engineering

A Vite Plugin That Renames v-model to v-mortal — and Why That's a Compile-Time Win

By xiaohe0601 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Compile-time directive rewriting shows how teams can adopt internal DSLs or naming conventions without forking Vue or paying a runtime tax. The same AST-plus-MagicString pattern applies to any SFC transformation — linting, injecting defaults, or polyfilling missing props — and keeps the production bundle untouched.

Summary

A developer named his custom directive `v-mortal` after his own English name and built a Vite plugin that rewrites it to `v-model` at compile time. The plugin hooks into the `transform` phase with `enforce: 'pre'`, parses each `.vue` file with `vue/compiler-sfc`, walks the template AST to locate every `v-mortal` directive, and overwrites just the directive name using MagicString. Because the substitution happens before Vue's own compiler sees the code, there is no runtime overhead and no need to teach Vue about a new directive.

A companion TypeScript declaration file (`v-mortal.d.ts`) augments Vue's `GlobalDirectives` interface so that IDEs offer autocomplete for `v-mortal` exactly as they do for `v-model`. The whole setup is a compact demonstration of moving syntax sugar into the build toolchain rather than into the framework runtime.

The author explicitly warns against using this in production — it's a teaching example — but the underlying principle generalizes: push complexity into the compiler and toolchain to keep the runtime lean.

Takeaways
A Vite plugin with `enforce: 'pre'` runs before Vue's compiler, letting it rewrite template code that Vue never sees in its original form.
Parsing a `.vue` file with `vue/compiler-sfc`'s `parse()` and walking `template.ast` finds directives precisely, avoiding regex edge cases and false matches inside strings or comments.
MagicString overwrites only the targeted byte range (`v-mortal` → `v-model`) and preserves source maps, so downstream tooling stays accurate.
Augmenting `vue`'s `GlobalDirectives` interface in a `.d.ts` file gives the custom directive full IDE IntelliSense without any runtime registration.
The entire substitution carries zero runtime overhead because the transformed code is plain `v-model` by the time Vue processes it.
Conclusions

The technique decouples developer-facing syntax from framework-facing syntax — a team could use `v-myprop` everywhere and compile it to standard Vue directives, keeping code familiar without framework changes.

AST-based rewriting inside Vite's `transform` hook is underused for SFC conventions; this pattern works for any deterministic template transform (default props, accessibility attributes, lint auto-fixes) and leaves the runtime untouched.

The TypeScript declaration trick is the quiet enabler here: without it, the custom directive would compile correctly but feel broken in the editor, which is where most developers would reject it.

Concepts & terms
Vite `enforce: 'pre'`
A plugin ordering flag that forces a Vite plugin to run before other plugins (including the framework's own compiler), giving it first access to raw source code.
MagicString
A lightweight JavaScript library by Rich Harris for performing surgical string replacements while generating accurate source maps, commonly used in build-tool transforms.
vue/compiler-sfc parse()
Vue's official SFC parser that splits a `.vue` file into its `<template>`, `<script>`, and `<style>` blocks and produces an AST for each, enabling programmatic analysis and transformation.
GlobalDirectives type augmentation
A TypeScript module augmentation that extends Vue's `GlobalDirectives` interface so that custom directives (even compile-time-only ones) appear in IDE autocomplete as if they were built-in.
From the discussion

The discussion splits between playful adoption of v-mortal as an AI-proofing tactic and a blunt question about its practical value. One side treats the rename as a clever compile-time trick that preserves tooling while confusing AI scrapers; the other asks why not just write v-model if the output is identical. A separate thread pokes fun at the tutorial format, and a final comment dismisses the endless churn of new tools altogether.

Using v-mortal instead of v-model can serve as a low-effort obfuscation against AI training on codebases, since the directive name is meaningless to models but resolved at build time.
The compile-time swap raises a direct objection: if the final output is standard v-model, the rename adds an unnecessary abstraction layer with no functional benefit.
The article's tutorial style draws light mockery — one reply calls it "teaching your grandmother to suck eggs," suggesting the content feels basic or showy to some readers.
A broader frustration surfaces that the frontend ecosystem's constant stream of new tools and plugins may be pointless busywork rather than genuine progress.
Featured comments
会爬树的金鱼 1 likes

This is great — I'll use v-mortal from now on, so AI can't replace me.

xiaohe0601

Damn, why didn't you say so earlier [mischievous grin]

striveLei 1 likes

Since v-mortal gets swapped to v-model at build time, why not just use v-model directly?

xiaohe0601

Good question [looking]

要问就是倪大烨

Learn a new tool today, learn another new tool tomorrow — what the hell is the point of learning all this crap?

See top comments, translated →
Source: juejin.cn ↗ Google Translate ↗ Backup ↗