or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-lilconfig

A zero-dependency alternative to cosmiconfig for loading configuration files

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/lilconfig@3.1.x

To install, run

npx @tessl/cli install tessl/npm-lilconfig@3.1.0

index.mddocs/

Lilconfig

Lilconfig is a zero-dependency alternative to cosmiconfig that provides configuration file discovery and loading functionality. It offers both synchronous and asynchronous APIs for searching and loading configuration files from common locations, supporting JavaScript, JSON, and custom file formats through configurable loaders.

Package Information

  • Package Name: lilconfig
  • Package Type: npm
  • Language: JavaScript with TypeScript definitions
  • Installation: npm install lilconfig

Core Imports

import { lilconfig, lilconfigSync, defaultLoaders, defaultLoadersSync } from 'lilconfig';

For CommonJS:

const { lilconfig, lilconfigSync, defaultLoaders, defaultLoadersSync } = require('lilconfig');

Basic Usage

import { lilconfig, lilconfigSync } from 'lilconfig';

// Async usage
const explorer = lilconfig('myapp');
const result = await explorer.search();

if (result) {
  console.log('Config found at:', result.filepath);
  console.log('Config contents:', result.config);
}

// Sync usage
const explorerSync = lilconfigSync('myapp');
const resultSync = explorerSync.search();

if (resultSync) {
  console.log('Config found at:', resultSync.filepath);
  console.log('Config contents:', resultSync.config);
}

Architecture

Lilconfig is built around several key components:

  • Search Strategy: Systematic file discovery from current directory upward to stop directory
  • Loader System: Pluggable file loading mechanism supporting different formats (.js, .json, .cjs, .mjs)
  • Caching Layer: Optional caching for both search and load operations to improve performance
  • Transform Pipeline: Optional transformation of loaded configuration data
  • Package.json Integration: Special handling for configuration stored in package.json files

Capabilities

Configuration Searching

Creates a configuration explorer that can search for configuration files in standard locations.

/**
 * Creates an async configuration explorer
 * @param name - The configuration name to search for (e.g., 'myapp')
 * @param options - Optional configuration options
 * @returns AsyncSearcher instance with search and load methods
 */
function lilconfig(name: string, options?: Partial<Options>): AsyncSearcher;

/**
 * Creates a sync configuration explorer
 * @param name - The configuration name to search for (e.g., 'myapp')
 * @param options - Optional configuration options
 * @returns SyncSearcher instance with search and load methods
 */
function lilconfigSync(name: string, options?: OptionsSync): SyncSearcher;

Default Search Places:

For a configuration named myapp, lilconfig searches for files in this order:

// Async version (lilconfig) searches for:
[
  'package.json',           // Looks for myapp property
  '.myapprc.json',
  '.myapprc.js',
  '.myapprc.cjs',
  '.myapprc.mjs',          // Only in async mode
  '.config/myapprc',
  '.config/myapprc.json',
  '.config/myapprc.js',
  '.config/myapprc.cjs',
  '.config/myapprc.mjs',   // Only in async mode
  'myapp.config.js',
  'myapp.config.cjs',
  'myapp.config.mjs'       // Only in async mode
]

// Sync version (lilconfigSync) excludes .mjs files due to module loading limitations

Usage Examples:

import { lilconfig, lilconfigSync } from 'lilconfig';

// Search for 'myapp' configuration files
const explorer = lilconfig('myapp');
const result = await explorer.search('/some/start/path');

// Search with custom options
const explorerWithOptions = lilconfig('myapp', {
  searchPlaces: ['package.json', '.myapprc.js', 'myapp.config.js'],
  stopDir: '/home/user',
  ignoreEmptySearchPlaces: false
});

const customResult = await explorerWithOptions.search();

Async Searcher Interface

The object returned by lilconfig() providing async search and load capabilities.

interface AsyncSearcher {
  /**
   * Search for configuration files starting from specified directory
   * @param searchFrom - Directory to start searching from (defaults to process.cwd())
   * @returns Promise resolving to LilconfigResult or null if not found
   */
  search(searchFrom?: string): Promise<LilconfigResult>;
  
  /**
   * Load configuration from a specific file path
   * @param filepath - Path to configuration file to load
   * @returns Promise resolving to LilconfigResult
   */
  load(filepath: string): Promise<LilconfigResult>;
  
  /** Clear the load cache */
  clearLoadCache(): void;
  
  /** Clear the search cache */
  clearSearchCache(): void;
  
  /** Clear both load and search caches */
  clearCaches(): void;
}

Sync Searcher Interface

The object returned by lilconfigSync() providing synchronous search and load capabilities.

interface SyncSearcher {
  /**
   * Search for configuration files starting from specified directory
   * @param searchFrom - Directory to start searching from (defaults to process.cwd())
   * @returns LilconfigResult or null if not found
   */
  search(searchFrom?: string): LilconfigResult;
  
  /**
   * Load configuration from a specific file path
   * @param filepath - Path to configuration file to load
   * @returns LilconfigResult
   */
  load(filepath: string): LilconfigResult;
  
  /** Clear the load cache */
  clearLoadCache(): void;
  
  /** Clear the search cache */
  clearSearchCache(): void;
  
  /** Clear both load and search caches */
  clearCaches(): void;
}

Default Loaders

Pre-configured loaders for common file types.

/** Default async loaders supporting .js, .mjs, .cjs, .json files and files with no extension */
const defaultLoaders: Loaders;

/** Default sync loaders supporting .js, .json, .cjs files and files with no extension (no .mjs support) */
const defaultLoadersSync: LoadersSync;

Loader Resolution:

  • File extensions are mapped to loaders (e.g., .js → JavaScript loader, .json → JSON parser)
  • Files without extensions use the special noExt loader key
  • Default noExt loader treats files as JSON
  • Custom loaders can override any extension or the noExt behavior

Usage Example:

import { lilconfig, defaultLoaders } from 'lilconfig';

// Extend default loaders with custom loader
const explorer = lilconfig('myapp', {
  loaders: {
    ...defaultLoaders,
    '.yaml': (filepath, content) => require('yaml').parse(content)
  }
});

Custom Configuration Options

Configuration options for customizing search behavior and file processing.

interface Options {
  /** Custom loaders for different file extensions */
  loaders?: Loaders;
  /** Transform function to modify loaded configuration */
  transform?: Transform;
  /** Enable/disable caching (default: true) */
  cache?: boolean;
  /** Directory to stop searching at (default: os.homedir()) */
  stopDir?: string;
  /** Custom list of places to search for config files */
  searchPlaces?: string[];
  /** Whether to ignore empty config files (default: true) */
  ignoreEmptySearchPlaces?: boolean;
  /** Property name(s) to extract from package.json (default: [name]) */
  packageProp?: string | string[];
}

interface OptionsSync {
  /** Custom sync loaders for different file extensions */
  loaders?: LoadersSync;
  /** Sync transform function to modify loaded configuration */
  transform?: TransformSync;
  /** Enable/disable caching (default: true) */
  cache?: boolean;
  /** Directory to stop searching at (default: os.homedir()) */
  stopDir?: string;
  /** Custom list of places to search for config files */
  searchPlaces?: string[];
  /** Whether to ignore empty config files (default: true) */
  ignoreEmptySearchPlaces?: boolean;
  /** Property name(s) to extract from package.json (default: [name]) */
  packageProp?: string | string[];
}

Option Details:

  • packageProp: When searching package.json, extracts configuration from the specified property. Can be a string ('myapp') or nested path (['config', 'myapp']). Defaults to the configuration name.
  • transform: Function called on all results (including null when no config found). Useful for adding defaults or metadata to loaded configurations.
  • searchPlaces: Overrides the default search places entirely. When not provided, uses the default search places based on the configuration name.

Usage Example:

import { lilconfig } from 'lilconfig';
import os from 'os';

const explorer = lilconfig('myapp', {
  stopDir: os.homedir(),
  searchPlaces: [
    'package.json',
    '.myapprc.json',
    '.myapprc.js',
    'myapp.config.js'
  ],
  ignoreEmptySearchPlaces: false,
  packageProp: ['myapp', 'config'],
  cache: true,
  transform: (result) => {
    if (result && result.config) {
      // Add metadata to config
      return {
        ...result,
        config: {
          ...result.config,
          _loadedFrom: result.filepath
        }
      };
    }
    return result;
  }
});

Types

/** Result object returned by search and load operations */
type LilconfigResult = null | {
  /** Path to the configuration file that was found/loaded */
  filepath: string;
  /** The loaded configuration data */
  config: any;
  /** Whether the configuration file was empty */
  isEmpty?: boolean;
};

/** Sync loader function for processing file content */
type LoaderSync = (filepath: string, content: string) => any;

/** Async loader function for processing file content */  
type Loader = LoaderSync | ((filepath: string, content: string) => Promise<any>);

/** Map of file extensions to sync loaders */
type LoadersSync = Record<string, LoaderSync>;

/** Map of file extensions to async loaders */
type Loaders = Record<string, Loader>;

/** Sync transform function for modifying loaded configuration */
type TransformSync = (result: LilconfigResult) => LilconfigResult;

/** Transform function for modifying loaded configuration */
type Transform = TransformSync | ((result: LilconfigResult) => Promise<LilconfigResult>);

Advanced Usage

Custom Loaders

import { lilconfig } from 'lilconfig';
import yaml from 'yaml';

// YAML loader example
function yamlLoader(filepath, content) {
  return yaml.parse(content);
}

const explorer = lilconfig('myapp', {
  loaders: {
    '.yaml': yamlLoader,
    '.yml': yamlLoader,
    // Override default behavior for files with no extension
    noExt: yamlLoader
  }
});

Transform Configuration

import { lilconfig } from 'lilconfig';

const explorer = lilconfig('myapp', {
  transform: (result) => {
    if (!result) return result;
    
    // Add default values
    return {
      ...result,
      config: {
        timeout: 5000,
        retries: 3,
        ...result.config
      }
    };
  }
});

Package.json Configuration

import { lilconfig } from 'lilconfig';

// Extract nested configuration from package.json
const explorer = lilconfig('myapp', {
  packageProp: ['config', 'myapp'] // Looks for package.json.config.myapp
});

// Extract from array of possible properties
const explorerMulti = lilconfig('myapp', {
  packageProp: ['myapp', 'myappConfig'] // Tries myapp first, then myappConfig
});

Error Handling

lilconfig throws specific errors in the following cases:

  • Missing loader: Error('Missing loader for extension "<extension>"') when no loader is configured for a file extension
  • Invalid loader: Error('Loader for extension "<extension>" is not a function: Received <type>.') when a loader is not a function
  • Empty filepath: Error('load must pass a non-empty string') when load() is called with empty filepath
  • Loader validation: Error('No loader specified for extension "<ext>"') when trying to load a file without a configured loader
import { lilconfig } from 'lilconfig';

try {
  const explorer = lilconfig('myapp');
  const result = await explorer.load('');
} catch (error) {
  console.error('Error:', error.message); // "load must pass a non-empty string"
}