c12 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -47
- package/dist/index.d.mts +14 -4
- package/dist/index.mjs +2 -2
- package/dist/shared/{c12.BXpNC6YI.mjs → c12.Bzgyhsy6.mjs} +34 -17
- package/dist/update.mjs +1 -1
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ c12 (pronounced as /siːtwelv/, like c-twelve) is a smart configuration loader.
|
|
|
14
14
|
|
|
15
15
|
- `.js`, `.ts`, `.mjs`, `.cjs`, `.mts`, `.cts` `.json` config loader with [unjs/jiti](https://jiti.unjs.io)
|
|
16
16
|
- `.jsonc`, `.json5`, `.yaml`, `.yml`, `.toml` config loader with [unjs/confbox](https://confbox.unjs.io)
|
|
17
|
-
- `.config/` directory support
|
|
17
|
+
- `.config/` directory support ([config dir proposal](https://github.com/pi0/config-dir))
|
|
18
18
|
- `.rc` config support with [unjs/rc9](https://github.com/unjs/rc9)
|
|
19
19
|
- `.env` support with [dotenv](https://www.npmjs.com/package/dotenv)
|
|
20
20
|
- Multiple sources merged with [unjs/defu](https://github.com/unjs/defu)
|
|
@@ -26,43 +26,24 @@ c12 (pronounced as /siːtwelv/, like c-twelve) is a smart configuration loader.
|
|
|
26
26
|
|
|
27
27
|
## 🦴 Used by
|
|
28
28
|
|
|
29
|
-
- [Nuxt](https://nuxt.com/)
|
|
30
|
-
- [Nitro](https://nitro.build/)
|
|
31
|
-
- [Unbuild](https://unbuild.unjs.io)
|
|
32
|
-
- [Automd](https://automd.unjs.io)
|
|
33
|
-
- [Changelogen](https://changelogen.unjs.io)
|
|
34
|
-
- [RemixKit](https://github.com/jrestall/remix-kit)
|
|
35
29
|
- [Hey API](https://github.com/hey-api/openapi-ts)
|
|
36
|
-
- [
|
|
30
|
+
- [Kysely](https://github.com/kysely-org/kysely-ctl)
|
|
31
|
+
- [Nitro](https://nitro.build/)
|
|
32
|
+
- [Nuxt](https://nuxt.com/)
|
|
33
|
+
- [Prisma](https://github.com/prisma/prisma)
|
|
34
|
+
- [Trigger.dev](https://github.com/triggerdotdev/trigger.dev)
|
|
35
|
+
- [UnJS](https://github.com/unjs)
|
|
36
|
+
- [WXT](https://github.com/wxt-dev/wxt)
|
|
37
37
|
|
|
38
38
|
## Usage
|
|
39
39
|
|
|
40
40
|
Install package:
|
|
41
41
|
|
|
42
|
-
<!-- automd:pm-install -->
|
|
43
|
-
|
|
44
42
|
```sh
|
|
45
43
|
# ✨ Auto-detect
|
|
46
44
|
npx nypm install c12
|
|
47
|
-
|
|
48
|
-
# npm
|
|
49
|
-
npm install c12
|
|
50
|
-
|
|
51
|
-
# yarn
|
|
52
|
-
yarn add c12
|
|
53
|
-
|
|
54
|
-
# pnpm
|
|
55
|
-
pnpm install c12
|
|
56
|
-
|
|
57
|
-
# bun
|
|
58
|
-
bun install c12
|
|
59
|
-
|
|
60
|
-
# deno
|
|
61
|
-
deno install c12
|
|
62
45
|
```
|
|
63
46
|
|
|
64
|
-
<!-- /automd -->
|
|
65
|
-
|
|
66
47
|
Import:
|
|
67
48
|
|
|
68
49
|
```js
|
|
@@ -121,7 +102,42 @@ Load RC config from the workspace directory and the user's home directory. Only
|
|
|
121
102
|
|
|
122
103
|
### `dotenv`
|
|
123
104
|
|
|
124
|
-
Loads `.env` file
|
|
105
|
+
Loads `.env` file when `true` or an options object is passed. It is disabled by default.
|
|
106
|
+
|
|
107
|
+
Supports loading multiple files that extend eachother in left-to-right order when a `fileName`s array of relative/absolute paths is passed in the options object.
|
|
108
|
+
|
|
109
|
+
**Example:**
|
|
110
|
+
|
|
111
|
+
```ini
|
|
112
|
+
# .env
|
|
113
|
+
CONNECTION_POOL_MAX="10"
|
|
114
|
+
DATABASE_URL="<...rds...>"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
```ini
|
|
118
|
+
# .env.local
|
|
119
|
+
DATABASE_URL="<...localhost...>"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
export default {
|
|
124
|
+
connectionPoolMax: process.env.CONNECTION_POOL_MAX,
|
|
125
|
+
databaseURL: process.env.DATABASE_URL,
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { loadConfig } from "c12";
|
|
131
|
+
|
|
132
|
+
const config = await loadConfig({
|
|
133
|
+
dotenv: {
|
|
134
|
+
fileName: [".env", ".env.local"],
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
console.log(config.config.connectionPoolMax); // "10"
|
|
139
|
+
console.log(config.config.databaseURL); // "<...localhost...>"
|
|
140
|
+
```
|
|
125
141
|
|
|
126
142
|
### `packageJson`
|
|
127
143
|
|
|
@@ -171,10 +187,17 @@ Environment name used for [environment specific configuration](#environment-spec
|
|
|
171
187
|
|
|
172
188
|
The default is `process.env.NODE_ENV`. You can set `envName` to `false` or an empty string to disable the feature.
|
|
173
189
|
|
|
190
|
+
### `context`
|
|
191
|
+
|
|
192
|
+
Context object passed to dynamic config functions.
|
|
193
|
+
|
|
174
194
|
### `resolve`
|
|
175
195
|
|
|
176
196
|
You can define a custom function that resolves the config.
|
|
177
197
|
|
|
198
|
+
### `configFileRequired`
|
|
199
|
+
|
|
200
|
+
If this option is set to `true`, loader fails if the main config file does not exists.
|
|
178
201
|
|
|
179
202
|
## Extending configuration
|
|
180
203
|
|
|
@@ -386,30 +409,11 @@ Update or create a new configuration files.
|
|
|
386
409
|
|
|
387
410
|
Add `magicast` peer dependency:
|
|
388
411
|
|
|
389
|
-
<!-- automd:pm-install name="magicast" dev -->
|
|
390
|
-
|
|
391
412
|
```sh
|
|
392
413
|
# ✨ Auto-detect
|
|
393
414
|
npx nypm install -D magicast
|
|
394
|
-
|
|
395
|
-
# npm
|
|
396
|
-
npm install -D magicast
|
|
397
|
-
|
|
398
|
-
# yarn
|
|
399
|
-
yarn add -D magicast
|
|
400
|
-
|
|
401
|
-
# pnpm
|
|
402
|
-
pnpm install -D magicast
|
|
403
|
-
|
|
404
|
-
# bun
|
|
405
|
-
bun install -D magicast
|
|
406
|
-
|
|
407
|
-
# deno
|
|
408
|
-
deno install --dev magicast
|
|
409
415
|
```
|
|
410
416
|
|
|
411
|
-
<!-- /automd -->
|
|
412
|
-
|
|
413
417
|
Import util from `c12/update`
|
|
414
418
|
|
|
415
419
|
```js
|
|
@@ -430,6 +434,28 @@ const { configFile, created } = await updateConfig({
|
|
|
430
434
|
console.log(`Config file ${created ? "created" : "updated"} in ${configFile}`);
|
|
431
435
|
```
|
|
432
436
|
|
|
437
|
+
## Configuration functions
|
|
438
|
+
|
|
439
|
+
You can use a function to define your configuration dynamically based on context.
|
|
440
|
+
|
|
441
|
+
```ts
|
|
442
|
+
// config.ts
|
|
443
|
+
export default (ctx) => {
|
|
444
|
+
return {
|
|
445
|
+
apiUrl: ctx?.dev ? "http://localhost:3000" : "https://api.example.com",
|
|
446
|
+
};
|
|
447
|
+
};
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
```ts
|
|
451
|
+
// Usage
|
|
452
|
+
import { loadConfig } from "c12";
|
|
453
|
+
|
|
454
|
+
const config = await loadConfig({
|
|
455
|
+
context: { dev: true },
|
|
456
|
+
});
|
|
457
|
+
```
|
|
458
|
+
|
|
433
459
|
## Contribution
|
|
434
460
|
|
|
435
461
|
<details>
|
package/dist/index.d.mts
CHANGED
|
@@ -6,13 +6,16 @@ import { diff } from 'ohash/utils';
|
|
|
6
6
|
interface DotenvOptions {
|
|
7
7
|
/**
|
|
8
8
|
* The project root directory (either absolute or relative to the current working directory).
|
|
9
|
+
*
|
|
10
|
+
* Defaults to `options.cwd` in `loadConfig` context, or `process.cwd()` when used as standalone.
|
|
9
11
|
*/
|
|
10
|
-
cwd
|
|
12
|
+
cwd?: string;
|
|
11
13
|
/**
|
|
12
|
-
* What file to look in for environment variables (either absolute or relative
|
|
14
|
+
* What file or files to look in for environment variables (either absolute or relative
|
|
13
15
|
* to the current working directory). For example, `.env`.
|
|
16
|
+
* With the array type, the order enforce the env loading priority (last one overrides).
|
|
14
17
|
*/
|
|
15
|
-
fileName?: string;
|
|
18
|
+
fileName?: string | string[];
|
|
16
19
|
/**
|
|
17
20
|
* Whether to interpolate variables within .env.
|
|
18
21
|
*
|
|
@@ -92,8 +95,12 @@ interface ResolvedConfig<T extends UserInputConfig = UserInputConfig, MT extends
|
|
|
92
95
|
config: T;
|
|
93
96
|
layers?: ConfigLayer<T, MT>[];
|
|
94
97
|
cwd?: string;
|
|
98
|
+
_configFile?: string;
|
|
95
99
|
}
|
|
96
100
|
type ConfigSource = "overrides" | "main" | "rc" | "packageJson" | "defaultConfig";
|
|
101
|
+
interface ConfigFunctionContext {
|
|
102
|
+
[key: string]: any;
|
|
103
|
+
}
|
|
97
104
|
interface ResolvableConfigContext<T extends UserInputConfig = UserInputConfig> {
|
|
98
105
|
configs: Record<ConfigSource, T | null | undefined>;
|
|
99
106
|
rawConfigs: Record<ConfigSource, ResolvableConfig<T> | null | undefined>;
|
|
@@ -113,6 +120,8 @@ interface LoadConfigOptions<T extends UserInputConfig = UserInputConfig, MT exte
|
|
|
113
120
|
defaultConfig?: ResolvableConfig<T>;
|
|
114
121
|
overrides?: ResolvableConfig<T>;
|
|
115
122
|
omit$Keys?: boolean;
|
|
123
|
+
/** Context passed to config functions */
|
|
124
|
+
context?: ConfigFunctionContext;
|
|
116
125
|
resolve?: (id: string, options: LoadConfigOptions<T, MT>) => null | undefined | ResolvedConfig<T, MT> | Promise<ResolvedConfig<T, MT> | undefined | null>;
|
|
117
126
|
jiti?: Jiti;
|
|
118
127
|
jitiOptions?: JitiOptions;
|
|
@@ -121,6 +130,7 @@ interface LoadConfigOptions<T extends UserInputConfig = UserInputConfig, MT exte
|
|
|
121
130
|
extend?: false | {
|
|
122
131
|
extendKey?: string | string[];
|
|
123
132
|
};
|
|
133
|
+
configFileRequired?: boolean;
|
|
124
134
|
}
|
|
125
135
|
type DefineConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = (input: InputConfig<T, MT>) => InputConfig<T, MT>;
|
|
126
136
|
declare function createDefineConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(): DefineConfig<T, MT>;
|
|
@@ -154,4 +164,4 @@ interface WatchConfigOptions<T extends UserInputConfig = UserInputConfig, MT ext
|
|
|
154
164
|
declare function watchConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(options: WatchConfigOptions<T, MT>): Promise<ConfigWatcher<T, MT>>;
|
|
155
165
|
|
|
156
166
|
export { SUPPORTED_EXTENSIONS, createDefineConfig, loadConfig, loadDotenv, setupDotenv, watchConfig };
|
|
157
|
-
export type { C12InputConfig, ConfigLayer, ConfigLayerMeta, ConfigSource, ConfigWatcher, DefineConfig, DotenvOptions, Env, InputConfig, LoadConfigOptions, ResolvableConfig, ResolvableConfigContext, ResolvedConfig, SourceOptions, UserInputConfig, WatchConfigOptions };
|
|
167
|
+
export type { C12InputConfig, ConfigFunctionContext, ConfigLayer, ConfigLayerMeta, ConfigSource, ConfigWatcher, DefineConfig, DotenvOptions, Env, InputConfig, LoadConfigOptions, ResolvableConfig, ResolvableConfigContext, ResolvedConfig, SourceOptions, UserInputConfig, WatchConfigOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { l as loadConfig, S as SUPPORTED_EXTENSIONS } from './shared/c12.
|
|
2
|
-
export { a as loadDotenv, s as setupDotenv } from './shared/c12.
|
|
1
|
+
import { l as loadConfig, S as SUPPORTED_EXTENSIONS } from './shared/c12.Bzgyhsy6.mjs';
|
|
2
|
+
export { a as loadDotenv, s as setupDotenv } from './shared/c12.Bzgyhsy6.mjs';
|
|
3
3
|
import { debounce } from 'perfect-debounce';
|
|
4
4
|
import { resolve } from 'pathe';
|
|
5
5
|
import 'node:fs';
|
|
@@ -31,10 +31,16 @@ async function setupDotenv(options) {
|
|
|
31
31
|
}
|
|
32
32
|
async function loadDotenv(options) {
|
|
33
33
|
const environment = /* @__PURE__ */ Object.create(null);
|
|
34
|
-
const
|
|
34
|
+
const cwd = resolve(options.cwd || ".");
|
|
35
|
+
const _fileName = options.fileName || ".env";
|
|
36
|
+
const dotenvFiles = typeof _fileName === "string" ? [_fileName] : _fileName;
|
|
35
37
|
const dotenvVars = getDotEnvVars(options.env || {});
|
|
36
38
|
Object.assign(environment, options.env);
|
|
37
|
-
|
|
39
|
+
for (const file of dotenvFiles) {
|
|
40
|
+
const dotenvFile = resolve(cwd, file);
|
|
41
|
+
if (!statSync(dotenvFile, { throwIfNoEntry: false })?.isFile()) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
38
44
|
const parsed = dotenv.parse(await promises.readFile(dotenvFile, "utf8"));
|
|
39
45
|
for (const key in parsed) {
|
|
40
46
|
if (key in environment && !dotenvVars.has(key)) {
|
|
@@ -144,7 +150,8 @@ async function loadConfig(options) {
|
|
|
144
150
|
config: {},
|
|
145
151
|
cwd: options.cwd,
|
|
146
152
|
configFile: resolve(options.cwd, options.configFile),
|
|
147
|
-
layers: []
|
|
153
|
+
layers: [],
|
|
154
|
+
_configFile: void 0
|
|
148
155
|
};
|
|
149
156
|
const rawConfigs = {
|
|
150
157
|
overrides: options.overrides,
|
|
@@ -163,6 +170,7 @@ async function loadConfig(options) {
|
|
|
163
170
|
if (_mainConfig.configFile) {
|
|
164
171
|
rawConfigs.main = _mainConfig.config;
|
|
165
172
|
r.configFile = _mainConfig.configFile;
|
|
173
|
+
r._configFile = _mainConfig._configFile;
|
|
166
174
|
}
|
|
167
175
|
if (_mainConfig.meta) {
|
|
168
176
|
r.meta = _mainConfig.meta;
|
|
@@ -194,18 +202,22 @@ async function loadConfig(options) {
|
|
|
194
202
|
const value = rawConfigs[key];
|
|
195
203
|
configs[key] = await (typeof value === "function" ? value({ configs, rawConfigs }) : value);
|
|
196
204
|
}
|
|
197
|
-
|
|
198
|
-
configs.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
205
|
+
if (Array.isArray(configs.main)) {
|
|
206
|
+
r.config = configs.main;
|
|
207
|
+
} else {
|
|
208
|
+
r.config = _merger(
|
|
209
|
+
configs.overrides,
|
|
210
|
+
configs.main,
|
|
211
|
+
configs.rc,
|
|
212
|
+
configs.packageJson,
|
|
213
|
+
configs.defaultConfig
|
|
214
|
+
);
|
|
215
|
+
if (options.extend) {
|
|
216
|
+
await extendConfig(r.config, options);
|
|
217
|
+
r.layers = r.config._layers;
|
|
218
|
+
delete r.config._layers;
|
|
219
|
+
r.config = _merger(r.config, ...r.layers.map((e) => e.config));
|
|
220
|
+
}
|
|
209
221
|
}
|
|
210
222
|
const baseLayers = [
|
|
211
223
|
configs.overrides && {
|
|
@@ -231,6 +243,9 @@ async function loadConfig(options) {
|
|
|
231
243
|
}
|
|
232
244
|
}
|
|
233
245
|
}
|
|
246
|
+
if (options.configFileRequired && !r._configFile) {
|
|
247
|
+
throw new Error(`Required config (${r.configFile}) cannot be resolved.`);
|
|
248
|
+
}
|
|
234
249
|
return r;
|
|
235
250
|
}
|
|
236
251
|
async function extendConfig(config, options) {
|
|
@@ -295,6 +310,7 @@ const GIGET_PREFIXES = [
|
|
|
295
310
|
];
|
|
296
311
|
const NPM_PACKAGE_RE = /^(@[\da-z~-][\d._a-z~-]*\/)?[\da-z~-][\d._a-z~-]*($|\/.*)/;
|
|
297
312
|
async function resolveConfig(source, options, sourceOptions = {}) {
|
|
313
|
+
const originalSource = source;
|
|
298
314
|
if (options.resolve) {
|
|
299
315
|
const res2 = await options.resolve(source, options);
|
|
300
316
|
if (res2) {
|
|
@@ -352,10 +368,11 @@ async function resolveConfig(source, options, sourceOptions = {}) {
|
|
|
352
368
|
res.configFile = tryResolve(resolve(cwd, source), options) || tryResolve(
|
|
353
369
|
resolve(cwd, ".config", source.replace(/\.config$/, "")),
|
|
354
370
|
options
|
|
355
|
-
) || tryResolve(resolve(cwd, ".config", source), options) || source;
|
|
371
|
+
) || tryResolve(resolve(cwd, ".config", source), options) || tryResolve(resolve(options.cwd || cwd, originalSource), options) || source;
|
|
356
372
|
if (!existsSync(res.configFile)) {
|
|
357
373
|
return res;
|
|
358
374
|
}
|
|
375
|
+
res._configFile = res.configFile;
|
|
359
376
|
const configFileExt = extname(res.configFile) || "";
|
|
360
377
|
if (configFileExt in ASYNC_LOADERS) {
|
|
361
378
|
const asyncLoader = await ASYNC_LOADERS[configFileExt]();
|
|
@@ -367,7 +384,7 @@ async function resolveConfig(source, options, sourceOptions = {}) {
|
|
|
367
384
|
});
|
|
368
385
|
}
|
|
369
386
|
if (typeof res.config === "function") {
|
|
370
|
-
res.config = await res.config();
|
|
387
|
+
res.config = await res.config(options.context);
|
|
371
388
|
}
|
|
372
389
|
if (options.envName) {
|
|
373
390
|
const envConfig = {
|
package/dist/update.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveModulePath } from 'exsolve';
|
|
2
|
-
import { S as SUPPORTED_EXTENSIONS } from './shared/c12.
|
|
2
|
+
import { S as SUPPORTED_EXTENSIONS } from './shared/c12.Bzgyhsy6.mjs';
|
|
3
3
|
import { join, normalize } from 'pathe';
|
|
4
4
|
import { mkdir, writeFile, readFile } from 'node:fs/promises';
|
|
5
5
|
import { dirname, extname } from 'node:path';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "c12",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Smart Config Loader",
|
|
5
5
|
"repository": "unjs/c12",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,28 +34,28 @@
|
|
|
34
34
|
"chokidar": "^4.0.3",
|
|
35
35
|
"confbox": "^0.2.2",
|
|
36
36
|
"defu": "^6.1.4",
|
|
37
|
-
"dotenv": "^
|
|
37
|
+
"dotenv": "^17.2.2",
|
|
38
38
|
"exsolve": "^1.0.7",
|
|
39
39
|
"giget": "^2.0.0",
|
|
40
|
-
"jiti": "^2.
|
|
40
|
+
"jiti": "^2.5.1",
|
|
41
41
|
"ohash": "^2.0.11",
|
|
42
42
|
"pathe": "^2.0.3",
|
|
43
|
-
"perfect-debounce": "^
|
|
44
|
-
"pkg-types": "^2.
|
|
43
|
+
"perfect-debounce": "^2.0.0",
|
|
44
|
+
"pkg-types": "^2.3.0",
|
|
45
45
|
"rc9": "^2.1.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@types/node": "^24.0
|
|
48
|
+
"@types/node": "^24.4.0",
|
|
49
49
|
"@vitest/coverage-v8": "^3.2.4",
|
|
50
50
|
"automd": "^0.4.0",
|
|
51
51
|
"changelogen": "^0.6.2",
|
|
52
|
-
"eslint": "^9.
|
|
52
|
+
"eslint": "^9.35.0",
|
|
53
53
|
"eslint-config-unjs": "^0.5.0",
|
|
54
54
|
"expect-type": "^1.2.2",
|
|
55
55
|
"magicast": "^0.3.5",
|
|
56
56
|
"prettier": "^3.6.2",
|
|
57
|
-
"typescript": "^5.
|
|
58
|
-
"unbuild": "^3.
|
|
57
|
+
"typescript": "^5.9.2",
|
|
58
|
+
"unbuild": "^3.6.1",
|
|
59
59
|
"vitest": "^3.2.4"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"optional": true
|
|
67
67
|
}
|
|
68
68
|
},
|
|
69
|
-
"packageManager": "pnpm@10.
|
|
69
|
+
"packageManager": "pnpm@10.16.1"
|
|
70
70
|
}
|