c12 1.8.0 → 1.9.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 CHANGED
@@ -12,8 +12,8 @@ c12 (pronounced as /siːtwelv/, like c-twelve) is a smart configuration loader.
12
12
 
13
13
  ## ✅ Features
14
14
 
15
- - `.js`, `.ts`, `.cjs`, `.mjs` config loader with [unjs/jiti](https://github.com/unjs/jiti)
16
- - `.json`, `.json5` and `.jsonc` config support
15
+ - `.js`, `.ts`, `.mjs`, `.cjs`, `.mts`, `.cts` `.json` config loader with [unjs/jiti](https://jiti.unjs.io)
16
+ - `.jsonc`, `.json5`, `.yaml`, `.yml`, `.toml` config loader with [unjs/confbox](https://confbox.unjs.io)
17
17
  - `.config/` directory support following [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)
package/dist/index.cjs CHANGED
@@ -10,7 +10,6 @@ const rc9 = require('rc9');
10
10
  const defu = require('defu');
11
11
  const ohash = require('ohash');
12
12
  const pkgTypes = require('pkg-types');
13
- const chokidar = require('chokidar');
14
13
  const perfectDebounce = require('perfect-debounce');
15
14
 
16
15
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
@@ -104,6 +103,29 @@ function interpolate(target, source = {}, parse = (v) => v) {
104
103
  }
105
104
 
106
105
  const _normalize = (p) => p?.replace(/\\/g, "/");
106
+ const ASYNC_LOADERS = {
107
+ ".yaml": () => import('confbox/yaml').then((r) => r.parseYAML),
108
+ ".yml": () => import('confbox/yaml').then((r) => r.parseYAML),
109
+ ".jsonc": () => import('confbox/jsonc').then((r) => r.parseJSONC),
110
+ ".json5": () => import('confbox/json5').then((r) => r.parseJSON5),
111
+ ".toml": () => import('confbox/toml').then((r) => r.parseTOML)
112
+ };
113
+ const SUPPORTED_EXTENSIONS = [
114
+ // with jiti
115
+ ".js",
116
+ ".ts",
117
+ ".mjs",
118
+ ".cjs",
119
+ ".mts",
120
+ ".cts",
121
+ ".json",
122
+ // with confbox
123
+ ".jsonc",
124
+ ".json5",
125
+ ".yaml",
126
+ ".yml",
127
+ ".toml"
128
+ ];
107
129
  async function loadConfig(options) {
108
130
  options.cwd = pathe.resolve(process.cwd(), options.cwd || ".");
109
131
  options.name = options.name || "config";
@@ -120,17 +142,7 @@ async function loadConfig(options) {
120
142
  interopDefault: true,
121
143
  requireCache: false,
122
144
  esmResolve: true,
123
- extensions: [
124
- ".js",
125
- ".mjs",
126
- ".cjs",
127
- ".ts",
128
- ".mts",
129
- ".cts",
130
- ".json",
131
- ".jsonc",
132
- ".json5"
133
- ],
145
+ extensions: [...SUPPORTED_EXTENSIONS],
134
146
  ...options.jitiOptions
135
147
  });
136
148
  const r = {
@@ -151,24 +163,17 @@ async function loadConfig(options) {
151
163
  }
152
164
  const configRC = {};
153
165
  if (options.rcFile) {
166
+ const rcSources = [];
167
+ rcSources.push(rc9__namespace.read({ name: options.rcFile, dir: options.cwd }));
154
168
  if (options.globalRc) {
155
- Object.assign(
156
- configRC,
157
- rc9__namespace.readUser({ name: options.rcFile, dir: options.cwd })
158
- );
159
169
  const workspaceDir = await pkgTypes.findWorkspaceDir(options.cwd).catch(() => {
160
170
  });
161
171
  if (workspaceDir) {
162
- Object.assign(
163
- configRC,
164
- rc9__namespace.read({ name: options.rcFile, dir: workspaceDir })
165
- );
172
+ rcSources.push(rc9__namespace.read({ name: options.rcFile, dir: workspaceDir }));
166
173
  }
174
+ rcSources.push(rc9__namespace.readUser({ name: options.rcFile, dir: options.cwd }));
167
175
  }
168
- Object.assign(
169
- configRC,
170
- rc9__namespace.read({ name: options.rcFile, dir: options.cwd })
171
- );
176
+ Object.assign(configRC, defu.defu({}, ...rcSources));
172
177
  }
173
178
  const pkgJson = {};
174
179
  if (options.packageJson) {
@@ -335,14 +340,11 @@ async function resolveConfig(source, options, sourceOptions = {}) {
335
340
  if (!node_fs.existsSync(res.configFile)) {
336
341
  return res;
337
342
  }
338
- if (res.configFile.endsWith(".jsonc")) {
339
- const { parse } = await import('jsonc-parser');
340
- res.config = parse(await promises.readFile(res.configFile, "utf8"));
341
- } else if (res.configFile.endsWith(".json5")) {
342
- const parse = await import('json5').then(
343
- (m) => m.parse || m.default?.parse || m.default
344
- );
345
- res.config = parse(await promises.readFile(res.configFile, "utf8"));
343
+ const configFileExt = pathe.extname(res.configFile) || "";
344
+ if (configFileExt in ASYNC_LOADERS) {
345
+ const asyncLoader = await ASYNC_LOADERS[configFileExt]();
346
+ const contents = await promises.readFile(res.configFile, "utf8");
347
+ res.config = asyncLoader(contents);
346
348
  } else {
347
349
  res.config = options.jiti(res.configFile);
348
350
  }
@@ -384,9 +386,15 @@ async function watchConfig(options) {
384
386
  const watchingFiles = [
385
387
  ...new Set(
386
388
  (config.layers || []).filter((l) => l.cwd).flatMap((l) => [
387
- ...["ts", "js", "mjs", "cjs", "cts", "mts", "json"].map(
388
- (ext) => pathe.resolve(l.cwd, configFileName + "." + ext)
389
- ),
389
+ ...SUPPORTED_EXTENSIONS.flatMap((ext) => [
390
+ pathe.resolve(l.cwd, configFileName + ext),
391
+ pathe.resolve(l.cwd, ".config", configFileName + ext),
392
+ pathe.resolve(
393
+ l.cwd,
394
+ ".config",
395
+ configFileName.replace(/\.config$/, "") + ext
396
+ )
397
+ ]),
390
398
  l.source && pathe.resolve(l.cwd, l.source),
391
399
  // TODO: Support watching rc from home and workspace
392
400
  options.rcFile && pathe.resolve(
@@ -397,7 +405,8 @@ async function watchConfig(options) {
397
405
  ]).filter(Boolean)
398
406
  )
399
407
  ];
400
- const _fswatcher = chokidar.watch(watchingFiles, {
408
+ const watch = await import('chokidar').then((r) => r.watch || r.default || r);
409
+ const _fswatcher = watch(watchingFiles, {
401
410
  ignoreInitial: true,
402
411
  ...options.chokidarOptions
403
412
  });
@@ -451,6 +460,7 @@ async function watchConfig(options) {
451
460
  });
452
461
  }
453
462
 
463
+ exports.SUPPORTED_EXTENSIONS = SUPPORTED_EXTENSIONS;
454
464
  exports.createDefineConfig = createDefineConfig;
455
465
  exports.loadConfig = loadConfig;
456
466
  exports.loadDotenv = loadDotenv;
package/dist/index.d.cts CHANGED
@@ -96,6 +96,7 @@ interface LoadConfigOptions<T extends UserInputConfig = UserInputConfig, MT exte
96
96
  type DefineConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = (input: InputConfig<T, MT>) => InputConfig<T, MT>;
97
97
  declare function createDefineConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(): DefineConfig<T, MT>;
98
98
 
99
+ declare const SUPPORTED_EXTENSIONS: readonly [".js", ".ts", ".mjs", ".cjs", ".mts", ".cts", ".json", ".jsonc", ".json5", ".yaml", ".yml", ".toml"];
99
100
  declare function loadConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(options: LoadConfigOptions<T, MT>): Promise<ResolvedConfig<T, MT>>;
100
101
 
101
102
  type ConfigWatcher<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = ResolvedConfig<T, MT> & {
@@ -122,4 +123,4 @@ interface WatchConfigOptions<T extends UserInputConfig = UserInputConfig, MT ext
122
123
  }
123
124
  declare function watchConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(options: WatchConfigOptions<T, MT>): Promise<ConfigWatcher<T, MT>>;
124
125
 
125
- export { type C12InputConfig, type ConfigLayer, type ConfigLayerMeta, type ConfigWatcher, type DefineConfig, type DotenvOptions, type Env, type InputConfig, type LoadConfigOptions, type ResolvedConfig, type SourceOptions, type UserInputConfig, type WatchConfigOptions, createDefineConfig, loadConfig, loadDotenv, setupDotenv, watchConfig };
126
+ export { type C12InputConfig, type ConfigLayer, type ConfigLayerMeta, type ConfigWatcher, type DefineConfig, type DotenvOptions, type Env, type InputConfig, type LoadConfigOptions, type ResolvedConfig, SUPPORTED_EXTENSIONS, type SourceOptions, type UserInputConfig, type WatchConfigOptions, createDefineConfig, loadConfig, loadDotenv, setupDotenv, watchConfig };
package/dist/index.d.mts CHANGED
@@ -96,6 +96,7 @@ interface LoadConfigOptions<T extends UserInputConfig = UserInputConfig, MT exte
96
96
  type DefineConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = (input: InputConfig<T, MT>) => InputConfig<T, MT>;
97
97
  declare function createDefineConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(): DefineConfig<T, MT>;
98
98
 
99
+ declare const SUPPORTED_EXTENSIONS: readonly [".js", ".ts", ".mjs", ".cjs", ".mts", ".cts", ".json", ".jsonc", ".json5", ".yaml", ".yml", ".toml"];
99
100
  declare function loadConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(options: LoadConfigOptions<T, MT>): Promise<ResolvedConfig<T, MT>>;
100
101
 
101
102
  type ConfigWatcher<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = ResolvedConfig<T, MT> & {
@@ -122,4 +123,4 @@ interface WatchConfigOptions<T extends UserInputConfig = UserInputConfig, MT ext
122
123
  }
123
124
  declare function watchConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(options: WatchConfigOptions<T, MT>): Promise<ConfigWatcher<T, MT>>;
124
125
 
125
- export { type C12InputConfig, type ConfigLayer, type ConfigLayerMeta, type ConfigWatcher, type DefineConfig, type DotenvOptions, type Env, type InputConfig, type LoadConfigOptions, type ResolvedConfig, type SourceOptions, type UserInputConfig, type WatchConfigOptions, createDefineConfig, loadConfig, loadDotenv, setupDotenv, watchConfig };
126
+ export { type C12InputConfig, type ConfigLayer, type ConfigLayerMeta, type ConfigWatcher, type DefineConfig, type DotenvOptions, type Env, type InputConfig, type LoadConfigOptions, type ResolvedConfig, SUPPORTED_EXTENSIONS, type SourceOptions, type UserInputConfig, type WatchConfigOptions, createDefineConfig, loadConfig, loadDotenv, setupDotenv, watchConfig };
package/dist/index.d.ts CHANGED
@@ -96,6 +96,7 @@ interface LoadConfigOptions<T extends UserInputConfig = UserInputConfig, MT exte
96
96
  type DefineConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = (input: InputConfig<T, MT>) => InputConfig<T, MT>;
97
97
  declare function createDefineConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(): DefineConfig<T, MT>;
98
98
 
99
+ declare const SUPPORTED_EXTENSIONS: readonly [".js", ".ts", ".mjs", ".cjs", ".mts", ".cts", ".json", ".jsonc", ".json5", ".yaml", ".yml", ".toml"];
99
100
  declare function loadConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(options: LoadConfigOptions<T, MT>): Promise<ResolvedConfig<T, MT>>;
100
101
 
101
102
  type ConfigWatcher<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = ResolvedConfig<T, MT> & {
@@ -122,4 +123,4 @@ interface WatchConfigOptions<T extends UserInputConfig = UserInputConfig, MT ext
122
123
  }
123
124
  declare function watchConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(options: WatchConfigOptions<T, MT>): Promise<ConfigWatcher<T, MT>>;
124
125
 
125
- export { type C12InputConfig, type ConfigLayer, type ConfigLayerMeta, type ConfigWatcher, type DefineConfig, type DotenvOptions, type Env, type InputConfig, type LoadConfigOptions, type ResolvedConfig, type SourceOptions, type UserInputConfig, type WatchConfigOptions, createDefineConfig, loadConfig, loadDotenv, setupDotenv, watchConfig };
126
+ export { type C12InputConfig, type ConfigLayer, type ConfigLayerMeta, type ConfigWatcher, type DefineConfig, type DotenvOptions, type Env, type InputConfig, type LoadConfigOptions, type ResolvedConfig, SUPPORTED_EXTENSIONS, type SourceOptions, type UserInputConfig, type WatchConfigOptions, createDefineConfig, loadConfig, loadDotenv, setupDotenv, watchConfig };
package/dist/index.mjs CHANGED
@@ -8,7 +8,6 @@ import * as rc9 from 'rc9';
8
8
  import { defu } from 'defu';
9
9
  import { hash, diff } from 'ohash';
10
10
  import { findWorkspaceDir, readPackageJSON } from 'pkg-types';
11
- import { watch } from 'chokidar';
12
11
  import { debounce } from 'perfect-debounce';
13
12
 
14
13
  async function setupDotenv(options) {
@@ -84,6 +83,29 @@ function interpolate(target, source = {}, parse = (v) => v) {
84
83
  }
85
84
 
86
85
  const _normalize = (p) => p?.replace(/\\/g, "/");
86
+ const ASYNC_LOADERS = {
87
+ ".yaml": () => import('confbox/yaml').then((r) => r.parseYAML),
88
+ ".yml": () => import('confbox/yaml').then((r) => r.parseYAML),
89
+ ".jsonc": () => import('confbox/jsonc').then((r) => r.parseJSONC),
90
+ ".json5": () => import('confbox/json5').then((r) => r.parseJSON5),
91
+ ".toml": () => import('confbox/toml').then((r) => r.parseTOML)
92
+ };
93
+ const SUPPORTED_EXTENSIONS = [
94
+ // with jiti
95
+ ".js",
96
+ ".ts",
97
+ ".mjs",
98
+ ".cjs",
99
+ ".mts",
100
+ ".cts",
101
+ ".json",
102
+ // with confbox
103
+ ".jsonc",
104
+ ".json5",
105
+ ".yaml",
106
+ ".yml",
107
+ ".toml"
108
+ ];
87
109
  async function loadConfig(options) {
88
110
  options.cwd = resolve(process.cwd(), options.cwd || ".");
89
111
  options.name = options.name || "config";
@@ -100,17 +122,7 @@ async function loadConfig(options) {
100
122
  interopDefault: true,
101
123
  requireCache: false,
102
124
  esmResolve: true,
103
- extensions: [
104
- ".js",
105
- ".mjs",
106
- ".cjs",
107
- ".ts",
108
- ".mts",
109
- ".cts",
110
- ".json",
111
- ".jsonc",
112
- ".json5"
113
- ],
125
+ extensions: [...SUPPORTED_EXTENSIONS],
114
126
  ...options.jitiOptions
115
127
  });
116
128
  const r = {
@@ -131,24 +143,17 @@ async function loadConfig(options) {
131
143
  }
132
144
  const configRC = {};
133
145
  if (options.rcFile) {
146
+ const rcSources = [];
147
+ rcSources.push(rc9.read({ name: options.rcFile, dir: options.cwd }));
134
148
  if (options.globalRc) {
135
- Object.assign(
136
- configRC,
137
- rc9.readUser({ name: options.rcFile, dir: options.cwd })
138
- );
139
149
  const workspaceDir = await findWorkspaceDir(options.cwd).catch(() => {
140
150
  });
141
151
  if (workspaceDir) {
142
- Object.assign(
143
- configRC,
144
- rc9.read({ name: options.rcFile, dir: workspaceDir })
145
- );
152
+ rcSources.push(rc9.read({ name: options.rcFile, dir: workspaceDir }));
146
153
  }
154
+ rcSources.push(rc9.readUser({ name: options.rcFile, dir: options.cwd }));
147
155
  }
148
- Object.assign(
149
- configRC,
150
- rc9.read({ name: options.rcFile, dir: options.cwd })
151
- );
156
+ Object.assign(configRC, defu({}, ...rcSources));
152
157
  }
153
158
  const pkgJson = {};
154
159
  if (options.packageJson) {
@@ -315,14 +320,11 @@ async function resolveConfig(source, options, sourceOptions = {}) {
315
320
  if (!existsSync(res.configFile)) {
316
321
  return res;
317
322
  }
318
- if (res.configFile.endsWith(".jsonc")) {
319
- const { parse } = await import('jsonc-parser');
320
- res.config = parse(await readFile(res.configFile, "utf8"));
321
- } else if (res.configFile.endsWith(".json5")) {
322
- const parse = await import('json5').then(
323
- (m) => m.parse || m.default?.parse || m.default
324
- );
325
- res.config = parse(await readFile(res.configFile, "utf8"));
323
+ const configFileExt = extname(res.configFile) || "";
324
+ if (configFileExt in ASYNC_LOADERS) {
325
+ const asyncLoader = await ASYNC_LOADERS[configFileExt]();
326
+ const contents = await readFile(res.configFile, "utf8");
327
+ res.config = asyncLoader(contents);
326
328
  } else {
327
329
  res.config = options.jiti(res.configFile);
328
330
  }
@@ -364,9 +366,15 @@ async function watchConfig(options) {
364
366
  const watchingFiles = [
365
367
  ...new Set(
366
368
  (config.layers || []).filter((l) => l.cwd).flatMap((l) => [
367
- ...["ts", "js", "mjs", "cjs", "cts", "mts", "json"].map(
368
- (ext) => resolve(l.cwd, configFileName + "." + ext)
369
- ),
369
+ ...SUPPORTED_EXTENSIONS.flatMap((ext) => [
370
+ resolve(l.cwd, configFileName + ext),
371
+ resolve(l.cwd, ".config", configFileName + ext),
372
+ resolve(
373
+ l.cwd,
374
+ ".config",
375
+ configFileName.replace(/\.config$/, "") + ext
376
+ )
377
+ ]),
370
378
  l.source && resolve(l.cwd, l.source),
371
379
  // TODO: Support watching rc from home and workspace
372
380
  options.rcFile && resolve(
@@ -377,6 +385,7 @@ async function watchConfig(options) {
377
385
  ]).filter(Boolean)
378
386
  )
379
387
  ];
388
+ const watch = await import('chokidar').then((r) => r.watch || r.default || r);
380
389
  const _fswatcher = watch(watchingFiles, {
381
390
  ignoreInitial: true,
382
391
  ...options.chokidarOptions
@@ -431,4 +440,4 @@ async function watchConfig(options) {
431
440
  });
432
441
  }
433
442
 
434
- export { createDefineConfig, loadConfig, loadDotenv, setupDotenv, watchConfig };
443
+ export { SUPPORTED_EXTENSIONS, createDefineConfig, loadConfig, loadDotenv, setupDotenv, watchConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c12",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Smart Config Loader",
5
5
  "repository": "unjs/c12",
6
6
  "license": "MIT",
@@ -31,12 +31,11 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "chokidar": "^3.5.3",
34
+ "confbox": "^0.1.3",
34
35
  "defu": "^6.1.4",
35
36
  "dotenv": "^16.3.2",
36
37
  "giget": "^1.2.1",
37
38
  "jiti": "^1.21.0",
38
- "json5": "^2.2.3",
39
- "jsonc-parser": "^3.2.1",
40
39
  "mlly": "^1.5.0",
41
40
  "ohash": "^1.1.3",
42
41
  "pathe": "^1.1.2",