Skip to content

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:

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:

ts
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 storesDir for store definitions
  • DevTools integration in development
  • SSR-optimized Vite configuration

Usage:

ts
// src/stores/counter.ts
export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const increment = () => count.value++
  return { count, increment }
})
vue
<script setup>
// defineStore and storeToRefs are auto-imported
import { useCounterStore } from '@/stores/counter'

const store = useCounterStore()
const { count } = storeToRefs(store)
</script>

Composables:

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

ts
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 @source directives for all layer directories
  • Auto-injects @reference in Vue <style> blocks using @apply
  • Creates #kimesh/tailwind alias for importing generated CSS
  • Supports custom theme through mainCss option

Usage:

css
/* src/app.css */
@import '#kimesh/tailwind';

@theme {
  --color-brand: #3b82f6;
}
vue
<style scoped>
/* @reference is auto-injected */
.button {
  @apply bg-brand text-white;
}
</style>

@kimesh/shadcn

Automatic component registration for shadcn-vue:

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

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

ts
;[
  shadcn,
  {
    componentDir: [
      { path: '@/components/ui', prefix: 'Ui' },
      { path: '@/components/app', prefix: 'App' },
    ],
  },
]

Module Context

Modules receive a context object with access to:

ts
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

ts
// 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 myModule

Using defineKimeshModule

For better type inference:

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

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

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

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

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

ts
// kimesh.config.ts
export default defineKmConfig({
  // Standard config
  name: 'my-app',

  // Custom module config (via vite)
  vite: {
    myModule: {
      customOption: true,
    },
  },

  modules: [myModule],
})
ts
// 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:

ts
ctx.addAlias('#kimesh/my-module', path) // Good
ctx.addAlias('#my-module', path) // Okay
ctx.addAlias('#module', path) // Bad - too generic

2. Handle Missing Dependencies

ts
setup(ctx) {
  try {
    require.resolve('optional-dep')
    // Use optional-dep
  } catch {
    console.warn('[my-module] Optional dependency not found')
  }
}

3. Support Debug Mode

ts
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

ts
interface MyModuleOptions {
  /**
   * Enable the module
   * @default true
   */
  enabled?: boolean

  /**
   * Custom prefix for generated names
   * @default ''
   */
  prefix?: string
}

API Reference

KimeshModule

ts
interface KimeshModule<TOptions = any> {
  /** Module name (for logging) */
  name: string

  /** Setup function */
  setup: (ctx: KimeshModuleContext, options?: TOptions) => void | Promise<void>
}

KimeshModuleContext

ts
interface KimeshModuleContext {
  layers: ResolvedLayer[]
  config: KimeshConfig
  root: string
  generatedDir: string
  addVitePlugin: (plugin: Plugin | Plugin[]) => void
  addAlias: (name: string, path: string) => void
}

KimeshModuleInput

ts
// Module reference formats
type KimeshModuleInput<T = any> =
  | KimeshModule<T> // Just the module
  | [KimeshModule<T>, T] // Module with options

defineKimeshModule

ts
function defineKimeshModule<TOptions = any>(module: KimeshModule<TOptions>): KimeshModule<TOptions>

Released under the MIT License.