vite-plugin-template-custom
v0.0.2
Published
Vite plugin for custom template rendering with flexible configuration
Readme
vite-plugin-template-custom
Flexible Vite plugin for rendering custom template files with support for multiple template languages (Go Template, Handlebars, Liquid, etc.).
Why This Plugin?
This plugin generates template files that reference your Vite assets in different template languages. Perfect for:
- Go Template - Go-based template systems
- Handlebars - Handlebars-based template systems
- Liquid - Liquid template engine
- Any custom template system - create your own renderer
Installation
pnpm add -D vite-plugin-template-customQuick Start
import { defineConfig } from 'vite'
import templateCustom, { clean, goTemplateRenderer } from 'vite-plugin-template-custom'
export default defineConfig({
plugins: [
clean(['assets', 'snippets']), // Clean old files before build
templateCustom({
entrypointsDir: 'frontend/entrypoints',
sourceCodeDir: 'frontend',
templates: [
{
name: 'vite-tag.html',
outputDirs: ['snippets'],
renderer: goTemplateRenderer()
}
]
})
]
})How It Works
- Plugin generates template files during Vite dev/build
- Include the generated template in your main template files
- Use the template to reference your Vite-built assets
For Go Templates
Generated file: snippets/vite-tag.html
Include in your theme:
<!-- In your layout file -->
{{ template "vite-tag" (dict "FileUrl" "theme.ts") }}For Handlebars
Generated file: snippets/vite-tag.html
Include in your theme:
<!-- In your layout file -->
{{ snippet 'vite-tag' path='theme.ts' }}💡 Important: Adjust the inclusion method according to your template engine's syntax. Check the Built-in Renderers section for detailed examples.
Built-in Renderers
Go Template Renderer
Perfect for Go Template syntax.
Configuration:
import { goTemplateRenderer } from 'vite-plugin-template-custom'
goTemplateRenderer({
assetPath?: string // Asset path prefix (default: 'assets')
includePreloadStylesheet?: boolean // Include preload option (default: true)
})Generated Template Example:
{{/* Auto-generated by vite-plugin-template-custom */}}
{{define "vite-tag"}}
{{ if eq .FileUrl "theme.css" }}
{{if .PreloadStylesheet}}
<link href="/assets/theme-Bqzl3FIG.css" as='style' rel="preload">
{{else}}
<link href="/assets/theme-Bqzl3FIG.css" rel="stylesheet" type="text/css" media="all" />
{{end}}
{{end}}
{{ if eq .FileUrl "theme.ts" }}
<script src="/assets/theme-C_GNs-Rh.js" type="module" crossorigin="anonymous"></script>
{{if .PreloadStylesheet}}
<link href="/assets/theme-Bqzl3FIG.css" as='style' rel="preload">
{{else}}
<link href="/assets/theme-Bqzl3FIG.css" rel="stylesheet" type="text/css" media="all" />
{{end}}
{{end}}
{{end}}Usage in Your Go Templates:
{{ template "vite-tag" (dict "FileUrl" "theme.ts") }}
{{ template "vite-tag" (dict "FileUrl" "theme.ts" "PreloadStylesheet" true) }}
{{ template "vite-tag" (dict "FileUrl" "theme.css") }}Handlebars Renderer
Perfect for Handlebars template syntax.
Configuration:
import { handlebarsRenderer } from 'vite-plugin-template-custom'
handlebarsRenderer({
versionNumbers?: boolean // Append version numbers (default: false)
includePreloadStylesheet?: boolean // Include preload option (default: true)
snippetVariableName?: string // Variable name (default: 'path')
})Generated Template Example:
{{# comment }}
Auto-generated by vite-plugin-template-custom
{{/ comment }}
{{ assign 'path' (replace path '@/' '../') }}
{{ assign 'path' (replace path '~/' '../') }}
{{# if path == "/frontend/entrypoints/theme.css" or path == "theme.css" }}
{{#if preload_stylesheet}}
{{ preload_tag (asset_url 'theme-Bqzl3FIG.css') as='style' }}
{{else}}
{{ stylesheet_tag (asset_url 'theme-Bqzl3FIG.css') }}
{{/if}}
{{else if path == "/frontend/entrypoints/theme.ts" or path == "theme.ts" }}
<script src="{{ asset_url 'theme-C_GNs-Rh.js' }}" type="module" crossorigin="anonymous"></script>
{{#if preload_stylesheet}}
{{ preload_tag (asset_url 'theme-Bqzl3FIG.css') as='style' }}
{{else}}
{{ stylesheet_tag (asset_url 'theme-Bqzl3FIG.css') }}
{{/if}}
{{/if}}Usage in Your Handlebars Templates:
{{ snippet 'vite-tag' path='theme.ts' }}
{{ snippet 'vite-tag' path='theme.ts' preload_stylesheet=true }}
{{ snippet 'vite-tag' path='theme.css' }}Liquid Renderer
Perfect for Liquid template syntax (Shopify themes).
Note: Implementation reference from vite-plugin-shopify
Configuration:
import { liquidRenderer } from 'vite-plugin-template-custom'
liquidRenderer({
versionNumbers?: boolean // Append version numbers (default: false)
includePreloadStylesheet?: boolean // Include preload option (default: true)
snippetVariableName?: string // Variable name (default: 'file')
})Generated Template Example:
{% comment %}
Auto-generated by vite-plugin-template-custom
{% endcomment %}
{% assign file = file | replace: '@/', '../' | replace: '~/', '../' %}
{% if file == '/frontend/entrypoints/theme.css' or file == 'theme.css' %}
{{ 'theme-Bqzl3FIG.css' | asset_url | split: '?' | first | stylesheet_tag: preload: preload_stylesheet }}
{% elsif file == '/frontend/entrypoints/theme.ts' or file == 'theme.ts' %}
<script src="{{ 'theme-C_GNs-Rh.js' | asset_url | split: '?' | first }}" type="module" crossorigin="anonymous"></script>
{{ 'theme-Bqzl3FIG.css' | asset_url | split: '?' | first | stylesheet_tag: preload: preload_stylesheet }}
{% endif %}Usage in Your Liquid Templates:
<!-- In your Shopify theme -->
{% render 'vite-tag', file: 'theme.ts' %}
{% render 'vite-tag', file: 'theme.ts', preload_stylesheet: true %}
{% render 'vite-tag', file: 'theme.css' %}CSS Handling
How CSS Works
When you import CSS in your JavaScript/TypeScript entry files, Vite automatically:
- Processes and bundles the CSS
- Generates a hashed CSS file (e.g.,
theme-Bqzl3FIG.css) - The plugin includes this CSS in the template output
Loading CSS Files
Option 1: Import CSS in your JS/TS entry file (Recommended)
// frontend/entrypoints/theme.ts
import '../styles/theme.css'
// Your JS code hereThen reference the JS file, CSS will be automatically included:
<!-- Go Template -->
{{ template "vite-tag" (dict "FileUrl" "theme.ts") }}Option 2: Reference CSS file directly
<!-- Go Template -->
{{ template "vite-tag" (dict "FileUrl" "theme.css") }}<!-- Handlebars -->
{{ snippet 'vite-tag' path='theme.css' }}Preload Stylesheet
The PreloadStylesheet / preload_stylesheet option tells the browser to preload CSS for better performance:
Go Template:
<!-- Preload CSS for faster loading -->
{{ template "vite-tag" (dict "FileUrl" "theme.ts" "PreloadStylesheet" true) }}
<!-- Or use standard stylesheet loading -->
{{ template "vite-tag" (dict "FileUrl" "theme.ts") }}Handlebars:
<!-- Preload CSS for faster loading -->
{{ snippet 'vite-tag' path='theme.ts' preload_stylesheet=true }}
<!-- Or use standard stylesheet loading -->
{{ snippet 'vite-tag' path='theme.ts' }}What's the difference?
With preload_stylesheet=true:
<script src="/assets/theme.js" type="module"></script>
<link href="/assets/theme.css" as='style' rel="preload">Without it (default):
<script src="/assets/theme.js" type="module"></script>
<link href="/assets/theme.css" rel="stylesheet" type="text/css" media="all" />💡 Tip: Use
preload_stylesheet=truefor above-the-fold critical CSS to improve perceived load time.
Multiple Templates
Generate different template formats in one build:
export default defineConfig({
plugins: [
templateCustom({
templates: [
{
name: 'vite-go.html',
outputDirs: ['snippets'],
renderer: goTemplateRenderer()
},
{
name: 'vite-handlebars.html',
outputDirs: ['snippets'],
renderer: handlebarsRenderer()
}
]
})
]
})Custom Renderer
Create your own renderer for any template language:
import { TemplateRenderer } from 'vite-plugin-template-custom'
const myCustomRenderer: TemplateRenderer = {
renderDev(context) {
// Development mode: connect to Vite dev server
return `<script src="${context.devServerUrl}/@vite/client"></script>
<script src="${context.devServerUrl}/${context.entrypointsDir}/{{ .file }}"></script>`
},
renderBuild(context, entries) {
// Build mode: reference compiled assets
return entries.map(entry => {
const tags = [`<script src="/${entry.file}" type="module"></script>`]
// Include CSS files
if (entry.css) {
entry.css.forEach(cssFile => {
tags.push(`<link href="/${cssFile}" rel="stylesheet">`)
})
}
return tags.join('\n')
}).join('\n')
}
}
export default defineConfig({
plugins: [
templateCustom({
templates: [{
name: 'custom.html',
outputDirs: ['dist'],
renderer: myCustomRenderer
}]
})
]
})Clean Utility
Clean build artifacts before building.
Signature:
clean(patterns?: string[], options?: { cleanContents?: boolean }): PluginUsage:
import { clean } from 'vite-plugin-template-custom'
// Clean directory contents (keeps directories)
clean(['assets', 'snippets'])
// Glob patterns
clean(['assets/**/*.js', 'snippets/vite-*.html'])
// Delete entire directories
clean(['dist'], { cleanContents: false })API Reference
Plugin Options
interface Options {
templates: TemplateConfig[] // Template configurations (required)
entrypointsDir?: string // Entry points directory (default: 'frontend/entrypoints')
sourceCodeDir?: string // Source code directory (default: 'frontend')
themeRoot?: string // Theme root directory (default: './')
additionalEntrypoints?: string[] // Additional entry files or glob patterns
}Template Configuration
interface TemplateConfig {
name: string // Template file name (e.g., 'vite-tag.html')
outputDirs: string[] // Output directories for this template
renderer: TemplateRenderer // Template renderer
generateInDev?: boolean // Generate in dev mode (default: true)
generateInBuild?: boolean // Generate in build mode (default: true)
}Template Renderer Interface
interface TemplateRenderer {
renderDev(context: DevContext): string
renderBuild(context: BuildContext, entries: AssetEntry[]): string
}
interface DevContext {
devServerUrl: string
entrypointsDir: string
hasReactPlugin: boolean
aliases: Array<{ find: string; replacement: string }>
}
interface BuildContext {
manifest: Manifest
entrypointsDir: string
base?: string
aliases: Array<{ find: string; replacement: string }>
}License
MIT
