c12 3.3.3 → 4.0.0-beta.1

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,7 +12,7 @@ c12 (pronounced as /siːtwelv/, like c-twelve) is a smart configuration loader.
12
12
 
13
13
  ## ✅ Features
14
14
 
15
- - `.js`, `.ts`, `.mjs`, `.cjs`, `.mts`, `.cts` `.json` config loader with [unjs/jiti](https://jiti.unjs.io)
15
+ - `.js`, `.ts`, `.mjs`, `.cjs`, `.mts`, `.cts` `.json` config loader with customizable import or [unjs/jiti](https://jiti.unjs.io) fallback.
16
16
  - `.jsonc`, `.json5`, `.yaml`, `.yml`, `.toml` config loader with [unjs/confbox](https://confbox.unjs.io)
17
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)
@@ -163,13 +163,33 @@ Specify override configuration. It has the **highest** priority and is applied *
163
163
 
164
164
  Exclude environment-specific and built-in keys start with `$` in the resolved config. The default is `false`.
165
165
 
166
- ### `jiti`
166
+ ### `import`
167
167
 
168
- Custom [unjs/jiti](https://github.com/unjs/jiti) instance used to import configuration files.
168
+ Custom import function used to load configuration files. By default, c12 uses native `import()` with [unjs/jiti](https://github.com/unjs/jiti) as fallback.
169
169
 
170
- ### `jitiOptions`
170
+ **Example:** Using a custom [jiti](https://github.com/unjs/jiti) instance with options:
171
171
 
172
- Custom [unjs/jiti](https://github.com/unjs/jiti) options to import configuration files.
172
+ ```js
173
+ import { createJiti } from "jiti";
174
+
175
+ const jiti = createJiti(import.meta.url, { /* jiti options */ });
176
+
177
+ const { config } = await loadConfig({
178
+ import: (id) => jiti.import(id),
179
+ });
180
+ ```
181
+
182
+ ### `resolveModule`
183
+
184
+ Custom resolver for picking which export to use from the loaded module. Default: `(mod) => mod.default || mod`.
185
+
186
+ **Example:** Using a named export:
187
+
188
+ ```js
189
+ const { config } = await loadConfig({
190
+ resolveModule: (mod) => mod.myConfig,
191
+ });
192
+ ```
173
193
 
174
194
  ### `giget`
175
195
 
@@ -364,6 +384,14 @@ export default {
364
384
 
365
385
  you can use `watchConfig` instead of `loadConfig` to load config and watch for changes, add and removals in all expected configuration paths and auto reload with new config.
366
386
 
387
+ > [!NOTE]
388
+ > Watching requires the [`chokidar`](https://github.com/paulmillr/chokidar) peer dependency to be installed.
389
+ >
390
+ > ```sh
391
+ > # ✨ Auto-detect
392
+ > npx nypm install chokidar
393
+ > ```
394
+
367
395
  ### Lifecycle hooks
368
396
 
369
397
  - `onWatch`: This function is always called when config is updated, added, or removed before attempting to reload the config.
@@ -0,0 +1,33 @@
1
+ # Licenses of Bundled Dependencies
2
+
3
+ The published artifact additionally contains code with the following licenses:
4
+ MIT
5
+
6
+ # Bundled Dependencies
7
+
8
+ ## ohash, perfect-debounce
9
+
10
+ License: MIT
11
+ Repositories: https://github.com/unjs/ohash, https://github.com/unjs/perfect-debounce
12
+
13
+ > MIT License
14
+ >
15
+ > Copyright (c) Pooya Parsa <pooya@pi0.io>
16
+ >
17
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
18
+ > of this software and associated documentation files (the "Software"), to deal
19
+ > in the Software without restriction, including without limitation the rights
20
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21
+ > copies of the Software, and to permit persons to whom the Software is
22
+ > furnished to do so, subject to the following conditions:
23
+ >
24
+ > The above copyright notice and this permission notice shall be included in all
25
+ > copies or substantial portions of the Software.
26
+ >
27
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33
+ > SOFTWARE.
@@ -0,0 +1,30 @@
1
+ //#region node_modules/.pnpm/ohash@2.0.11/node_modules/ohash/dist/utils/index.d.mts
2
+ /**
3
+ * Calculates the difference between two objects and returns a list of differences.
4
+ *
5
+ * @param {any} obj1 - The first object to compare.
6
+ * @param {any} obj2 - The second object to compare.
7
+ * @param {HashOptions} [opts={}] - Configuration options for hashing the objects. See {@link HashOptions}.
8
+ * @returns {DiffEntry[]} An array with the differences between the two objects.
9
+ */
10
+ declare function diff(obj1: any, obj2: any): DiffEntry[];
11
+ declare class DiffEntry {
12
+ key: string;
13
+ type: "changed" | "added" | "removed";
14
+ newValue: DiffHashedObject;
15
+ oldValue?: DiffHashedObject | undefined;
16
+ constructor(key: string, type: "changed" | "added" | "removed", newValue: DiffHashedObject, oldValue?: DiffHashedObject | undefined);
17
+ toString(): string;
18
+ toJSON(): string;
19
+ }
20
+ declare class DiffHashedObject {
21
+ key: string;
22
+ value: any;
23
+ hash?: string | undefined;
24
+ props?: Record<string, DiffHashedObject> | undefined;
25
+ constructor(key: string, value: any, hash?: string | undefined, props?: Record<string, DiffHashedObject> | undefined);
26
+ toString(): string;
27
+ toJSON(): string;
28
+ }
29
+ //#endregion
30
+ export { diff as t };
@@ -0,0 +1,182 @@
1
+ import "node:module";
2
+ import { createHash } from "node:crypto";
3
+ var __defProp = Object.defineProperty;
4
+ var __exportAll = (all, no_symbols) => {
5
+ let target = {};
6
+ for (var name in all) __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true
9
+ });
10
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
11
+ return target;
12
+ };
13
+ function serialize(o) {
14
+ return typeof o == "string" ? `'${o}'` : new c().serialize(o);
15
+ }
16
+ const c = /* @__PURE__ */ function() {
17
+ class o {
18
+ #t = /* @__PURE__ */ new Map();
19
+ compare(t, r) {
20
+ const e = typeof t, n = typeof r;
21
+ return e === "string" && n === "string" ? t.localeCompare(r) : e === "number" && n === "number" ? t - r : String.prototype.localeCompare.call(this.serialize(t, true), this.serialize(r, true));
22
+ }
23
+ serialize(t, r) {
24
+ if (t === null) return "null";
25
+ switch (typeof t) {
26
+ case "string": return r ? t : `'${t}'`;
27
+ case "bigint": return `${t}n`;
28
+ case "object": return this.$object(t);
29
+ case "function": return this.$function(t);
30
+ }
31
+ return String(t);
32
+ }
33
+ serializeObject(t) {
34
+ const r = Object.prototype.toString.call(t);
35
+ if (r !== "[object Object]") return this.serializeBuiltInType(r.length < 10 ? `unknown:${r}` : r.slice(8, -1), t);
36
+ const e = t.constructor, n = e === Object || e === void 0 ? "" : e.name;
37
+ if (n !== "" && globalThis[n] === e) return this.serializeBuiltInType(n, t);
38
+ if (typeof t.toJSON == "function") {
39
+ const i = t.toJSON();
40
+ return n + (i !== null && typeof i == "object" ? this.$object(i) : `(${this.serialize(i)})`);
41
+ }
42
+ return this.serializeObjectEntries(n, Object.entries(t));
43
+ }
44
+ serializeBuiltInType(t, r) {
45
+ const e = this["$" + t];
46
+ if (e) return e.call(this, r);
47
+ if (typeof r?.entries == "function") return this.serializeObjectEntries(t, r.entries());
48
+ throw new Error(`Cannot serialize ${t}`);
49
+ }
50
+ serializeObjectEntries(t, r) {
51
+ const e = Array.from(r).sort((i, a) => this.compare(i[0], a[0]));
52
+ let n = `${t}{`;
53
+ for (let i = 0; i < e.length; i++) {
54
+ const [a, l] = e[i];
55
+ n += `${this.serialize(a, true)}:${this.serialize(l)}`, i < e.length - 1 && (n += ",");
56
+ }
57
+ return n + "}";
58
+ }
59
+ $object(t) {
60
+ let r = this.#t.get(t);
61
+ return r === void 0 && (this.#t.set(t, `#${this.#t.size}`), r = this.serializeObject(t), this.#t.set(t, r)), r;
62
+ }
63
+ $function(t) {
64
+ const r = Function.prototype.toString.call(t);
65
+ return r.slice(-15) === "[native code] }" ? `${t.name || ""}()[native]` : `${t.name}(${t.length})${r.replace(/\s*\n\s*/g, "")}`;
66
+ }
67
+ $Array(t) {
68
+ let r = "[";
69
+ for (let e = 0; e < t.length; e++) r += this.serialize(t[e]), e < t.length - 1 && (r += ",");
70
+ return r + "]";
71
+ }
72
+ $Date(t) {
73
+ try {
74
+ return `Date(${t.toISOString()})`;
75
+ } catch {
76
+ return "Date(null)";
77
+ }
78
+ }
79
+ $ArrayBuffer(t) {
80
+ return `ArrayBuffer[${new Uint8Array(t).join(",")}]`;
81
+ }
82
+ $Set(t) {
83
+ return `Set${this.$Array(Array.from(t).sort((r, e) => this.compare(r, e)))}`;
84
+ }
85
+ $Map(t) {
86
+ return this.serializeObjectEntries("Map", t.entries());
87
+ }
88
+ }
89
+ for (const s of [
90
+ "Error",
91
+ "RegExp",
92
+ "URL"
93
+ ]) o.prototype["$" + s] = function(t) {
94
+ return `${s}(${t})`;
95
+ };
96
+ for (const s of [
97
+ "Int8Array",
98
+ "Uint8Array",
99
+ "Uint8ClampedArray",
100
+ "Int16Array",
101
+ "Uint16Array",
102
+ "Int32Array",
103
+ "Uint32Array",
104
+ "Float32Array",
105
+ "Float64Array"
106
+ ]) o.prototype["$" + s] = function(t) {
107
+ return `${s}[${t.join(",")}]`;
108
+ };
109
+ for (const s of ["BigInt64Array", "BigUint64Array"]) o.prototype["$" + s] = function(t) {
110
+ return `${s}[${t.join("n,")}${t.length > 0 ? "n" : ""}]`;
111
+ };
112
+ return o;
113
+ }();
114
+ const e = globalThis.process?.getBuiltinModule?.("crypto")?.hash, r = "sha256", s = "base64url";
115
+ function digest(t) {
116
+ if (e) return e(r, t, s);
117
+ const o = createHash(r).update(t);
118
+ return globalThis.process?.versions?.webcontainer ? o.digest().toString(s) : o.digest(s);
119
+ }
120
+ var dist_exports = /* @__PURE__ */ __exportAll({ digest: () => digest });
121
+ var utils_exports = /* @__PURE__ */ __exportAll({ diff: () => diff });
122
+ function diff(obj1, obj2) {
123
+ return _diff(_toHashedObject(obj1), _toHashedObject(obj2));
124
+ }
125
+ function _diff(h1, h2) {
126
+ const diffs = [];
127
+ const allProps = /* @__PURE__ */ new Set([...Object.keys(h1.props || {}), ...Object.keys(h2.props || {})]);
128
+ if (h1.props && h2.props) for (const prop of allProps) {
129
+ const p1 = h1.props[prop];
130
+ const p2 = h2.props[prop];
131
+ if (p1 && p2) diffs.push(..._diff(h1.props?.[prop], h2.props?.[prop]));
132
+ else if (p1 || p2) diffs.push(new DiffEntry((p2 || p1).key, p1 ? "removed" : "added", p2, p1));
133
+ }
134
+ if (allProps.size === 0 && h1.hash !== h2.hash) diffs.push(new DiffEntry((h2 || h1).key, "changed", h2, h1));
135
+ return diffs;
136
+ }
137
+ function _toHashedObject(obj, key = "") {
138
+ if (obj && typeof obj !== "object") return new DiffHashedObject(key, obj, serialize(obj));
139
+ const props = {};
140
+ const hashes = [];
141
+ for (const _key in obj) {
142
+ props[_key] = _toHashedObject(obj[_key], key ? `${key}.${_key}` : _key);
143
+ hashes.push(props[_key].hash);
144
+ }
145
+ return new DiffHashedObject(key, obj, `{${hashes.join(":")}}`, props);
146
+ }
147
+ var DiffEntry = class {
148
+ constructor(key, type, newValue, oldValue) {
149
+ this.key = key;
150
+ this.type = type;
151
+ this.newValue = newValue;
152
+ this.oldValue = oldValue;
153
+ }
154
+ toString() {
155
+ return this.toJSON();
156
+ }
157
+ toJSON() {
158
+ switch (this.type) {
159
+ case "added": return `Added \`${this.key}\``;
160
+ case "removed": return `Removed \`${this.key}\``;
161
+ case "changed": return `Changed \`${this.key}\` from \`${this.oldValue?.toString() || "-"}\` to \`${this.newValue.toString()}\``;
162
+ }
163
+ }
164
+ };
165
+ var DiffHashedObject = class {
166
+ constructor(key, value, hash, props) {
167
+ this.key = key;
168
+ this.value = value;
169
+ this.hash = hash;
170
+ this.props = props;
171
+ }
172
+ toString() {
173
+ if (this.props) return `{${Object.keys(this.props).join(",")}}`;
174
+ else return JSON.stringify(this.value);
175
+ }
176
+ toJSON() {
177
+ const k = this.key || ".";
178
+ if (this.props) return `${k}({${Object.keys(this.props).join(",")}})`;
179
+ return `${k}(${this.value})`;
180
+ }
181
+ };
182
+ export { dist_exports as n, utils_exports as t };
@@ -0,0 +1,68 @@
1
+ const DEBOUNCE_DEFAULTS = { trailing: true };
2
+ function debounce(fn, wait = 25, options = {}) {
3
+ options = {
4
+ ...DEBOUNCE_DEFAULTS,
5
+ ...options
6
+ };
7
+ if (!Number.isFinite(wait)) throw new TypeError("Expected `wait` to be a finite number");
8
+ let leadingValue;
9
+ let timeout;
10
+ let resolveList = [];
11
+ let currentPromise;
12
+ let trailingArgs;
13
+ const applyFn = (_this, args) => {
14
+ currentPromise = _applyPromised(fn, _this, args);
15
+ currentPromise.finally(() => {
16
+ currentPromise = null;
17
+ if (options.trailing && trailingArgs && !timeout) {
18
+ const promise = applyFn(_this, trailingArgs);
19
+ trailingArgs = null;
20
+ return promise;
21
+ }
22
+ });
23
+ return currentPromise;
24
+ };
25
+ const debounced = function(...args) {
26
+ if (options.trailing) trailingArgs = args;
27
+ if (currentPromise) return currentPromise;
28
+ return new Promise((resolve) => {
29
+ const shouldCallNow = !timeout && options.leading;
30
+ clearTimeout(timeout);
31
+ timeout = setTimeout(() => {
32
+ timeout = null;
33
+ const promise = options.leading ? leadingValue : applyFn(this, args);
34
+ trailingArgs = null;
35
+ for (const _resolve of resolveList) _resolve(promise);
36
+ resolveList = [];
37
+ }, wait);
38
+ if (shouldCallNow) {
39
+ leadingValue = applyFn(this, args);
40
+ resolve(leadingValue);
41
+ } else resolveList.push(resolve);
42
+ });
43
+ };
44
+ const _clearTimeout = (timer) => {
45
+ if (timer) {
46
+ clearTimeout(timer);
47
+ timeout = null;
48
+ }
49
+ };
50
+ debounced.isPending = () => !!timeout;
51
+ debounced.cancel = () => {
52
+ _clearTimeout(timeout);
53
+ resolveList = [];
54
+ trailingArgs = null;
55
+ };
56
+ debounced.flush = () => {
57
+ _clearTimeout(timeout);
58
+ if (!trailingArgs || currentPromise) return;
59
+ const args = trailingArgs;
60
+ trailingArgs = null;
61
+ return applyFn(this, args);
62
+ };
63
+ return debounced;
64
+ }
65
+ async function _applyPromised(fn, _this, args) {
66
+ return await fn.apply(_this, args);
67
+ }
68
+ export { debounce as t };
package/dist/index.d.mts CHANGED
@@ -1,7 +1,6 @@
1
- import { Jiti, JitiOptions } from "jiti";
1
+ import { t as diff } from "./_chunks/libs/ohash.mjs";
2
2
  import { ChokidarOptions } from "chokidar";
3
3
  import { DownloadTemplateOptions } from "giget";
4
- import { diff } from "ohash/utils";
5
4
 
6
5
  //#region src/dotenv.d.ts
7
6
  interface DotenvOptions {
@@ -125,8 +124,10 @@ interface LoadConfigOptions<T extends UserInputConfig = UserInputConfig, MT exte
125
124
  /** Context passed to config functions */
126
125
  context?: ConfigFunctionContext;
127
126
  resolve?: (id: string, options: LoadConfigOptions<T, MT>) => null | undefined | ResolvedConfig<T, MT> | Promise<ResolvedConfig<T, MT> | undefined | null>;
128
- jiti?: Jiti;
129
- jitiOptions?: JitiOptions;
127
+ /** Custom import function used to load configuration files */
128
+ import?: (id: string) => Promise<unknown>;
129
+ /** Custom resolver for picking which export to use from the loaded module. Default: `(mod) => mod.default || mod` */
130
+ resolveModule?: (mod: any) => any;
130
131
  giget?: false | DownloadTemplateOptions;
131
132
  merger?: (...sources: Array<T | null | undefined>) => T;
132
133
  extend?: false | {
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { t as debounce } from "./_chunks/libs/perfect-debounce.mjs";
1
2
  import { existsSync, promises, statSync } from "node:fs";
2
3
  import { basename, dirname, extname, join, normalize, resolve } from "pathe";
3
4
  import * as dotenv from "dotenv";
@@ -5,18 +6,9 @@ import { readFile, rm } from "node:fs/promises";
5
6
  import { pathToFileURL } from "node:url";
6
7
  import { homedir } from "node:os";
7
8
  import { resolveModulePath } from "exsolve";
8
- import { createJiti } from "jiti";
9
9
  import * as rc9 from "rc9";
10
10
  import { defu } from "defu";
11
11
  import { findWorkspaceDir, readPackageJSON } from "pkg-types";
12
- import { debounce } from "perfect-debounce";
13
-
14
- //#region src/dotenv.ts
15
- /**
16
- * Load and interpolate environment variables into `process.env`.
17
- * If you need more control (or access to the values), consider using `loadDotenv` instead
18
- *
19
- */
20
12
  async function setupDotenv(options) {
21
13
  const targetEnvironment = options.env ?? process.env;
22
14
  const environment = await loadDotenv({
@@ -32,7 +24,6 @@ async function setupDotenv(options) {
32
24
  }
33
25
  return environment;
34
26
  }
35
- /** Load environment variables into an object. */
36
27
  async function loadDotenv(options) {
37
28
  const environment = Object.create(null);
38
29
  const cwd = resolve(options.cwd || ".");
@@ -57,15 +48,15 @@ function interpolate(target, source = {}, parse = (v) => v) {
57
48
  function getValue(key) {
58
49
  return source[key] === void 0 ? target[key] : source[key];
59
50
  }
60
- function interpolate$1(value, parents = []) {
51
+ function interpolate(value, parents = []) {
61
52
  if (typeof value !== "string") return value;
62
53
  return parse((value.match(/(.?\${?(?:[\w:]+)?}?)/g) || []).reduce((newValue, match) => {
63
54
  const parts = /(.?)\${?([\w:]+)?}?/g.exec(match) || [];
64
55
  const prefix = parts[1];
65
- let value$1, replacePart;
56
+ let value, replacePart;
66
57
  if (prefix === "\\") {
67
58
  replacePart = parts[0] || "";
68
- value$1 = replacePart.replace(String.raw`\$`, "$");
59
+ value = replacePart.replace(String.raw`\$`, "$");
69
60
  } else {
70
61
  const key = parts[2];
71
62
  replacePart = (parts[0] || "").slice(prefix.length);
@@ -73,22 +64,19 @@ function interpolate(target, source = {}, parse = (v) => v) {
73
64
  console.warn(`Please avoid recursive environment variables ( loop: ${parents.join(" > ")} > ${key} )`);
74
65
  return "";
75
66
  }
76
- value$1 = getValue(key);
77
- value$1 = interpolate$1(value$1, [...parents, key]);
67
+ value = getValue(key);
68
+ value = interpolate(value, [...parents, key]);
78
69
  }
79
- return value$1 === void 0 ? newValue : newValue.replace(replacePart, value$1);
70
+ return value === void 0 ? newValue : newValue.replace(replacePart, value);
80
71
  }, value));
81
72
  }
82
- for (const key in target) target[key] = interpolate$1(getValue(key));
73
+ for (const key in target) target[key] = interpolate(getValue(key));
83
74
  }
84
75
  function getDotEnvVars(targetEnvironment) {
85
76
  const globalRegistry = globalThis.__c12_dotenv_vars__ ||= /* @__PURE__ */ new Map();
86
77
  if (!globalRegistry.has(targetEnvironment)) globalRegistry.set(targetEnvironment, /* @__PURE__ */ new Set());
87
78
  return globalRegistry.get(targetEnvironment);
88
79
  }
89
-
90
- //#endregion
91
- //#region src/loader.ts
92
80
  const _normalize = (p) => p?.replace(/\\/g, "/");
93
81
  const ASYNC_LOADERS = {
94
82
  ".yaml": () => import("confbox/yaml").then((r) => r.parseYAML),
@@ -122,12 +110,6 @@ async function loadConfig(options) {
122
110
  ...options.extend
123
111
  };
124
112
  const _merger = options.merger || defu;
125
- options.jiti = options.jiti || createJiti(join(options.cwd, options.configFile), {
126
- interopDefault: true,
127
- moduleCache: false,
128
- extensions: [...SUPPORTED_EXTENSIONS],
129
- ...options.jitiOptions
130
- });
131
113
  const r = {
132
114
  config: {},
133
115
  cwd: options.cwd,
@@ -274,15 +256,15 @@ const GIGET_PREFIXES = [
274
256
  const NPM_PACKAGE_RE = /^(@[\da-z~-][\d._a-z~-]*\/)?[\da-z~-][\d._a-z~-]*($|\/.*)/;
275
257
  async function resolveConfig(source, options, sourceOptions = {}) {
276
258
  if (options.resolve) {
277
- const res$1 = await options.resolve(source, options);
278
- if (res$1) return res$1;
259
+ const res = await options.resolve(source, options);
260
+ if (res) return res;
279
261
  }
280
262
  const _merger = options.merger || defu;
281
263
  const customProviderKeys = Object.keys(sourceOptions.giget?.providers || {}).map((key) => `${key}:`);
282
264
  const gigetPrefixes = customProviderKeys.length > 0 ? [...new Set([...customProviderKeys, ...GIGET_PREFIXES])] : GIGET_PREFIXES;
283
265
  if (options.giget !== false && gigetPrefixes.some((prefix) => source.startsWith(prefix))) {
284
266
  const { downloadTemplate } = await import("giget");
285
- const { digest } = await import("ohash");
267
+ const { digest } = await import("./_chunks/libs/ohash.mjs").then((n) => n.n);
286
268
  const cloneName = source.replace(/\W+/g, "_").split("_").splice(0, 3).join("_") + "_" + digest(source).slice(0, 10).replace(/[-_]/g, "");
287
269
  let cloneDir;
288
270
  const localNodeModules = resolve(options.cwd, "node_modules");
@@ -317,7 +299,22 @@ async function resolveConfig(source, options, sourceOptions = {}) {
317
299
  res._configFile = res.configFile;
318
300
  const configFileExt = extname(res.configFile) || "";
319
301
  if (configFileExt in ASYNC_LOADERS) res.config = (await ASYNC_LOADERS[configFileExt]())(await readFile(res.configFile, "utf8"));
320
- else res.config = await options.jiti.import(res.configFile, { default: true });
302
+ else {
303
+ const _resolveModule = options.resolveModule || ((mod) => mod.default || mod);
304
+ if (options.import) res.config = _resolveModule(await options.import(res.configFile));
305
+ else res.config = await import(res.configFile).then(_resolveModule, async (error) => {
306
+ const { createJiti } = await import("jiti").catch(() => {
307
+ throw new Error(`Failed to load config file \`${res.configFile}\`: ${error?.message}. Hint install \`jiti\` for compatibility.`, { cause: error });
308
+ });
309
+ const jiti = createJiti(join(options.cwd || ".", options.configFile || "/"), {
310
+ interopDefault: true,
311
+ moduleCache: false,
312
+ extensions: [...SUPPORTED_EXTENSIONS]
313
+ });
314
+ options.import = (id) => jiti.import(id);
315
+ return _resolveModule(await options.import(res.configFile));
316
+ });
317
+ }
321
318
  if (typeof res.config === "function") res.config = await res.config(options.context);
322
319
  if (options.envName) {
323
320
  const envConfig = {
@@ -343,15 +340,9 @@ function tryResolve(id, options) {
343
340
  });
344
341
  return res ? normalize(res) : void 0;
345
342
  }
346
-
347
- //#endregion
348
- //#region src/types.ts
349
343
  function createDefineConfig() {
350
344
  return (input) => input;
351
345
  }
352
-
353
- //#endregion
354
- //#region src/watch.ts
355
346
  const eventMap = {
356
347
  add: "created",
357
348
  change: "updated",
@@ -372,7 +363,7 @@ async function watchConfig(options) {
372
363
  options.packageJson && resolve(l.cwd, "package.json")
373
364
  ]).filter(Boolean))];
374
365
  const watch = await import("chokidar").then((r) => r.watch || r.default || r);
375
- const { diff } = await import("ohash/utils");
366
+ const { diff } = await import("./_chunks/libs/ohash.mjs").then((n) => n.t);
376
367
  const _fswatcher = watch(watchingFiles, {
377
368
  ignoreInitial: true,
378
369
  ...options.chokidarOptions
@@ -414,6 +405,4 @@ async function watchConfig(options) {
414
405
  return config[prop];
415
406
  } });
416
407
  }
417
-
418
- //#endregion
419
- export { SUPPORTED_EXTENSIONS, createDefineConfig, loadConfig, loadDotenv, setupDotenv, watchConfig };
408
+ export { SUPPORTED_EXTENSIONS, createDefineConfig, loadConfig, loadDotenv, setupDotenv, watchConfig };
package/dist/update.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import * as magicast0 from "magicast";
1
+ import * as magicast from "magicast";
2
2
 
3
3
  //#region src/update.d.ts
4
4
  /**
@@ -10,7 +10,7 @@ interface UpdateConfigResult {
10
10
  created?: boolean;
11
11
  }
12
12
  type MaybePromise<T> = T | Promise<T>;
13
- type MagicAstOptions = Exclude<Parameters<(typeof magicast0)["parseModule"]>[1], undefined>;
13
+ type MagicAstOptions = Exclude<Parameters<(typeof magicast)["parseModule"]>[1], undefined>;
14
14
  interface UpdateConfigOptions {
15
15
  /**
16
16
  * Current working directory
package/dist/update.mjs CHANGED
@@ -4,13 +4,10 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
4
4
  import "node:url";
5
5
  import "node:os";
6
6
  import { join, normalize } from "pathe";
7
- import "jiti";
8
7
  import "rc9";
9
8
  import "defu";
10
9
  import "pkg-types";
11
10
  import { dirname, extname } from "node:path";
12
-
13
- //#region src/loader.ts
14
11
  const SUPPORTED_EXTENSIONS = Object.freeze([
15
12
  ".js",
16
13
  ".ts",
@@ -25,9 +22,6 @@ const SUPPORTED_EXTENSIONS = Object.freeze([
25
22
  ".yml",
26
23
  ".toml"
27
24
  ]);
28
-
29
- //#endregion
30
- //#region src/update.ts
31
25
  const UPDATABLE_EXTS = [
32
26
  ".js",
33
27
  ".ts",
@@ -36,9 +30,6 @@ const UPDATABLE_EXTS = [
36
30
  ".mts",
37
31
  ".cts"
38
32
  ];
39
- /**
40
- * @experimental Update a config file or create a new one.
41
- */
42
33
  async function updateConfig(opts) {
43
34
  const { parseModule } = await import("magicast");
44
35
  let configFile = tryResolve(`./${opts.configFile}`, opts.cwd, SUPPORTED_EXTENSIONS) || tryResolve(`./.config/${opts.configFile}`, opts.cwd, SUPPORTED_EXTENSIONS) || tryResolve(`./.config/${opts.configFile.split(".")[0]}`, opts.cwd, SUPPORTED_EXTENSIONS);
@@ -75,6 +66,4 @@ function tryResolve(path, cwd, extensions) {
75
66
  });
76
67
  return res ? normalize(res) : void 0;
77
68
  }
78
-
79
- //#endregion
80
- export { updateConfig };
69
+ export { updateConfig };
package/package.json CHANGED
@@ -1,69 +1,72 @@
1
1
  {
2
2
  "name": "c12",
3
- "version": "3.3.3",
3
+ "version": "4.0.0-beta.1",
4
4
  "description": "Smart Config Loader",
5
- "repository": "unjs/c12",
6
5
  "license": "MIT",
7
- "sideEffects": false,
8
- "type": "module",
9
- "exports": {
10
- ".": {
11
- "types": "./dist/index.d.mts",
12
- "default": "./dist/index.mjs"
13
- },
14
- "./update": {
15
- "types": "./dist/update.d.mts",
16
- "default": "./dist/update.mjs"
17
- }
18
- },
19
- "types": "./dist/index.d.mts",
6
+ "repository": "unjs/c12",
20
7
  "files": [
21
8
  "dist"
22
9
  ],
10
+ "type": "module",
11
+ "sideEffects": false,
12
+ "types": "./dist/index.d.mts",
13
+ "exports": {
14
+ ".": "./dist/index.mjs",
15
+ "./update": "./dist/update.mjs"
16
+ },
23
17
  "scripts": {
24
18
  "build": "obuild",
25
19
  "dev": "vitest dev",
26
- "lint": "eslint . && prettier -c src test",
27
- "lint:fix": "automd && eslint . --fix && prettier -w src test",
28
- "release": "pnpm build && pnpm test && changelogen --release --push --publish",
20
+ "lint": "oxlint . && oxfmt --check src test",
21
+ "lint:fix": "automd && oxlint . --fix && oxfmt src test",
22
+ "release": "pnpm test && pnpm build && changelogen --release --prerelease --publish && git push --follow-tags",
29
23
  "test": "pnpm lint && vitest run --coverage && pnpm test:types",
30
- "test:types": "tsc --noEmit"
24
+ "test:types": "tsgo --noEmit"
31
25
  },
32
26
  "dependencies": {
33
- "chokidar": "^5.0.0",
34
- "confbox": "^0.2.2",
27
+ "confbox": "^0.2.4",
35
28
  "defu": "^6.1.4",
36
- "dotenv": "^17.2.3",
29
+ "dotenv": "^17.2.4",
37
30
  "exsolve": "^1.0.8",
38
- "giget": "^2.0.0",
39
- "jiti": "^2.6.1",
40
- "ohash": "^2.0.11",
31
+ "giget": "^3.1.2",
41
32
  "pathe": "^2.0.3",
42
- "perfect-debounce": "^2.0.0",
43
33
  "pkg-types": "^2.3.0",
44
- "rc9": "^2.1.2"
34
+ "rc9": "^3.0.0"
45
35
  },
46
36
  "devDependencies": {
47
- "@types/node": "^25.0.2",
48
- "@vitest/coverage-v8": "^4.0.15",
49
- "automd": "^0.4.2",
37
+ "@types/node": "^25.2.1",
38
+ "@typescript/native-preview": "^7.0.0-dev.20260206.1",
39
+ "@vitest/coverage-v8": "^4.0.18",
40
+ "automd": "^0.4.3",
50
41
  "changelogen": "^0.6.2",
51
- "eslint": "^9.39.2",
52
- "eslint-config-unjs": "^0.5.0",
42
+ "chokidar": "^5.0.0",
43
+ "eslint-config-unjs": "^0.6.2",
53
44
  "expect-type": "^1.3.0",
54
- "magicast": "^0.5.1",
55
- "obuild": "^0.4.8",
56
- "prettier": "^3.7.4",
45
+ "jiti": "^2.6.1",
46
+ "magicast": "^0.5.2",
47
+ "obuild": "^0.4.27",
48
+ "ohash": "^2.0.11",
49
+ "oxfmt": "^0.28.0",
50
+ "oxlint": "^1.43.0",
51
+ "perfect-debounce": "^2.1.0",
57
52
  "typescript": "^5.9.3",
58
- "vitest": "^4.0.15"
53
+ "vitest": "^4.0.18"
59
54
  },
60
55
  "peerDependencies": {
56
+ "chokidar": "^5",
57
+ "jiti": "*",
61
58
  "magicast": "*"
62
59
  },
63
60
  "peerDependenciesMeta": {
64
61
  "magicast": {
65
62
  "optional": true
63
+ },
64
+ "chokidar": {
65
+ "optional": true
66
+ },
67
+ "jiti": {
68
+ "optional": true
66
69
  }
67
70
  },
68
- "packageManager": "pnpm@10.26.0"
71
+ "packageManager": "pnpm@10.28.2"
69
72
  }