All files / src/plugin vitek-build.ts

45% Statements 18/40
41.46% Branches 17/41
80% Functions 4/5
44.44% Lines 16/36

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 110 111                                27x         25x 25x       2x 1x 1x   1x 1x   1x 1x         2x 2x                   1x 2x 2x                                     1x                                                                              
/**
 * vitek:build — buildStart, closeBundle, buildEnd, configResolved
 */
 
import type { Plugin } from 'vite';
import * as path from 'path';
import * as fs from 'fs';
import { scanApiDirectory } from '../core/file-system/scan-api-dir.js';
import { writeManifest } from '../core/introspection/manifest.js';
import { parsedRoutesToSchema, runFileGeneration } from '../core/generation/run-file-generation.js';
import { buildApiBundle, getApiBundleFilename } from '../build/build-api-bundle.js';
import { buildSocketsBundle, getSocketsBundleFilename } from '../build/build-sockets-bundle.js';
import { API_BASE_PATH, API_DIR_NAME, getSocketBasePath } from '../shared/constants.js';
import type { PluginContext } from './context.js';
 
export function createBuildPlugin(ctx: PluginContext): Plugin {
  return {
    name: 'vitek:build',
    enforce: 'pre',
 
    configResolved(config) {
      ctx.root = config.root;
      ctx.buildOutDir = path.resolve(ctx.root!, config.build?.outDir ?? 'dist');
    },
 
    async buildStart() {
      if (!ctx.buildApi || !ctx.root) return;
      const fullApiDir = path.resolve(ctx.root, ctx.apiDirOption);
      Iif (!fs.existsSync(fullApiDir)) return;
 
      const scanResult = scanApiDirectory(fullApiDir);
      Iif (scanResult.routes.length === 0 && scanResult.sockets.length === 0) return;
 
      const schema = parsedRoutesToSchema(scanResult.routes);
      const socketBasePath = getSocketBasePath(
        ctx.options.apiBasePath,
        typeof ctx.options.sockets === 'object' ? ctx.options.sockets?.path : undefined
      );
 
      try {
        await runFileGeneration({
          root: ctx.root,
          schema,
          sockets: scanResult.sockets,
          apiBasePath: ctx.options.apiBasePath ?? API_BASE_PATH,
          socketBasePath,
          openApi: ctx.options.openApi,
          serverPort: 5173,
          onGenerationError: ctx.options.onGenerationError,
        });
        const plugins = ctx.options.plugins ?? [];
        const apiBasePath = ctx.options.apiBasePath ?? API_BASE_PATH;
        for (const plugin of plugins) {
          if (plugin.afterTypesGenerated) {
            await plugin.afterTypesGenerated({
              root: ctx.root,
              schema,
              sockets: scanResult.sockets,
              apiBasePath,
              socketBasePath,
            });
          }
        }
      } catch (err) {
        const error = err instanceof Error ? err : new Error(String(err));
        console.error('[vitek] Failed to generate types/services:', error.message);
        ctx.options.onGenerationError?.(error);
      }
    },
 
    async closeBundle() {
      Eif (!ctx.buildApi || !ctx.root || !ctx.buildOutDir) return;
      const fullApiDir = path.resolve(ctx.root, ctx.apiDirOption);
      try {
        writeManifest(ctx.root, ctx.apiDirOption, ctx.buildOutDir);
      } catch (err) {
        console.error('[vitek] Failed to write manifest:', err instanceof Error ? err.message : err);
      }
      try {
        await buildApiBundle({
          root: ctx.root,
          apiDir: fullApiDir,
          outDir: ctx.buildOutDir,
          alias: ctx.options.alias,
        });
      } catch (err) {
        console.error('[vitek] Failed to build API bundle:', err instanceof Error ? err.message : err);
      }
      const socketsEnabled = ctx.options.sockets !== false;
      if (socketsEnabled && fs.existsSync(fullApiDir)) {
        try {
          await buildSocketsBundle({
            root: ctx.root,
            apiDir: fullApiDir,
            outDir: ctx.buildOutDir,
          });
        } catch (err) {
          console.error('[vitek] Failed to build sockets bundle:', err instanceof Error ? err.message : err);
        }
      }
    },
 
    buildEnd() {
      if (ctx.cleanupFn) {
        ctx.cleanupFn();
        ctx.cleanupFn = null;
      }
    },
  };
}