Kimesh Modules
Kimesh Modules are reusable packages that extend your application's functionality at build-time. They can add Vite plugins, modify configuration, register components, and much more.
Overview
Modules are the primary way to extend Kimesh. They provide:
- Vite plugin integration
- Configuration extension
- Auto-import sources
- Component registration
- Build-time code generation
Using Modules
Add modules to your kimesh.config.ts:
import { defineKmConfig } from '@kimesh/kit'
import tailwindcss from '@kimesh/tailwindcss'
import shadcn from '@kimesh/shadcn'
export default defineKmConfig({
modules: [
// Use module with defaults
tailwindcss,
// Use module with custom options
[shadcn, { prefix: 'Ui' }],
],
})Official Modules
@kimesh/pinia
Pinia state management integration with auto-imports and DevTools:
import pinia from '@kimesh/pinia'
export default defineKmConfig({
modules: [
[
pinia,
{
// Auto-import Pinia functions (default: true)
autoImports: true,
// Store directory (default: 'src/stores')
storesDir: 'src/stores',
// Store file pattern (default: '*.ts')
storePattern: '*.ts',
// Vue DevTools integration (default: true)
devtools: true,
// Auto-scan stores directory (default: true)
autoScanStores: true,
},
],
],
})Features:
- Auto-imports 11 Pinia functions (defineStore, storeToRefs, mapStores, etc.)
- Auto-scans
storesDirfor store definitions - DevTools integration in development
- SSR-optimized Vite configuration
Usage:
// src/stores/counter.ts
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
})<script setup>
// defineStore and storeToRefs are auto-imported
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
const { count } = storeToRefs(store)
</script>Composables:
import { useKimeshPinia, tryUseKimeshPinia } from '@kimesh/pinia'
// Get Pinia instance (throws if not available)
const pinia = useKimeshPinia()
// Try to get (returns undefined if not available)
const maybePinia = tryUseKimeshPinia()@kimesh/tailwindcss
Automatic TailwindCSS integration for Kimesh layers:
import tailwindcss from '@kimesh/tailwindcss'
export default defineKmConfig({
modules: [
[
tailwindcss,
{
// Additional source patterns
additionalSources: ['../shared/components'],
// Auto-inject @reference in <style> blocks
autoReference: true,
// Alias for generated CSS
alias: '#kimesh/tailwind',
// Path to main CSS file with @theme
mainCss: 'src/app.css',
},
],
],
})Features:
- Generates
@sourcedirectives for all layer directories - Auto-injects
@referencein Vue<style>blocks using@apply - Creates
#kimesh/tailwindalias for importing generated CSS - Supports custom theme through
mainCssoption
Usage:
/* src/app.css */
@import '#kimesh/tailwind';
@theme {
--color-brand: #3b82f6;
}<style scoped>
/* @reference is auto-injected */
.button {
@apply bg-brand text-white;
}
</style>@kimesh/shadcn
Automatic component registration for shadcn-vue:
import shadcn from '@kimesh/shadcn'
export default defineKmConfig({
modules: [
[
shadcn,
{
// Component prefix (default: 'Ui')
prefix: 'Ui',
// Component directory (supports @/ alias)
componentDir: '@/components/ui',
// Enable debug logging
debug: false,
},
],
],
})Features:
- Scans shadcn component directories across all layers
- Parses barrel files (index.ts) using OXC parser
- Registers components with configurable prefix
- Supports multiple component directories
Usage:
<template>
<!-- UiButton is auto-imported from @/components/ui/button -->
<UiButton>Click me</UiButton>
<!-- UiCard from @/components/ui/card -->
<UiCard>
<UiCardHeader>Title</UiCardHeader>
<UiCardContent>Content</UiCardContent>
</UiCard>
</template>Multiple Directories:
;[
shadcn,
{
componentDir: [
{ path: '@/components/ui', prefix: 'Ui' },
{ path: '@/components/app', prefix: 'App' },
],
},
]Module Context
Modules receive a context object with access to:
interface KimeshModuleContext {
// Resolved layers (host app + extended layers)
layers: ResolvedLayer[]
// Kimesh configuration
config: KimeshConfig
// Project root directory
root: string
// Generated directory path (.kimesh)
generatedDir: string
// Add a Vite plugin
addVitePlugin: (plugin: Plugin | Plugin[]) => void
// Add an alias
addAlias: (name: string, path: string) => void
}Creating a Module
Basic Structure
// my-module/src/module.ts
import { defineKimeshModule } from '@kimesh/kit'
import type { KimeshModule, KimeshModuleContext } from '@kimesh/kit'
interface MyModuleOptions {
enabled?: boolean
prefix?: string
}
const myModule: KimeshModule<MyModuleOptions> = {
name: '@my-org/my-module',
setup(ctx: KimeshModuleContext, options: MyModuleOptions = {}) {
const { enabled = true, prefix = '' } = options
if (!enabled) return
// Access layers
for (const layer of ctx.layers) {
console.log(`Processing layer: ${layer.name}`)
}
// Add Vite plugin
ctx.addVitePlugin({
name: 'my-plugin',
transform(code, id) {
// Transform code
return code
},
})
// Add alias
ctx.addAlias('#my-module', ctx.generatedDir + '/my-module.ts')
},
}
export default myModuleUsing defineKimeshModule
For better type inference:
import { defineKimeshModule } from '@kimesh/kit'
export default defineKimeshModule<{ apiKey: string }>({
name: '@my-org/api-module',
async setup(ctx, options) {
// options.apiKey is typed as string
console.log(`API Key: ${options?.apiKey}`)
},
})Async Setup
Modules can perform async operations:
const asyncModule: KimeshModule = {
name: '@my-org/async-module',
async setup(ctx) {
// Scan files
const files = await glob('**/*.ts', { cwd: ctx.root })
// Generate code
const code = generateCode(files)
// Write to generated directory
await writeFile(join(ctx.generatedDir, 'generated.ts'), code)
},
}Module Patterns
Layer-Aware Processing
Process files from all layers:
setup(ctx) {
for (const layer of ctx.layers) {
const srcDir = join(layer.path, 'src')
// Process layer-specific files
processDirectory(srcDir, {
prefix: layer.isApp ? '' : layer.name,
})
}
}Generating Files
Write files to the .kimesh directory:
import { writeFileSync, mkdirSync, existsSync } from 'node:fs'
import { join } from 'node:path'
setup(ctx) {
const outputDir = ctx.generatedDir
if (!existsSync(outputDir)) {
mkdirSync(outputDir, { recursive: true })
}
// Generate TypeScript file
const content = `export const layers = ${JSON.stringify(ctx.layers.map(l => l.name))}`
writeFileSync(join(outputDir, 'layers.ts'), content, 'utf-8')
// Add alias to access it
ctx.addAlias('#layers', join(outputDir, 'layers.ts'))
}Transform Plugin
Add code transformations:
setup(ctx) {
ctx.addVitePlugin({
name: 'my-transform',
transform(code, id) {
if (!id.endsWith('.vue')) return
// Transform Vue files
return code.replace(
/__MY_MARKER__/g,
JSON.stringify(ctx.config.name)
)
},
})
}Configuration Extension
Modules can read from Kimesh config:
// kimesh.config.ts
export default defineKmConfig({
// Standard config
name: 'my-app',
// Custom module config (via vite)
vite: {
myModule: {
customOption: true,
},
},
modules: [myModule],
})// In module
setup(ctx) {
const viteConfig = ctx.config.vite as any
const myConfig = viteConfig?.myModule
if (myConfig?.customOption) {
// Custom behavior
}
}Best Practices
1. Prefix Your Aliases
Use unique prefixes to avoid conflicts:
ctx.addAlias('#kimesh/my-module', path) // Good
ctx.addAlias('#my-module', path) // Okay
ctx.addAlias('#module', path) // Bad - too generic2. Handle Missing Dependencies
setup(ctx) {
try {
require.resolve('optional-dep')
// Use optional-dep
} catch {
console.warn('[my-module] Optional dependency not found')
}
}3. Support Debug Mode
setup(ctx, options) {
const debug = options?.debug ?? false
if (debug) {
console.log('[my-module] Config:', ctx.config)
console.log('[my-module] Layers:', ctx.layers.map(l => l.name))
}
}4. Document Options
interface MyModuleOptions {
/**
* Enable the module
* @default true
*/
enabled?: boolean
/**
* Custom prefix for generated names
* @default ''
*/
prefix?: string
}API Reference
KimeshModule
interface KimeshModule<TOptions = any> {
/** Module name (for logging) */
name: string
/** Setup function */
setup: (ctx: KimeshModuleContext, options?: TOptions) => void | Promise<void>
}KimeshModuleContext
interface KimeshModuleContext {
layers: ResolvedLayer[]
config: KimeshConfig
root: string
generatedDir: string
addVitePlugin: (plugin: Plugin | Plugin[]) => void
addAlias: (name: string, path: string) => void
}KimeshModuleInput
// Module reference formats
type KimeshModuleInput<T = any> =
| KimeshModule<T> // Just the module
| [KimeshModule<T>, T] // Module with optionsdefineKimeshModule
function defineKimeshModule<TOptions = any>(module: KimeshModule<TOptions>): KimeshModule<TOptions>