Skip to content

Config Loader

Loader

Functions

loadConfig

function loadConfig(configPath?: string): Config
TypeScript

Use loadConfig to load your skrypt configuration and pass it to the documentation generator at runtime.

Call this at the start of your generation pipeline when you need programmatic control over which config file is loaded — for example, when building a custom CI script that switches between configs for different environments or monorepo packages.

By default, skrypt walks up the directory tree looking for a skrypt.config.yml (or .json) file. Pass an explicit configPath to skip discovery and load a specific file instead. Throws immediately if the path you provide doesn't exist, so config errors surface before any generation work begins.

Parameters

NameTypeRequiredDescription
configPathstringNoAbsolute or relative path to a specific config file. Omit to let skrypt auto-discover the nearest config by walking up from the current working directory.

Returns

Returns a Config object with your resolved settings (provider, output directory, source entries, etc.). Pass this directly to your generation function — it's the single source of truth for the entire run.

Heads up

  • If you pass an explicit configPath and the file doesn't exist, loadConfig throws synchronously. Wrap it in a try/catch when the path comes from user input or an environment variable.
  • Omitting configPath does not throw if no config file is found — it returns the DEFAULT_CONFIG instead, so generation will still run with baseline defaults.

Example:

import { existsSync, readFileSync } from "fs";
import { join } from "path";

// Inline types (do not import from autodocs)
interface SourceEntry {
  path: string;
  language?: "typescript" | "python" | "go" | "rust";
}

interface Config {
  provider: string;
  apiKey?: string;
  output: string;
  sources: SourceEntry[];
  byTopic: boolean;
  multiLang: boolean;
}

const DEFAULT_CONFIG: Config = {
  provider: "openai",
  output: "./docs",
  sources: [],
  byTopic: false,
  multiLang: false,
};

// Minimal self-contained loadConfig implementation
function loadConfig(configPath?: string): Config {
  const searchPaths = configPath
    ? [configPath]
    : ["./skrypt.config.yml", "./skrypt.config.json"];

  for (const candidate of searchPaths) {
    if (configPath && !existsSync(configPath)) {
      throw new Error(`Config file not found: ${configPath}`);
    }
    if (existsSync(candidate)) {
      const raw = readFileSync(candidate, "utf-8");
      const parsed = JSON.parse(raw); // simplified — real impl supports YAML too
      return { ...DEFAULT_CONFIG, ...parsed };
    }
  }

  return DEFAULT_CONFIG;
}

// --- Usage ---

// Simulate a config file on disk
import { writeFileSync } from "fs";
writeFileSync(
  "./skrypt.config.json",
  JSON.stringify({
    provider: "anthropic",
    apiKey: "sk-ant-api03-xK9mP2nQrL8vW4jY",
    output: "./content/docs",
    sources: [{ path: "./src", language: "typescript" }],
    byTopic: true,
    multiLang: true,
  })
);

try {
  // Load a specific config file (e.g. in a CI script targeting a monorepo package)
  const config = loadConfig("./skrypt.config.json");

  console.log("Loaded config:", config);
  // → {
  //     provider: 'anthropic',
  //     apiKey: 'sk-ant-api03-xK9mP2nQrL8vW4jY',
  //     output: './content/docs',
  //     sources: [ { path: './src', language: 'typescript' } ],
  //     byTopic: true,
  //     multiLang: true
  //   }

  // Pass config.output and config.sources to your generation step
  console.log(`Generating docs → ${config.output} (topic mode: ${config.byTopic})`);
} catch (err) {
  console.error("Failed to load config:", (err as Error).message);
  process.exit(1);
}
TypeScript

validateConfig

function validateConfig(config: Config): string[]
TypeScript

Use validateConfig to catch configuration errors before passing a Config object to skrypt generate, so documentation generation fails fast with clear messages instead of crashing mid-run.

Call this after loading or constructing a Config object — for example, after reading a skrypt.config.yaml file — to surface all problems at once before any files are scanned or AI calls are made.

It collects every validation error in a single pass rather than throwing on the first problem, so you get a complete list of what needs fixing.

NameTypeRequiredDescription
configConfigYesThe fully constructed config object to validate — typically built from a config file or CLI flags before being passed to the generator.

Returns: An array of human-readable error strings describing every invalid field. An empty array means the config is valid. Pass the config to your generation step only when this returns []; otherwise surface the errors to the user and exit.

Heads up:

  • Validation is non-throwing — even a completely broken config returns errors as strings rather than throwing. Always check the array length before proceeding.
  • Version must be exactly 1 or 2; any other value (including "1" as a string or 0) produces an error.

Example:

type LLMProvider = "openai" | "anthropic" | "gemini";

interface SourceEntry {
  path: string;
  language?: string;
}

interface Config {
  version: number;
  provider: LLMProvider;
  sources: SourceEntry[];
  output: string;
  multiLang?: boolean;
  byTopic?: boolean;
  name?: string;
  license?: string;
}

function validateConfig(config: Config): string[] {
  const errors: string[] = [];

  if (config.version !== 1 && config.version !== 2) {
    errors.push(`Unsupported config version: ${config.version}`);
  }

  if (!config.provider) {
    errors.push("Missing required field: provider");
  }

  if (!config.output) {
    errors.push("Missing required field: output");
  }

  if (!config.sources || config.sources.length === 0) {
    errors.push("At least one source entry is required");
  }

  return errors;
}

// Simulate a config loaded from skrypt.config.yaml with a bad version
const loadedConfig: Config = {
  version: 3, // unsupported
  provider: "openai",
  sources: [{ path: "./src", language: "typescript" }],
  output: "./content/docs",
  name: "Payments API",
};

const errors = validateConfig(loadedConfig);

if (errors.length > 0) {
  console.log("Invalid config — fix the following before generating docs:");
  errors.forEach((err) => console.log(`  ✗ ${err}`));
  process.exit(1);
} else {
  console.log("Config is valid — proceeding with generation.");
}

// Expected output:
// Invalid config — fix the following before generating docs:
//   ✗ Unsupported config version: 3
TypeScript

resolveSourceEntries

function resolveSourceEntries(config: Config): SourceEntry[]
TypeScript

Use resolveSourceEntries to normalize your skrypt config into a consistent list of source entries, regardless of whether the config uses the legacy v1 source field or the newer v2 sources array.

Call this when you're building tooling on top of skrypt's config format and need to iterate over source paths without branching on which config version a project uses. It's the right first step before scanning files or passing source paths to a generator.

When a v2 sources array is present, each entry is returned as-is (with missing fields falling back to the top-level source defaults). When only a v1 source is defined, it's wrapped in an array so downstream code always receives the same shape.

Parameters

NameTypeRequiredDescription
configConfigYesA parsed skrypt config object — typically loaded from skrypt.config.yml. Must contain at least a source field; optionally a sources array for multi-source v2 configs.

Returns

An array of SourceEntry objects, each with a path, optional label, and include glob patterns. Pass this array directly to your file scanner or documentation generator — every entry is guaranteed to have the same shape, so you can iterate without checking which config version you're dealing with.

Heads up

  • If sources is defined but empty ([]), the function falls back to the top-level source field rather than returning an empty array. An empty sources key is treated as if it weren't there.
  • Individual entries in sources that omit include will inherit the top-level source.include globs — so the top-level source field is always meaningful, even in v2 configs.

Example:

type SourceEntry = {
  path: string;
  label?: string;
  include?: string[];
};

type SourceConfig = {
  path: string;
  include?: string[];
};

type Config = {
  source: SourceConfig;
  sources?: Array<{ path: string; label?: string; include?: string[] }>;
};

function resolveSourceEntries(config: Config): SourceEntry[] {
  if (config.sources && config.sources.length > 0) {
    return config.sources.map((s) => ({
      path: s.path,
      label: s.label,
      include: s.include ?? config.source.include,
    }));
  }
  return [
    {
      path: config.source.path,
      include: config.source.include,
    },
  ];
}

// Scenario 1: v2 config with multiple sources
const v2Config: Config = {
  source: { path: "./src", include: ["**/*.ts"] },
  sources: [
    { path: "./src/core", label: "Core" },
    { path: "./src/plugins", label: "Plugins", include: ["**/*.ts", "**/*.tsx"] },
  ],
};

// Scenario 2: v1 config with a single source
const v1Config: Config = {
  source: { path: "./src", include: ["**/*.ts", "**/*.go"] },
};

try {
  const v2Entries = resolveSourceEntries(v2Config);
  console.log("v2 entries:", JSON.stringify(v2Entries, null, 2));
  // [
  //   { path: "./src/core", label: "Core", include: ["**/*.ts"] },       <- inherits top-level include
  //   { path: "./src/plugins", label: "Plugins", include: ["**/*.ts", "**/*.tsx"] }
  // ]

  const v1Entries = resolveSourceEntries(v1Config);
  console.log("v1 entries:", JSON.stringify(v1Entries, null, 2));
  // [{ path: "./src", include: ["**/*.ts", "**/*.go"] }]
} catch (err) {
  console.error("Failed to resolve source entries:", err);
}
TypeScript

checkApiKey

function checkApiKey(provider: LLMProvider): { ok: boolean; envKey: string | null }
TypeScript

Use checkApiKey to verify that the required environment variable for a given LLM provider is set before attempting to generate documentation.

Call this before running a generation job to give users a clear, early error if their API credentials are missing — rather than letting the job fail mid-run with a cryptic provider error. It's especially useful in CLI startup checks or configuration validation flows.

Ollama is the exception: since it runs locally without an API key, checkApiKey always returns ok: true for it with envKey: null.

Parameters

NameTypeRequiredDescription
providerLLMProviderYesThe AI provider to check — e.g. "openai", "anthropic", or "ollama". Determines which environment variable is looked up.

Returns

Returns { ok: boolean; envKey: string | null }. If ok is false, envKey contains the name of the missing environment variable (e.g. "OPENAI_API_KEY") — surface this to the user so they know exactly what to set. If ok is true and envKey is null, the provider requires no key (Ollama).

Heads up

  • This checks for the variable's presence, not its validity. A key that exists but is expired or malformed will still return ok: true.

Example:

type LLMProvider = "openai" | "anthropic" | "ollama" | "gemini";

const PROVIDER_ENV_KEYS: Record<LLMProvider, string | null> = {
  openai: "OPENAI_API_KEY",
  anthropic: "ANTHROPIC_API_KEY",
  gemini: "GEMINI_API_KEY",
  ollama: null,
};

function checkApiKey(provider: LLMProvider): { ok: boolean; envKey: string | null } {
  const envKey = PROVIDER_ENV_KEYS[provider];

  if (!envKey) {
    return { ok: true, envKey: null };
  }

  const ok = !!process.env[envKey];
  return { ok, envKey };
}

// Simulate a pre-generation startup check
const provider: LLMProvider = "openai";

// Simulate the key being set
process.env["OPENAI_API_KEY"] = "sk-proj-a1b2c3d4e5f6g7h8i9j0";

const result = checkApiKey(provider);

if (!result.ok) {
  console.error(`Missing API key: set the ${result.envKey} environment variable to continue.`);
  process.exit(1);
}

console.log(result);
// { ok: true, envKey: 'OPENAI_API_KEY' }
TypeScript
Was this helpful?