Plugin API
vitePluginEmber(options?)
The main Vite plugin. Import and add it to your Vite config.
import vitePluginEmber from 'vite-plugin-ember';Options
| Option | Type | Default | Description |
|---|---|---|---|
compilerPath | string | 'ember-source/dist/ember-template-compiler.js' | Path to the Ember template compiler module. Override this only if you need a custom or pre-release compiler. |
resolve | Record<string, string> | {} | Custom import resolution map. Keys are bare specifiers, values are resolved paths or package names. |
babelPlugins | any[] | [] | Additional Babel plugins appended after the built-in template compilation and decorator transforms. |
resolve option
Use resolve to map custom import specifiers to file paths or package names. This is useful for third-party Ember addons, local utility modules, or any import that Vite can't resolve automatically.
vitePluginEmber({
resolve: {
// Map a custom specifier to a local file
'my-helpers': './src/helpers/index.js',
// Map to an installed package
'tracked-built-ins': 'tracked-built-ins',
},
});With this config, your component code can import from these specifiers:
```gjs live
import { TrackedArray } from 'tracked-built-ins';
// ...
```babelPlugins option
Some Ember addons ship custom Babel plugins that transform specific syntax (e.g., ember-concurrency's async arrow tasks). Add them here:
vitePluginEmber({
babelPlugins: ['ember-concurrency/async-arrow-task-transform'],
});These plugins run after the built-in template compilation and decorator transforms.
What the plugin does
Template compilation — Preprocesses
<template>tags viacontent-tag, then compiles them to wire format usingbabel-plugin-ember-template-compilation.Decorator transforms — Converts
@trackedand other decorators usingdecorator-transforms.TypeScript — Strips type annotations from
.gtsfiles via@babel/plugin-transform-typescript.Module resolution — Resolves
@ember/*and@glimmer/*bare specifiers to files insideember-source/dist/packages/.@embroider/macrosshim — Provides runtime implementations forisDevelopingApp(),macroCondition(), and other compile-time macros thatember-source's ESM modules import.Virtual module serving — Handles
virtual:ember-demo-*modules generated by the markdown fence plugin.
emberFence(md, component?)
A markdown-it plugin that transforms code fences into live component mounts.
import { emberFence } from 'vite-plugin-ember';Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
md | MarkdownIt | (required) | The markdown-it instance provided by VitePress |
component | string | 'CodePreview' | Name of the Vue wrapper component to render |
Usage
Register it in your VitePress markdown config:
export default defineConfig({
markdown: {
config(md) {
emberFence(md);
// or with a custom component name:
// emberFence(md, 'MyEmberDemo');
},
},
});Fence syntax
The plugin intercepts fences with gjs or gts language and a live flag:
| Input | Output HTML |
|---|---|
```gjs live | <CodePreview :loader="() => import('virtual:ember-demo-HASH.gjs')" /> |
```gts live | <CodePreview :loader="() => import('virtual:ember-demo-HASH.gts')" /> |
```gjs live preview | <CodePreview :loader="() => import('...')"><pre><code>…</code></pre></CodePreview> |
Each fence body is hashed and stored in a shared registry. The Vite plugin's load hook serves the source when the module is imported, and the transform hook compiles it.
For file-based demos, a core ruler transforms <CodePreview src="/demos/counter.gts" /> into <CodePreview :loader="() => import('/demos/counter.gts')" /> so Vite can statically analyse and bundle the import in production (SSG) builds.
CodePreview (Vue component)
The Vue wrapper component that mounts Ember components into the page.
Props
| Prop | Type | Description |
|---|---|---|
loader | () => Promise<any> | A function that returns a dynamic import of the Ember module (preferred) |
src | string | URL of the module to import (fallback, uses @vite-ignore) |
Slots
| Slot | Description |
|---|---|
default | Content displayed below the rendered component (used by preview mode to show source code) |
How it works
- On mount, it calls the
loader()prop (or falls back toimport(src)) to load the module. - It imports
renderComponentfrom@ember/renderer. - It calls
renderComponent(Component, { into: element })to mount the Ember component. - On Vue unmount, it calls
cleanup.destroy()to tear down the Ember component tree.
Error handling
If the import or render fails, an error banner is displayed inside the component using VitePress theme variables for consistent styling.
Build pipeline
The compilation flow for each .gjs / .gts module:
Source (.gjs/.gts)
│
├─ content-tag preprocessor
│ Converts <template>...</template> into JS function calls
│
├─ Babel transform
│ ├─ babel-plugin-ember-template-compilation (wire format)
│ ├─ decorator-transforms (@tracked)
│ ├─ custom babelPlugins (if configured)
│ └─ @babel/plugin-transform-typescript (.gts only)
│
└─ Compiled ES module
Ready for browser import