All files / src/cli init.ts

100% Statements 49/49
96.15% Branches 25/26
100% Functions 5/5
100% Lines 45/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109                  1x 1x                   1x     10x 10x 10x 16x   10x             7x 3x               7x 22x 22x   5x               5x 5x   3x     3x 3x 3x 3x       3x 3x 2x 2x 2x       7x 7x 7x 7x   7x   7x 6x 6x   1x     7x 7x 2x 2x 2x 1x 1x   1x     5x       7x    
/**
 * vitek init: scaffold src/api and add vitek to vite.config
 * Usage: vitek init [--force]
 * Idempotent: does not overwrite existing files unless --force.
 */
 
import * as fs from 'fs';
import * as path from 'path';
 
const DEFAULT_API_DIR = 'src/api';
const HEALTH_ROUTE_JS = `/**
 * Health check endpoint
 * GET /api/health
 */
 
export default function handler() {
  return { ok: true, timestamp: new Date().toISOString() };
}
`;
 
const VITE_CONFIG_NAMES = ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.cjs'];
 
export function parseInitArgs(): { force: boolean } {
  const argv = process.argv.slice(2);
  let force = false;
  for (const arg of argv) {
    if (arg === '--force' || arg === '-f') force = true;
  }
  return { force };
}
 
/**
 * Ensures directory exists. Creates it and parents if needed.
 */
function ensureDir(dir: string): void {
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
}
 
/**
 * Finds the first existing vite config file in cwd.
 */
function findViteConfig(cwd: string): string | null {
  for (const name of VITE_CONFIG_NAMES) {
    const p = path.join(cwd, name);
    if (fs.existsSync(p) && fs.statSync(p).isFile()) return p;
  }
  return null;
}
 
/**
 * Injects vitek import and plugin into config content if not already present.
 * Returns new content or null if no change needed.
 */
export function injectVitekIntoConfig(content: string): string | null {
  const hasVitek = /vitek-plugin|vitek\s*\(/.test(content);
  if (hasVitek) return null;
 
  let out = content;
 
  // Add import after first line (so it's at the top with other imports)
  Eif (!out.includes('vitek-plugin')) {
    const firstNewline = out.indexOf('\n');
    const insertIdx = firstNewline === -1 ? out.length : firstNewline + 1;
    out = out.slice(0, insertIdx) + "import { vitek } from 'vitek-plugin';\n" + out.slice(insertIdx);
  }
 
  // Add vitek() to plugins array: find plugins: [ and insert vitek(), as first element
  const pluginsMatch = out.match(/plugins:\s*\[/);
  if (!pluginsMatch) return null;
  const insertPluginsIdx = pluginsMatch.index! + pluginsMatch[0].length;
  out = out.slice(0, insertPluginsIdx) + 'vitek(), ' + out.slice(insertPluginsIdx);
  return out;
}
 
export async function runInit(): Promise<void> {
  const { force } = parseInitArgs();
  const cwd = process.cwd();
  const apiDir = path.join(cwd, DEFAULT_API_DIR);
  const healthPath = path.join(apiDir, 'health.get.ts');
 
  ensureDir(apiDir);
 
  if (!fs.existsSync(healthPath) || force) {
    fs.writeFileSync(healthPath, HEALTH_ROUTE_JS, 'utf-8');
    console.log(`[vitek init] Created ${DEFAULT_API_DIR}/health.get.ts`);
  } else {
    console.log(`[vitek init] ${DEFAULT_API_DIR}/health.get.ts already exists (use --force to overwrite)`);
  }
 
  const configPath = findViteConfig(cwd);
  if (configPath) {
    const content = fs.readFileSync(configPath, 'utf-8');
    const newContent = injectVitekIntoConfig(content);
    if (newContent) {
      fs.writeFileSync(configPath, newContent, 'utf-8');
      console.log(`[vitek init] Added vitek to ${path.basename(configPath)}`);
    } else {
      console.log(`[vitek init] ${path.basename(configPath)} already has vitek`);
    }
  } else {
    console.log(
      '[vitek init] No vite.config.js/ts/mjs/cjs found; add vitek() to your Vite config manually. See https://martinsbicudo.github.io/vitek-plugin/guide/getting-started'
    );
  }
  console.log('[vitek init] Tip: expose your API to AI assistants with `vitek mcp` — https://martinsbicudo.github.io/vitek-plugin/guide/mcp-project');
}