c12 1.2.0 → 1.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 CHANGED
@@ -1,11 +1,11 @@
1
- # c12
1
+ # ⚙️ c12
2
2
 
3
3
  [![npm version][npm-version-src]][npm-version-href]
4
4
  [![npm downloads][npm-downloads-src]][npm-downloads-href]
5
- [![Github Actions][github-actions-src]][github-actions-href]
6
5
  [![Codecov][codecov-src]][codecov-href]
6
+ [![License][license-src]][license-href]
7
7
 
8
- > Smart Configuration Loader
8
+ Smart Configuration Loader.
9
9
 
10
10
  ## Features
11
11
 
@@ -13,8 +13,9 @@
13
13
  - RC config support with [unjs/rc9](https://github.com/unjs/rc9)
14
14
  - Multiple sources merged with [unjs/defu](https://github.com/unjs/defu)
15
15
  - `.env` support with [dotenv](https://www.npmjs.com/package/dotenv)
16
- - Support reading config from the nearest `package.json` file
17
- - Support extending nested configurations from multiple local or git sources
16
+ - Reads config from the nearest `package.json` file
17
+ - [Extends configurations](https://github.com/unjs/c12#extending-configuration) from multiple local or git sources
18
+ - Overwrite with [environment-specific configuration](#environment-specific-configuration)
18
19
 
19
20
  ## Usage
20
21
 
@@ -246,11 +247,11 @@ Made with 💛 Published under [MIT License](./LICENSE).
246
247
 
247
248
  <!-- Badges -->
248
249
 
249
- [npm-version-src]: https://img.shields.io/npm/v/c12?style=flat-square
250
+ [npm-version-src]: https://img.shields.io/npm/v/c12?style=flat&colorA=18181B&colorB=F0DB4F
250
251
  [npm-version-href]: https://npmjs.com/package/c12
251
- [npm-downloads-src]: https://img.shields.io/npm/dm/c12?style=flat-square
252
+ [npm-downloads-src]: https://img.shields.io/npm/dm/c12?style=flat&colorA=18181B&colorB=F0DB4F
252
253
  [npm-downloads-href]: https://npmjs.com/package/c12
253
- [github-actions-src]: https://img.shields.io/github/actions/workflow/status/unjs/c12/ci.yml?branch=main&style=flat-square
254
- [github-actions-href]: https://github.com/unjs/c12/actions?query=workflow%3Aci
255
- [codecov-src]: https://img.shields.io/codecov/c/gh/unjs/c12/main?style=flat-square
254
+ [codecov-src]: https://img.shields.io/codecov/c/gh/unjs/c12/main?style=flat&colorA=18181B&colorB=F0DB4F
256
255
  [codecov-href]: https://codecov.io/gh/unjs/c12
256
+ [license-src]: https://img.shields.io/github/license/unjs/c12.svg?style=flat&colorA=18181B&colorB=F0DB4F
257
+ [license-href]: https://github.com/unjs/c12/blob/main/LICENSE
package/dist/index.cjs CHANGED
@@ -10,7 +10,10 @@ const rc9 = require('rc9');
10
10
  const defu = require('defu');
11
11
  const pkgTypes = require('pkg-types');
12
12
 
13
- function _interopNamespaceDefault(e) {
13
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
14
+
15
+ function _interopNamespaceCompat(e) {
16
+ if (e && typeof e === 'object' && 'default' in e) return e;
14
17
  const n = Object.create(null);
15
18
  if (e) {
16
19
  for (const k in e) {
@@ -21,8 +24,9 @@ function _interopNamespaceDefault(e) {
21
24
  return n;
22
25
  }
23
26
 
24
- const dotenv__namespace = /*#__PURE__*/_interopNamespaceDefault(dotenv);
25
- const rc9__namespace = /*#__PURE__*/_interopNamespaceDefault(rc9);
27
+ const dotenv__namespace = /*#__PURE__*/_interopNamespaceCompat(dotenv);
28
+ const createJiti__default = /*#__PURE__*/_interopDefaultCompat(createJiti);
29
+ const rc9__namespace = /*#__PURE__*/_interopNamespaceCompat(rc9);
26
30
 
27
31
  async function setupDotenv(options) {
28
32
  const targetEnvironment = options.env ?? process.env;
@@ -46,7 +50,7 @@ async function loadDotenv(options) {
46
50
  const parsed = dotenv__namespace.parse(await node_fs.promises.readFile(dotenvFile, "utf8"));
47
51
  Object.assign(environment, parsed);
48
52
  }
49
- if (!options.env._applied) {
53
+ if (!options.env?._applied) {
50
54
  Object.assign(environment, options.env);
51
55
  environment._applied = true;
52
56
  }
@@ -67,15 +71,15 @@ function interpolate(target, source = {}, parse = (v) => v) {
67
71
  return parse(
68
72
  // eslint-disable-next-line unicorn/no-array-reduce
69
73
  matches.reduce((newValue, match) => {
70
- const parts = /(.?)\${?([\w:]+)?}?/g.exec(match);
74
+ const parts = /(.?)\${?([\w:]+)?}?/g.exec(match) || [];
71
75
  const prefix = parts[1];
72
76
  let value2, replacePart;
73
77
  if (prefix === "\\") {
74
- replacePart = parts[0];
78
+ replacePart = parts[0] || "";
75
79
  value2 = replacePart.replace("\\$", "$");
76
80
  } else {
77
81
  const key = parts[2];
78
- replacePart = parts[0].slice(prefix.length);
82
+ replacePart = (parts[0] || "").slice(prefix.length);
79
83
  if (parents.includes(key)) {
80
84
  console.warn(
81
85
  `Please avoid recursive environment variables ( loop: ${parents.join(
@@ -108,7 +112,7 @@ async function loadConfig(options) {
108
112
  ...options.extend
109
113
  };
110
114
  }
111
- options.jiti = options.jiti || createJiti(void 0, {
115
+ options.jiti = options.jiti || createJiti__default(void 0, {
112
116
  interopDefault: true,
113
117
  requireCache: false,
114
118
  esmResolve: true,
@@ -258,7 +262,7 @@ async function resolveConfig(source, options, sourceOptions = {}) {
258
262
  const name = gitRepo.replace(/[#/:@\\]/g, "_");
259
263
  const tmpDir = process.env.XDG_CACHE_HOME ? pathe.resolve(process.env.XDG_CACHE_HOME, "c12", name) : pathe.resolve(node_os.homedir(), ".cache/c12", name);
260
264
  if (node_fs.existsSync(tmpDir)) {
261
- await promises.rmdir(tmpDir, { recursive: true });
265
+ await promises.rm(tmpDir, { recursive: true });
262
266
  }
263
267
  const cloned = await downloadTemplate(source, { dir: tmpDir });
264
268
  source = cloned.dir;
@@ -274,7 +278,12 @@ async function resolveConfig(source, options, sourceOptions = {}) {
274
278
  if (isDir) {
275
279
  source = options.configFile;
276
280
  }
277
- const res = { config: void 0, cwd, source, sourceOptions };
281
+ const res = {
282
+ config: void 0,
283
+ cwd,
284
+ source,
285
+ sourceOptions
286
+ };
278
287
  try {
279
288
  res.configFile = options.jiti.resolve(pathe.resolve(cwd, source), {
280
289
  paths: [cwd]
@@ -305,6 +314,11 @@ async function resolveConfig(source, options, sourceOptions = {}) {
305
314
  return res;
306
315
  }
307
316
 
317
+ function createDefineConfig() {
318
+ return (input) => input;
319
+ }
320
+
321
+ exports.createDefineConfig = createDefineConfig;
308
322
  exports.loadConfig = loadConfig;
309
323
  exports.loadDotenv = loadDotenv;
310
324
  exports.setupDotenv = setupDotenv;
package/dist/index.d.ts CHANGED
@@ -37,41 +37,37 @@ declare function setupDotenv(options: DotenvOptions): Promise<Env>;
37
37
  /** Load environment variables into an object. */
38
38
  declare function loadDotenv(options: DotenvOptions): Promise<Env>;
39
39
 
40
- type UserInputConfig = Record<string, any>;
41
40
  interface ConfigLayerMeta {
42
41
  name?: string;
43
42
  [key: string]: any;
44
43
  }
45
- interface C12InputConfig {
46
- $test?: UserInputConfig;
47
- $development?: UserInputConfig;
48
- $production?: UserInputConfig;
49
- $env?: Record<string, UserInputConfig>;
50
- $meta?: ConfigLayerMeta;
51
- }
52
- interface InputConfig extends C12InputConfig, UserInputConfig {
44
+ type UserInputConfig = Record<string, any>;
45
+ interface C12InputConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> {
46
+ $test?: T;
47
+ $development?: T;
48
+ $production?: T;
49
+ $env?: Record<string, T>;
50
+ $meta?: MT;
53
51
  }
54
- interface SourceOptions {
55
- meta?: ConfigLayerMeta;
56
- overrides?: UserInputConfig;
52
+ type InputConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = C12InputConfig<T, MT> & T;
53
+ interface SourceOptions<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> {
54
+ meta?: MT;
55
+ overrides?: T;
57
56
  [key: string]: any;
58
57
  }
59
- interface ConfigLayer<T extends InputConfig = InputConfig> {
58
+ interface ConfigLayer<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> {
60
59
  config: T | null;
61
60
  source?: string;
62
- sourceOptions?: SourceOptions;
63
- meta?: ConfigLayerMeta;
61
+ sourceOptions?: SourceOptions<T, MT>;
62
+ meta?: MT;
64
63
  cwd?: string;
65
64
  configFile?: string;
66
65
  }
67
- interface ResolvedConfig<T extends InputConfig = InputConfig> extends ConfigLayer<T> {
68
- layers?: ConfigLayer<T>[];
66
+ interface ResolvedConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> extends ConfigLayer<T, MT> {
67
+ layers?: ConfigLayer<T, MT>[];
69
68
  cwd?: string;
70
69
  }
71
- interface ResolveConfigOptions {
72
- cwd: string;
73
- }
74
- interface LoadConfigOptions<T extends InputConfig = InputConfig> {
70
+ interface LoadConfigOptions<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> {
75
71
  name?: string;
76
72
  cwd?: string;
77
73
  configFile?: string;
@@ -83,13 +79,16 @@ interface LoadConfigOptions<T extends InputConfig = InputConfig> {
83
79
  defaults?: T;
84
80
  defaultConfig?: T;
85
81
  overrides?: T;
86
- resolve?: (id: string, options: LoadConfigOptions) => null | ResolvedConfig | Promise<ResolvedConfig | null>;
82
+ resolve?: (id: string, options: LoadConfigOptions<T, MT>) => null | undefined | ResolvedConfig<T, MT> | Promise<ResolvedConfig<T, MT> | undefined | null>;
87
83
  jiti?: JITI;
88
84
  jitiOptions?: JITIOptions;
89
85
  extend?: false | {
90
86
  extendKey?: string | string[];
91
87
  };
92
88
  }
93
- declare function loadConfig<T extends InputConfig = InputConfig>(options: LoadConfigOptions<T>): Promise<ResolvedConfig<T>>;
89
+ type DefineConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = (input: InputConfig<T, MT>) => InputConfig<T, MT>;
90
+ declare function createDefineConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(): DefineConfig<T, MT>;
91
+
92
+ declare function loadConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(options: LoadConfigOptions<T, MT>): Promise<ResolvedConfig<T, MT>>;
94
93
 
95
- export { C12InputConfig, ConfigLayer, ConfigLayerMeta, DotenvOptions, Env, InputConfig, LoadConfigOptions, ResolveConfigOptions, ResolvedConfig, SourceOptions, UserInputConfig, loadConfig, loadDotenv, setupDotenv };
94
+ export { C12InputConfig, ConfigLayer, ConfigLayerMeta, DefineConfig, DotenvOptions, Env, InputConfig, LoadConfigOptions, ResolvedConfig, SourceOptions, UserInputConfig, createDefineConfig, loadConfig, loadDotenv, setupDotenv };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { existsSync, promises } from 'node:fs';
2
2
  import { resolve, extname, dirname } from 'pathe';
3
3
  import * as dotenv from 'dotenv';
4
- import { rmdir } from 'node:fs/promises';
4
+ import { rm } from 'node:fs/promises';
5
5
  import { homedir } from 'node:os';
6
6
  import createJiti from 'jiti';
7
7
  import * as rc9 from 'rc9';
@@ -30,7 +30,7 @@ async function loadDotenv(options) {
30
30
  const parsed = dotenv.parse(await promises.readFile(dotenvFile, "utf8"));
31
31
  Object.assign(environment, parsed);
32
32
  }
33
- if (!options.env._applied) {
33
+ if (!options.env?._applied) {
34
34
  Object.assign(environment, options.env);
35
35
  environment._applied = true;
36
36
  }
@@ -51,15 +51,15 @@ function interpolate(target, source = {}, parse = (v) => v) {
51
51
  return parse(
52
52
  // eslint-disable-next-line unicorn/no-array-reduce
53
53
  matches.reduce((newValue, match) => {
54
- const parts = /(.?)\${?([\w:]+)?}?/g.exec(match);
54
+ const parts = /(.?)\${?([\w:]+)?}?/g.exec(match) || [];
55
55
  const prefix = parts[1];
56
56
  let value2, replacePart;
57
57
  if (prefix === "\\") {
58
- replacePart = parts[0];
58
+ replacePart = parts[0] || "";
59
59
  value2 = replacePart.replace("\\$", "$");
60
60
  } else {
61
61
  const key = parts[2];
62
- replacePart = parts[0].slice(prefix.length);
62
+ replacePart = (parts[0] || "").slice(prefix.length);
63
63
  if (parents.includes(key)) {
64
64
  console.warn(
65
65
  `Please avoid recursive environment variables ( loop: ${parents.join(
@@ -242,7 +242,7 @@ async function resolveConfig(source, options, sourceOptions = {}) {
242
242
  const name = gitRepo.replace(/[#/:@\\]/g, "_");
243
243
  const tmpDir = process.env.XDG_CACHE_HOME ? resolve(process.env.XDG_CACHE_HOME, "c12", name) : resolve(homedir(), ".cache/c12", name);
244
244
  if (existsSync(tmpDir)) {
245
- await rmdir(tmpDir, { recursive: true });
245
+ await rm(tmpDir, { recursive: true });
246
246
  }
247
247
  const cloned = await downloadTemplate(source, { dir: tmpDir });
248
248
  source = cloned.dir;
@@ -258,7 +258,12 @@ async function resolveConfig(source, options, sourceOptions = {}) {
258
258
  if (isDir) {
259
259
  source = options.configFile;
260
260
  }
261
- const res = { config: void 0, cwd, source, sourceOptions };
261
+ const res = {
262
+ config: void 0,
263
+ cwd,
264
+ source,
265
+ sourceOptions
266
+ };
262
267
  try {
263
268
  res.configFile = options.jiti.resolve(resolve(cwd, source), {
264
269
  paths: [cwd]
@@ -289,4 +294,8 @@ async function resolveConfig(source, options, sourceOptions = {}) {
289
294
  return res;
290
295
  }
291
296
 
292
- export { loadConfig, loadDotenv, setupDotenv };
297
+ function createDefineConfig() {
298
+ return (input) => input;
299
+ }
300
+
301
+ export { createDefineConfig, loadConfig, loadDotenv, setupDotenv };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c12",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Smart Config Loader",
5
5
  "repository": "unjs/c12",
6
6
  "license": "MIT",
@@ -26,27 +26,29 @@
26
26
  "lint:fix": "eslint --ext .ts,.js,.mjs,.cjs . --fix && prettier -w src test",
27
27
  "prepack": "unbuild",
28
28
  "release": "changelogen --release && npm publish && git push --follow-tags",
29
- "test": "vitest run --coverage"
29
+ "test": "vitest run --coverage && pnpm test:types",
30
+ "test:types": "tsc --noEmit"
30
31
  },
31
32
  "dependencies": {
32
33
  "defu": "^6.1.2",
33
34
  "dotenv": "^16.0.3",
34
35
  "giget": "^1.1.2",
35
- "jiti": "^1.17.2",
36
+ "jiti": "^1.18.2",
36
37
  "mlly": "^1.2.0",
37
38
  "pathe": "^1.1.0",
38
39
  "pkg-types": "^1.0.2",
39
- "rc9": "^2.0.1"
40
+ "rc9": "^2.1.0"
40
41
  },
41
42
  "devDependencies": {
42
- "@vitest/coverage-c8": "^0.29.2",
43
- "changelogen": "^0.5.1",
44
- "eslint": "^8.36.0",
43
+ "@vitest/coverage-c8": "^0.30.1",
44
+ "changelogen": "^0.5.3",
45
+ "eslint": "^8.38.0",
45
46
  "eslint-config-unjs": "^0.1.0",
46
- "prettier": "^2.8.4",
47
- "typescript": "^4.9.5",
48
- "unbuild": "^1.1.2",
49
- "vitest": "^0.29.2"
47
+ "expect-type": "^0.15.0",
48
+ "prettier": "^2.8.7",
49
+ "typescript": "^5.0.4",
50
+ "unbuild": "^1.2.1",
51
+ "vitest": "^0.30.1"
50
52
  },
51
- "packageManager": "pnpm@7.29.1"
52
- }
53
+ "packageManager": "pnpm@8.2.0"
54
+ }