feat: init

This commit is contained in:
2026-02-13 22:02:30 +01:00
commit 8f9ff830fb
16711 changed files with 3307340 additions and 0 deletions

170
node_modules/c12/dist/index.d.mts generated vendored Normal file
View File

@@ -0,0 +1,170 @@
import { Jiti, JitiOptions } from "jiti";
import { ChokidarOptions } from "chokidar";
import { DownloadTemplateOptions } from "giget";
import { diff } from "ohash/utils";
//#region src/dotenv.d.ts
interface DotenvOptions {
/**
* The project root directory (either absolute or relative to the current working directory).
*
* Defaults to `options.cwd` in `loadConfig` context, or `process.cwd()` when used as standalone.
*/
cwd?: string;
/**
* What file or files to look in for environment variables (either absolute or relative
* to the current working directory). For example, `.env`.
* With the array type, the order enforce the env loading priority (last one overrides).
*/
fileName?: string | string[];
/**
* Whether to interpolate variables within .env.
*
* @example
* ```env
* BASE_DIR="/test"
* # resolves to "/test/further"
* ANOTHER_DIR="${BASE_DIR}/further"
* ```
*/
interpolate?: boolean;
/**
* An object describing environment variables (key, value pairs).
*/
env?: NodeJS.ProcessEnv;
}
type Env = typeof process.env;
/**
* Load and interpolate environment variables into `process.env`.
* If you need more control (or access to the values), consider using `loadDotenv` instead
*
*/
declare function setupDotenv(options: DotenvOptions): Promise<Env>;
/** Load environment variables into an object. */
declare function loadDotenv(options: DotenvOptions): Promise<Env>;
declare global {
var __c12_dotenv_vars__: Map<Record<string, any>, Set<string>>;
}
//#endregion
//#region src/types.d.ts
interface ConfigLayerMeta {
name?: string;
[key: string]: any;
}
type UserInputConfig = Record<string, any>;
interface C12InputConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> {
$test?: T;
$development?: T;
$production?: T;
$env?: Record<string, T>;
$meta?: MT;
}
type InputConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = C12InputConfig<T, MT> & T;
interface SourceOptions<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> {
/** Custom meta for layer */
meta?: MT;
/** Layer config overrides */
overrides?: T;
[key: string]: any;
/**
* Options for cloning remote sources
*
* @see https://giget.unjs.io
*/
giget?: DownloadTemplateOptions;
/**
* Install dependencies after cloning
*
* @see https://nypm.unjs.io
*/
install?: boolean;
/**
* Token for cloning private sources
*
* @see https://giget.unjs.io#providing-token-for-private-repositories
*/
auth?: string;
}
interface ConfigLayer<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> {
config: T | null;
source?: string;
sourceOptions?: SourceOptions<T, MT>;
meta?: MT;
cwd?: string;
configFile?: string;
}
interface ResolvedConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> extends ConfigLayer<T, MT> {
config: T;
layers?: ConfigLayer<T, MT>[];
cwd?: string;
_configFile?: string;
}
type ConfigSource = "overrides" | "main" | "rc" | "packageJson" | "defaultConfig";
interface ConfigFunctionContext {
[key: string]: any;
}
interface ResolvableConfigContext<T extends UserInputConfig = UserInputConfig> {
configs: Record<ConfigSource, T | null | undefined>;
rawConfigs: Record<ConfigSource, ResolvableConfig<T> | null | undefined>;
}
type MaybePromise<T> = T | Promise<T>;
type ResolvableConfig<T extends UserInputConfig = UserInputConfig> = MaybePromise<T | null | undefined> | ((ctx: ResolvableConfigContext<T>) => MaybePromise<T | null | undefined>);
interface LoadConfigOptions<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> {
name?: string;
cwd?: string;
configFile?: string;
rcFile?: false | string;
globalRc?: boolean;
dotenv?: boolean | DotenvOptions;
envName?: string | false;
packageJson?: boolean | string | string[];
defaults?: T;
defaultConfig?: ResolvableConfig<T>;
overrides?: ResolvableConfig<T>;
omit$Keys?: boolean;
/** Context passed to config functions */
context?: ConfigFunctionContext;
resolve?: (id: string, options: LoadConfigOptions<T, MT>) => null | undefined | ResolvedConfig<T, MT> | Promise<ResolvedConfig<T, MT> | undefined | null>;
jiti?: Jiti;
jitiOptions?: JitiOptions;
giget?: false | DownloadTemplateOptions;
merger?: (...sources: Array<T | null | undefined>) => T;
extend?: false | {
extendKey?: string | string[];
};
configFileRequired?: boolean;
}
type DefineConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = (input: InputConfig<T, MT>) => InputConfig<T, MT>;
declare function createDefineConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(): DefineConfig<T, MT>;
//#endregion
//#region src/loader.d.ts
declare const SUPPORTED_EXTENSIONS: string[];
declare function loadConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(options: LoadConfigOptions<T, MT>): Promise<ResolvedConfig<T, MT>>;
//#endregion
//#region src/watch.d.ts
type DiffEntries = ReturnType<typeof diff>;
type ConfigWatcher<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = ResolvedConfig<T, MT> & {
watchingFiles: string[];
unwatch: () => Promise<void>;
};
interface WatchConfigOptions<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> extends LoadConfigOptions<T, MT> {
chokidarOptions?: ChokidarOptions;
debounce?: false | number;
onWatch?: (event: {
type: "created" | "updated" | "removed";
path: string;
}) => void | Promise<void>;
acceptHMR?: (context: {
getDiff: () => DiffEntries;
newConfig: ResolvedConfig<T, MT>;
oldConfig: ResolvedConfig<T, MT>;
}) => void | boolean | Promise<void | boolean>;
onUpdate?: (context: {
getDiff: () => ReturnType<typeof diff>;
newConfig: ResolvedConfig<T, MT>;
oldConfig: ResolvedConfig<T, MT>;
}) => void | Promise<void>;
}
declare function watchConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta>(options: WatchConfigOptions<T, MT>): Promise<ConfigWatcher<T, MT>>;
//#endregion
export { C12InputConfig, ConfigFunctionContext, ConfigLayer, ConfigLayerMeta, ConfigSource, ConfigWatcher, DefineConfig, DotenvOptions, Env, InputConfig, LoadConfigOptions, ResolvableConfig, ResolvableConfigContext, ResolvedConfig, SUPPORTED_EXTENSIONS, SourceOptions, UserInputConfig, WatchConfigOptions, createDefineConfig, loadConfig, loadDotenv, setupDotenv, watchConfig };

419
node_modules/c12/dist/index.mjs generated vendored Normal file
View File

@@ -0,0 +1,419 @@
import { existsSync, promises, statSync } from "node:fs";
import { basename, dirname, extname, join, normalize, resolve } from "pathe";
import * as dotenv from "dotenv";
import { readFile, rm } from "node:fs/promises";
import { pathToFileURL } from "node:url";
import { homedir } from "node:os";
import { resolveModulePath } from "exsolve";
import { createJiti } from "jiti";
import * as rc9 from "rc9";
import { defu } from "defu";
import { findWorkspaceDir, readPackageJSON } from "pkg-types";
import { debounce } from "perfect-debounce";
//#region src/dotenv.ts
/**
* Load and interpolate environment variables into `process.env`.
* If you need more control (or access to the values), consider using `loadDotenv` instead
*
*/
async function setupDotenv(options) {
const targetEnvironment = options.env ?? process.env;
const environment = await loadDotenv({
cwd: options.cwd,
fileName: options.fileName ?? ".env",
env: targetEnvironment,
interpolate: options.interpolate ?? true
});
const dotenvVars = getDotEnvVars(targetEnvironment);
for (const key in environment) {
if (key.startsWith("_")) continue;
if (targetEnvironment[key] === void 0 || dotenvVars.has(key)) targetEnvironment[key] = environment[key];
}
return environment;
}
/** Load environment variables into an object. */
async function loadDotenv(options) {
const environment = Object.create(null);
const cwd = resolve(options.cwd || ".");
const _fileName = options.fileName || ".env";
const dotenvFiles = typeof _fileName === "string" ? [_fileName] : _fileName;
const dotenvVars = getDotEnvVars(options.env || {});
Object.assign(environment, options.env);
for (const file of dotenvFiles) {
const dotenvFile = resolve(cwd, file);
if (!statSync(dotenvFile, { throwIfNoEntry: false })?.isFile()) continue;
const parsed = dotenv.parse(await promises.readFile(dotenvFile, "utf8"));
for (const key in parsed) {
if (key in environment && !dotenvVars.has(key)) continue;
environment[key] = parsed[key];
dotenvVars.add(key);
}
}
if (options.interpolate) interpolate(environment);
return environment;
}
function interpolate(target, source = {}, parse = (v) => v) {
function getValue(key) {
return source[key] === void 0 ? target[key] : source[key];
}
function interpolate$1(value, parents = []) {
if (typeof value !== "string") return value;
return parse((value.match(/(.?\${?(?:[\w:]+)?}?)/g) || []).reduce((newValue, match) => {
const parts = /(.?)\${?([\w:]+)?}?/g.exec(match) || [];
const prefix = parts[1];
let value$1, replacePart;
if (prefix === "\\") {
replacePart = parts[0] || "";
value$1 = replacePart.replace(String.raw`\$`, "$");
} else {
const key = parts[2];
replacePart = (parts[0] || "").slice(prefix.length);
if (parents.includes(key)) {
console.warn(`Please avoid recursive environment variables ( loop: ${parents.join(" > ")} > ${key} )`);
return "";
}
value$1 = getValue(key);
value$1 = interpolate$1(value$1, [...parents, key]);
}
return value$1 === void 0 ? newValue : newValue.replace(replacePart, value$1);
}, value));
}
for (const key in target) target[key] = interpolate$1(getValue(key));
}
function getDotEnvVars(targetEnvironment) {
const globalRegistry = globalThis.__c12_dotenv_vars__ ||= /* @__PURE__ */ new Map();
if (!globalRegistry.has(targetEnvironment)) globalRegistry.set(targetEnvironment, /* @__PURE__ */ new Set());
return globalRegistry.get(targetEnvironment);
}
//#endregion
//#region src/loader.ts
const _normalize = (p) => p?.replace(/\\/g, "/");
const ASYNC_LOADERS = {
".yaml": () => import("confbox/yaml").then((r) => r.parseYAML),
".yml": () => import("confbox/yaml").then((r) => r.parseYAML),
".jsonc": () => import("confbox/jsonc").then((r) => r.parseJSONC),
".json5": () => import("confbox/json5").then((r) => r.parseJSON5),
".toml": () => import("confbox/toml").then((r) => r.parseTOML)
};
const SUPPORTED_EXTENSIONS = Object.freeze([
".js",
".ts",
".mjs",
".cjs",
".mts",
".cts",
".json",
".jsonc",
".json5",
".yaml",
".yml",
".toml"
]);
async function loadConfig(options) {
options.cwd = resolve(process.cwd(), options.cwd || ".");
options.name = options.name || "config";
options.envName = options.envName ?? process.env.NODE_ENV;
options.configFile = options.configFile ?? (options.name === "config" ? "config" : `${options.name}.config`);
options.rcFile = options.rcFile ?? `.${options.name}rc`;
if (options.extend !== false) options.extend = {
extendKey: "extends",
...options.extend
};
const _merger = options.merger || defu;
options.jiti = options.jiti || createJiti(join(options.cwd, options.configFile), {
interopDefault: true,
moduleCache: false,
extensions: [...SUPPORTED_EXTENSIONS],
...options.jitiOptions
});
const r = {
config: {},
cwd: options.cwd,
configFile: resolve(options.cwd, options.configFile),
layers: [],
_configFile: void 0
};
const rawConfigs = {
overrides: options.overrides,
main: void 0,
rc: void 0,
packageJson: void 0,
defaultConfig: options.defaultConfig
};
if (options.dotenv) await setupDotenv({
cwd: options.cwd,
...options.dotenv === true ? {} : options.dotenv
});
const _mainConfig = await resolveConfig(".", options);
if (_mainConfig.configFile) {
rawConfigs.main = _mainConfig.config;
r.configFile = _mainConfig.configFile;
r._configFile = _mainConfig._configFile;
}
if (_mainConfig.meta) r.meta = _mainConfig.meta;
if (options.rcFile) {
const rcSources = [];
rcSources.push(rc9.read({
name: options.rcFile,
dir: options.cwd
}));
if (options.globalRc) {
const workspaceDir = await findWorkspaceDir(options.cwd).catch(() => {});
if (workspaceDir) rcSources.push(rc9.read({
name: options.rcFile,
dir: workspaceDir
}));
rcSources.push(rc9.readUser({
name: options.rcFile,
dir: options.cwd
}));
}
rawConfigs.rc = _merger({}, ...rcSources);
}
if (options.packageJson) {
const keys = (Array.isArray(options.packageJson) ? options.packageJson : [typeof options.packageJson === "string" ? options.packageJson : options.name]).filter((t) => t && typeof t === "string");
const pkgJsonFile = await readPackageJSON(options.cwd).catch(() => {});
rawConfigs.packageJson = _merger({}, ...keys.map((key) => pkgJsonFile?.[key]));
}
const configs = {};
for (const key in rawConfigs) {
const value = rawConfigs[key];
configs[key] = await (typeof value === "function" ? value({
configs,
rawConfigs
}) : value);
}
if (Array.isArray(configs.main)) r.config = configs.main;
else {
r.config = _merger(configs.overrides, configs.main, configs.rc, configs.packageJson, configs.defaultConfig);
if (options.extend) {
await extendConfig(r.config, options);
r.layers = r.config._layers;
delete r.config._layers;
r.config = _merger(r.config, ...r.layers.map((e) => e.config));
}
}
r.layers = [...[
configs.overrides && {
config: configs.overrides,
configFile: void 0,
cwd: void 0
},
{
config: configs.main,
configFile: options.configFile,
cwd: options.cwd
},
configs.rc && {
config: configs.rc,
configFile: options.rcFile
},
configs.packageJson && {
config: configs.packageJson,
configFile: "package.json"
}
].filter((l) => l && l.config), ...r.layers];
if (options.defaults) r.config = _merger(r.config, options.defaults);
if (options.omit$Keys) {
for (const key in r.config) if (key.startsWith("$")) delete r.config[key];
}
if (options.configFileRequired && !r._configFile) throw new Error(`Required config (${r.configFile}) cannot be resolved.`);
return r;
}
async function extendConfig(config, options) {
config._layers = config._layers || [];
if (!options.extend) return;
let keys = options.extend.extendKey;
if (typeof keys === "string") keys = [keys];
const extendSources = [];
for (const key of keys) {
extendSources.push(...(Array.isArray(config[key]) ? config[key] : [config[key]]).filter(Boolean));
delete config[key];
}
for (let extendSource of extendSources) {
const originalExtendSource = extendSource;
let sourceOptions = {};
if (extendSource.source) {
sourceOptions = extendSource.options || {};
extendSource = extendSource.source;
}
if (Array.isArray(extendSource)) {
sourceOptions = extendSource[1] || {};
extendSource = extendSource[0];
}
if (typeof extendSource !== "string") {
console.warn(`Cannot extend config from \`${JSON.stringify(originalExtendSource)}\` in ${options.cwd}`);
continue;
}
const _config = await resolveConfig(extendSource, options, sourceOptions);
if (!_config.config) {
console.warn(`Cannot extend config from \`${extendSource}\` in ${options.cwd}`);
continue;
}
await extendConfig(_config.config, {
...options,
cwd: _config.cwd
});
config._layers.push(_config);
if (_config.config._layers) {
config._layers.push(..._config.config._layers);
delete _config.config._layers;
}
}
}
const GIGET_PREFIXES = [
"gh:",
"github:",
"gitlab:",
"bitbucket:",
"https://",
"http://"
];
const NPM_PACKAGE_RE = /^(@[\da-z~-][\d._a-z~-]*\/)?[\da-z~-][\d._a-z~-]*($|\/.*)/;
async function resolveConfig(source, options, sourceOptions = {}) {
if (options.resolve) {
const res$1 = await options.resolve(source, options);
if (res$1) return res$1;
}
const _merger = options.merger || defu;
const customProviderKeys = Object.keys(sourceOptions.giget?.providers || {}).map((key) => `${key}:`);
const gigetPrefixes = customProviderKeys.length > 0 ? [...new Set([...customProviderKeys, ...GIGET_PREFIXES])] : GIGET_PREFIXES;
if (options.giget !== false && gigetPrefixes.some((prefix) => source.startsWith(prefix))) {
const { downloadTemplate } = await import("giget");
const { digest } = await import("ohash");
const cloneName = source.replace(/\W+/g, "_").split("_").splice(0, 3).join("_") + "_" + digest(source).slice(0, 10).replace(/[-_]/g, "");
let cloneDir;
const localNodeModules = resolve(options.cwd, "node_modules");
const parentDir = dirname(options.cwd);
if (basename(parentDir) === ".c12") cloneDir = join(parentDir, cloneName);
else if (existsSync(localNodeModules)) cloneDir = join(localNodeModules, ".c12", cloneName);
else cloneDir = process.env.XDG_CACHE_HOME ? resolve(process.env.XDG_CACHE_HOME, "c12", cloneName) : resolve(homedir(), ".cache/c12", cloneName);
if (existsSync(cloneDir) && !sourceOptions.install) await rm(cloneDir, { recursive: true });
source = (await downloadTemplate(source, {
dir: cloneDir,
install: sourceOptions.install,
force: sourceOptions.install,
auth: sourceOptions.auth,
...options.giget,
...sourceOptions.giget
})).dir;
}
if (NPM_PACKAGE_RE.test(source)) source = tryResolve(source, options) || source;
const ext = extname(source);
const isDir = !ext || ext === basename(source);
const cwd = resolve(options.cwd, isDir ? source : dirname(source));
if (isDir) source = options.configFile;
const res = {
config: void 0,
configFile: void 0,
cwd,
source,
sourceOptions
};
res.configFile = tryResolve(resolve(cwd, source), options) || tryResolve(resolve(cwd, ".config", source.replace(/\.config$/, "")), options) || tryResolve(resolve(cwd, ".config", source), options) || source;
if (!existsSync(res.configFile)) return res;
res._configFile = res.configFile;
const configFileExt = extname(res.configFile) || "";
if (configFileExt in ASYNC_LOADERS) res.config = (await ASYNC_LOADERS[configFileExt]())(await readFile(res.configFile, "utf8"));
else res.config = await options.jiti.import(res.configFile, { default: true });
if (typeof res.config === "function") res.config = await res.config(options.context);
if (options.envName) {
const envConfig = {
...res.config["$" + options.envName],
...res.config.$env?.[options.envName]
};
if (Object.keys(envConfig).length > 0) res.config = _merger(envConfig, res.config);
}
res.meta = defu(res.sourceOptions.meta, res.config.$meta);
delete res.config.$meta;
if (res.sourceOptions.overrides) res.config = _merger(res.sourceOptions.overrides, res.config);
res.configFile = _normalize(res.configFile);
res.source = _normalize(res.source);
return res;
}
function tryResolve(id, options) {
const res = resolveModulePath(id, {
try: true,
from: pathToFileURL(join(options.cwd || ".", options.configFile || "/")),
suffixes: ["", "/index"],
extensions: SUPPORTED_EXTENSIONS,
cache: false
});
return res ? normalize(res) : void 0;
}
//#endregion
//#region src/types.ts
function createDefineConfig() {
return (input) => input;
}
//#endregion
//#region src/watch.ts
const eventMap = {
add: "created",
change: "updated",
unlink: "removed"
};
async function watchConfig(options) {
let config = await loadConfig(options);
const configName = options.name || "config";
const configFileName = options.configFile ?? (options.name === "config" ? "config" : `${options.name}.config`);
const watchingFiles = [...new Set((config.layers || []).filter((l) => l.cwd).flatMap((l) => [
...SUPPORTED_EXTENSIONS.flatMap((ext) => [
resolve(l.cwd, configFileName + ext),
resolve(l.cwd, ".config", configFileName + ext),
resolve(l.cwd, ".config", configFileName.replace(/\.config$/, "") + ext)
]),
l.source && resolve(l.cwd, l.source),
options.rcFile && resolve(l.cwd, typeof options.rcFile === "string" ? options.rcFile : `.${configName}rc`),
options.packageJson && resolve(l.cwd, "package.json")
]).filter(Boolean))];
const watch = await import("chokidar").then((r) => r.watch || r.default || r);
const { diff } = await import("ohash/utils");
const _fswatcher = watch(watchingFiles, {
ignoreInitial: true,
...options.chokidarOptions
});
const onChange = async (event, path) => {
const type = eventMap[event];
if (!type) return;
if (options.onWatch) await options.onWatch({
type,
path
});
const oldConfig = config;
try {
config = await loadConfig(options);
} catch (error) {
console.warn(`Failed to load config ${path}\n${error}`);
return;
}
const changeCtx = {
newConfig: config,
oldConfig,
getDiff: () => diff(oldConfig.config, config.config)
};
if (options.acceptHMR) {
if (await options.acceptHMR(changeCtx)) return;
}
if (options.onUpdate) await options.onUpdate(changeCtx);
};
if (options.debounce === false) _fswatcher.on("all", onChange);
else _fswatcher.on("all", debounce(onChange, options.debounce ?? 100));
const utils = {
watchingFiles,
unwatch: async () => {
await _fswatcher.close();
}
};
return new Proxy(utils, { get(_, prop) {
if (prop in utils) return utils[prop];
return config[prop];
} });
}
//#endregion
export { SUPPORTED_EXTENSIONS, createDefineConfig, loadConfig, loadDotenv, setupDotenv, watchConfig };

53
node_modules/c12/dist/update.d.mts generated vendored Normal file
View File

@@ -0,0 +1,53 @@
import * as magicast0 from "magicast";
//#region src/update.d.ts
/**
* @experimental Update a config file or create a new one.
*/
declare function updateConfig(opts: UpdateConfigOptions): Promise<UpdateConfigResult>;
interface UpdateConfigResult {
configFile?: string;
created?: boolean;
}
type MaybePromise<T> = T | Promise<T>;
type MagicAstOptions = Exclude<Parameters<(typeof magicast0)["parseModule"]>[1], undefined>;
interface UpdateConfigOptions {
/**
* Current working directory
*/
cwd: string;
/**
* Config file name
*/
configFile: string;
/**
* Extension used for new config file.
*/
createExtension?: string;
/**
* Magicast options
*/
magicast?: MagicAstOptions;
/**
* Update function.
*/
onUpdate?: (config: any) => MaybePromise<void>;
/**
* Handle default config creation.
*
* Tip: you can use this option as a hook to prompt users about config creation.
*
* Context object:
* - path: determined full path to the config file
*
* Returns types:
* - string: custom config template
* - true: write the template
* - false: abort the operation
*/
onCreate?: (ctx: {
configFile: string;
}) => MaybePromise<string | boolean>;
}
//#endregion
export { UpdateConfigOptions, UpdateConfigResult, updateConfig };

80
node_modules/c12/dist/update.mjs generated vendored Normal file
View File

@@ -0,0 +1,80 @@
import { resolveModulePath } from "exsolve";
import "node:fs";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import "node:url";
import "node:os";
import { join, normalize } from "pathe";
import "jiti";
import "rc9";
import "defu";
import "pkg-types";
import { dirname, extname } from "node:path";
//#region src/loader.ts
const SUPPORTED_EXTENSIONS = Object.freeze([
".js",
".ts",
".mjs",
".cjs",
".mts",
".cts",
".json",
".jsonc",
".json5",
".yaml",
".yml",
".toml"
]);
//#endregion
//#region src/update.ts
const UPDATABLE_EXTS = [
".js",
".ts",
".mjs",
".cjs",
".mts",
".cts"
];
/**
* @experimental Update a config file or create a new one.
*/
async function updateConfig(opts) {
const { parseModule } = await import("magicast");
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);
let created = false;
if (!configFile) {
configFile = join(opts.cwd, opts.configFile + (opts.createExtension || ".ts"));
const createResult = await opts.onCreate?.({ configFile }) ?? true;
if (!createResult) throw new Error("Config file creation aborted.");
const content = typeof createResult === "string" ? createResult : `export default {}\n`;
await mkdir(dirname(configFile), { recursive: true });
await writeFile(configFile, content, "utf8");
created = true;
}
const ext = extname(configFile);
if (!UPDATABLE_EXTS.includes(ext)) throw new Error(`Unsupported config file extension: ${ext} (${configFile}) (supported: ${UPDATABLE_EXTS.join(", ")})`);
const _module = parseModule(await readFile(configFile, "utf8"), opts.magicast);
const defaultExport = _module.exports.default;
if (!defaultExport) throw new Error("Default export is missing in the config file!");
const configObj = defaultExport.$type === "function-call" ? defaultExport.$args[0] : defaultExport;
await opts.onUpdate?.(configObj);
await writeFile(configFile, _module.generate().code);
return {
configFile,
created
};
}
function tryResolve(path, cwd, extensions) {
const res = resolveModulePath(path, {
try: true,
from: join(cwd, "/"),
extensions,
suffixes: ["", "/index"],
cache: false
});
return res ? normalize(res) : void 0;
}
//#endregion
export { updateConfig };