A zero-dependency alternative to cosmiconfig for loading configuration files
npx @tessl/cli install tessl/npm-lilconfig@3.1.00
# Lilconfig
1
2
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.
3
4
## Package Information
5
6
- **Package Name**: lilconfig
7
- **Package Type**: npm
8
- **Language**: JavaScript with TypeScript definitions
9
- **Installation**: `npm install lilconfig`
10
11
## Core Imports
12
13
```javascript
14
import { lilconfig, lilconfigSync, defaultLoaders, defaultLoadersSync } from 'lilconfig';
15
```
16
17
For CommonJS:
18
19
```javascript
20
const { lilconfig, lilconfigSync, defaultLoaders, defaultLoadersSync } = require('lilconfig');
21
```
22
23
## Basic Usage
24
25
```javascript
26
import { lilconfig, lilconfigSync } from 'lilconfig';
27
28
// Async usage
29
const explorer = lilconfig('myapp');
30
const result = await explorer.search();
31
32
if (result) {
33
console.log('Config found at:', result.filepath);
34
console.log('Config contents:', result.config);
35
}
36
37
// Sync usage
38
const explorerSync = lilconfigSync('myapp');
39
const resultSync = explorerSync.search();
40
41
if (resultSync) {
42
console.log('Config found at:', resultSync.filepath);
43
console.log('Config contents:', resultSync.config);
44
}
45
```
46
47
## Architecture
48
49
Lilconfig is built around several key components:
50
51
- **Search Strategy**: Systematic file discovery from current directory upward to stop directory
52
- **Loader System**: Pluggable file loading mechanism supporting different formats (.js, .json, .cjs, .mjs)
53
- **Caching Layer**: Optional caching for both search and load operations to improve performance
54
- **Transform Pipeline**: Optional transformation of loaded configuration data
55
- **Package.json Integration**: Special handling for configuration stored in package.json files
56
57
## Capabilities
58
59
### Configuration Searching
60
61
Creates a configuration explorer that can search for configuration files in standard locations.
62
63
```javascript { .api }
64
/**
65
* Creates an async configuration explorer
66
* @param name - The configuration name to search for (e.g., 'myapp')
67
* @param options - Optional configuration options
68
* @returns AsyncSearcher instance with search and load methods
69
*/
70
function lilconfig(name: string, options?: Partial<Options>): AsyncSearcher;
71
72
/**
73
* Creates a sync configuration explorer
74
* @param name - The configuration name to search for (e.g., 'myapp')
75
* @param options - Optional configuration options
76
* @returns SyncSearcher instance with search and load methods
77
*/
78
function lilconfigSync(name: string, options?: OptionsSync): SyncSearcher;
79
```
80
81
**Default Search Places:**
82
83
For a configuration named `myapp`, lilconfig searches for files in this order:
84
85
```javascript
86
// Async version (lilconfig) searches for:
87
[
88
'package.json', // Looks for myapp property
89
'.myapprc.json',
90
'.myapprc.js',
91
'.myapprc.cjs',
92
'.myapprc.mjs', // Only in async mode
93
'.config/myapprc',
94
'.config/myapprc.json',
95
'.config/myapprc.js',
96
'.config/myapprc.cjs',
97
'.config/myapprc.mjs', // Only in async mode
98
'myapp.config.js',
99
'myapp.config.cjs',
100
'myapp.config.mjs' // Only in async mode
101
]
102
103
// Sync version (lilconfigSync) excludes .mjs files due to module loading limitations
104
```
105
106
**Usage Examples:**
107
108
```javascript
109
import { lilconfig, lilconfigSync } from 'lilconfig';
110
111
// Search for 'myapp' configuration files
112
const explorer = lilconfig('myapp');
113
const result = await explorer.search('/some/start/path');
114
115
// Search with custom options
116
const explorerWithOptions = lilconfig('myapp', {
117
searchPlaces: ['package.json', '.myapprc.js', 'myapp.config.js'],
118
stopDir: '/home/user',
119
ignoreEmptySearchPlaces: false
120
});
121
122
const customResult = await explorerWithOptions.search();
123
```
124
125
### Async Searcher Interface
126
127
The object returned by `lilconfig()` providing async search and load capabilities.
128
129
```javascript { .api }
130
interface AsyncSearcher {
131
/**
132
* Search for configuration files starting from specified directory
133
* @param searchFrom - Directory to start searching from (defaults to process.cwd())
134
* @returns Promise resolving to LilconfigResult or null if not found
135
*/
136
search(searchFrom?: string): Promise<LilconfigResult>;
137
138
/**
139
* Load configuration from a specific file path
140
* @param filepath - Path to configuration file to load
141
* @returns Promise resolving to LilconfigResult
142
*/
143
load(filepath: string): Promise<LilconfigResult>;
144
145
/** Clear the load cache */
146
clearLoadCache(): void;
147
148
/** Clear the search cache */
149
clearSearchCache(): void;
150
151
/** Clear both load and search caches */
152
clearCaches(): void;
153
}
154
```
155
156
### Sync Searcher Interface
157
158
The object returned by `lilconfigSync()` providing synchronous search and load capabilities.
159
160
```javascript { .api }
161
interface SyncSearcher {
162
/**
163
* Search for configuration files starting from specified directory
164
* @param searchFrom - Directory to start searching from (defaults to process.cwd())
165
* @returns LilconfigResult or null if not found
166
*/
167
search(searchFrom?: string): LilconfigResult;
168
169
/**
170
* Load configuration from a specific file path
171
* @param filepath - Path to configuration file to load
172
* @returns LilconfigResult
173
*/
174
load(filepath: string): LilconfigResult;
175
176
/** Clear the load cache */
177
clearLoadCache(): void;
178
179
/** Clear the search cache */
180
clearSearchCache(): void;
181
182
/** Clear both load and search caches */
183
clearCaches(): void;
184
}
185
```
186
187
### Default Loaders
188
189
Pre-configured loaders for common file types.
190
191
```javascript { .api }
192
/** Default async loaders supporting .js, .mjs, .cjs, .json files and files with no extension */
193
const defaultLoaders: Loaders;
194
195
/** Default sync loaders supporting .js, .json, .cjs files and files with no extension (no .mjs support) */
196
const defaultLoadersSync: LoadersSync;
197
```
198
199
**Loader Resolution:**
200
- File extensions are mapped to loaders (e.g., `.js` → JavaScript loader, `.json` → JSON parser)
201
- Files without extensions use the special `noExt` loader key
202
- Default `noExt` loader treats files as JSON
203
- Custom loaders can override any extension or the `noExt` behavior
204
205
**Usage Example:**
206
207
```javascript
208
import { lilconfig, defaultLoaders } from 'lilconfig';
209
210
// Extend default loaders with custom loader
211
const explorer = lilconfig('myapp', {
212
loaders: {
213
...defaultLoaders,
214
'.yaml': (filepath, content) => require('yaml').parse(content)
215
}
216
});
217
```
218
219
### Custom Configuration Options
220
221
Configuration options for customizing search behavior and file processing.
222
223
```javascript { .api }
224
interface Options {
225
/** Custom loaders for different file extensions */
226
loaders?: Loaders;
227
/** Transform function to modify loaded configuration */
228
transform?: Transform;
229
/** Enable/disable caching (default: true) */
230
cache?: boolean;
231
/** Directory to stop searching at (default: os.homedir()) */
232
stopDir?: string;
233
/** Custom list of places to search for config files */
234
searchPlaces?: string[];
235
/** Whether to ignore empty config files (default: true) */
236
ignoreEmptySearchPlaces?: boolean;
237
/** Property name(s) to extract from package.json (default: [name]) */
238
packageProp?: string | string[];
239
}
240
241
interface OptionsSync {
242
/** Custom sync loaders for different file extensions */
243
loaders?: LoadersSync;
244
/** Sync transform function to modify loaded configuration */
245
transform?: TransformSync;
246
/** Enable/disable caching (default: true) */
247
cache?: boolean;
248
/** Directory to stop searching at (default: os.homedir()) */
249
stopDir?: string;
250
/** Custom list of places to search for config files */
251
searchPlaces?: string[];
252
/** Whether to ignore empty config files (default: true) */
253
ignoreEmptySearchPlaces?: boolean;
254
/** Property name(s) to extract from package.json (default: [name]) */
255
packageProp?: string | string[];
256
}
257
```
258
259
**Option Details:**
260
261
- **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.
262
- **transform**: Function called on all results (including `null` when no config found). Useful for adding defaults or metadata to loaded configurations.
263
- **searchPlaces**: Overrides the default search places entirely. When not provided, uses the default search places based on the configuration name.
264
265
**Usage Example:**
266
267
```javascript
268
import { lilconfig } from 'lilconfig';
269
import os from 'os';
270
271
const explorer = lilconfig('myapp', {
272
stopDir: os.homedir(),
273
searchPlaces: [
274
'package.json',
275
'.myapprc.json',
276
'.myapprc.js',
277
'myapp.config.js'
278
],
279
ignoreEmptySearchPlaces: false,
280
packageProp: ['myapp', 'config'],
281
cache: true,
282
transform: (result) => {
283
if (result && result.config) {
284
// Add metadata to config
285
return {
286
...result,
287
config: {
288
...result.config,
289
_loadedFrom: result.filepath
290
}
291
};
292
}
293
return result;
294
}
295
});
296
```
297
298
## Types
299
300
```javascript { .api }
301
/** Result object returned by search and load operations */
302
type LilconfigResult = null | {
303
/** Path to the configuration file that was found/loaded */
304
filepath: string;
305
/** The loaded configuration data */
306
config: any;
307
/** Whether the configuration file was empty */
308
isEmpty?: boolean;
309
};
310
311
/** Sync loader function for processing file content */
312
type LoaderSync = (filepath: string, content: string) => any;
313
314
/** Async loader function for processing file content */
315
type Loader = LoaderSync | ((filepath: string, content: string) => Promise<any>);
316
317
/** Map of file extensions to sync loaders */
318
type LoadersSync = Record<string, LoaderSync>;
319
320
/** Map of file extensions to async loaders */
321
type Loaders = Record<string, Loader>;
322
323
/** Sync transform function for modifying loaded configuration */
324
type TransformSync = (result: LilconfigResult) => LilconfigResult;
325
326
/** Transform function for modifying loaded configuration */
327
type Transform = TransformSync | ((result: LilconfigResult) => Promise<LilconfigResult>);
328
```
329
330
## Advanced Usage
331
332
### Custom Loaders
333
334
```javascript
335
import { lilconfig } from 'lilconfig';
336
import yaml from 'yaml';
337
338
// YAML loader example
339
function yamlLoader(filepath, content) {
340
return yaml.parse(content);
341
}
342
343
const explorer = lilconfig('myapp', {
344
loaders: {
345
'.yaml': yamlLoader,
346
'.yml': yamlLoader,
347
// Override default behavior for files with no extension
348
noExt: yamlLoader
349
}
350
});
351
```
352
353
### Transform Configuration
354
355
```javascript
356
import { lilconfig } from 'lilconfig';
357
358
const explorer = lilconfig('myapp', {
359
transform: (result) => {
360
if (!result) return result;
361
362
// Add default values
363
return {
364
...result,
365
config: {
366
timeout: 5000,
367
retries: 3,
368
...result.config
369
}
370
};
371
}
372
});
373
```
374
375
### Package.json Configuration
376
377
```javascript
378
import { lilconfig } from 'lilconfig';
379
380
// Extract nested configuration from package.json
381
const explorer = lilconfig('myapp', {
382
packageProp: ['config', 'myapp'] // Looks for package.json.config.myapp
383
});
384
385
// Extract from array of possible properties
386
const explorerMulti = lilconfig('myapp', {
387
packageProp: ['myapp', 'myappConfig'] // Tries myapp first, then myappConfig
388
});
389
```
390
391
## Error Handling
392
393
lilconfig throws specific errors in the following cases:
394
395
- **Missing loader**: `Error('Missing loader for extension "<extension>"')` when no loader is configured for a file extension
396
- **Invalid loader**: `Error('Loader for extension "<extension>" is not a function: Received <type>.')` when a loader is not a function
397
- **Empty filepath**: `Error('load must pass a non-empty string')` when load() is called with empty filepath
398
- **Loader validation**: `Error('No loader specified for extension "<ext>"')` when trying to load a file without a configured loader
399
400
```javascript
401
import { lilconfig } from 'lilconfig';
402
403
try {
404
const explorer = lilconfig('myapp');
405
const result = await explorer.load('');
406
} catch (error) {
407
console.error('Error:', error.message); // "load must pass a non-empty string"
408
}
409
```