Skip to content

Plugin API

vitePluginEmber(options?)

The main Vite plugin. Import and add it to your Vite config.

ts
import vitePluginEmber from 'vite-plugin-ember';

Options

OptionTypeDefaultDescription
compilerPathstring'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.
resolveRecord<string, string>{}Custom import resolution map. Keys are bare specifiers, values are resolved paths or package names.
babelPluginsany[][]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.

ts
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:

md
```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:

ts
vitePluginEmber({
  babelPlugins: ['ember-concurrency/async-arrow-task-transform'],
});

These plugins run after the built-in template compilation and decorator transforms.

What the plugin does

  1. Template compilation — Preprocesses <template> tags via content-tag, then compiles them to wire format using babel-plugin-ember-template-compilation.

  2. Decorator transforms — Converts @tracked and other decorators using decorator-transforms.

  3. TypeScript — Strips type annotations from .gts files via @babel/plugin-transform-typescript.

  4. Module resolution — Resolves @ember/* and @glimmer/* bare specifiers to files inside ember-source/dist/packages/.

  5. @embroider/macros shim — Provides runtime implementations for isDevelopingApp(), macroCondition(), and other compile-time macros that ember-source's ESM modules import.

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

ts
import { emberFence } from 'vite-plugin-ember';

Parameters

ParameterTypeDefaultDescription
mdMarkdownIt(required)The markdown-it instance provided by VitePress
componentstring'CodePreview'Name of the Vue wrapper component to render

Usage

Register it in your VitePress markdown config:

ts
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:

InputOutput 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

PropTypeDescription
loader() => Promise<any>A function that returns a dynamic import of the Ember module (preferred)
srcstringURL of the module to import (fallback, uses @vite-ignore)

Slots

SlotDescription
defaultContent displayed below the rendered component (used by preview mode to show source code)

How it works

  1. On mount, it calls the loader() prop (or falls back to import(src)) to load the module.
  2. It imports renderComponent from @ember/renderer.
  3. It calls renderComponent(Component, { into: element }) to mount the Ember component.
  4. 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:

text
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