new changes

This commit is contained in:
Niranjan
2026-04-07 05:05:28 +05:30
parent 7c070224bd
commit a18bba15f2
29975 changed files with 3247495 additions and 2761 deletions

View File

@@ -0,0 +1,438 @@
Auto Config Loader
===
[![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor)
[![CI](https://github.com/jaywcjlove/auto-config-loader/actions/workflows/main.yml/badge.svg)](https://github.com/jaywcjlove/auto-config-loader/actions/workflows/main.yml)
[![NPM version](https://img.shields.io/npm/v/auto-config-loader.svg?style=flat&label=npm)](https://npmjs.org/package/auto-config-loader)
[![Coverage Status](https://jaywcjlove.github.io/auto-config-loader/badges.svg)](https://jaywcjlove.github.io/auto-config-loader/lcov-report/)
[![npm Downloads](https://img.shields.io/npm/dm/auto-config-loader?style=flat)](https://www.npmjs.com/package/auto-config-loader)
Find and load configuration from a `package.json` property, `rc` file, or `CommonJS` module. It has smart default based on traditional expectations in the JavaScript ecosystem. But it's also flexible enough to search anywhere you want and load whatever you want.
[V1 To V2 Migration](#v1-to-v2-migration)
## Features
- Support [JSON](https://www.json.org), [JSONC](https://github.com/microsoft/node-jsonc-parser), [JSON5](https://json5.org/), [YAML](https://yaml.org/), [TOML](https://toml.io), [INI](https://en.wikipedia.org/wiki/INI_file), [CJS](http://www.commonjs.org), [Typescript](https://www.typescriptlang.org/), and ESM config load.
- Reads config from the nearest `package.json` file
## Install
```bash
$ npm i auto-config-loader
```
## Quick start
```js
const autoConf = require('auto-config-loader');
import { autoConf } from 'auto-config-loader';
// will look for:
// process.cwd() + '.namespacerc'
// process.cwd() + '.namespacerc.js'
// process.cwd() + '.namespacerc.ts'
// process.cwd() + '.namespacerc.mjs'
// process.cwd() + '.namespacerc.cjs'
// process.cwd() + '.namespacerc.json'
// process.cwd() + '.namespacerc.json5'
// process.cwd() + '.namespacerc.jsonc'
// process.cwd() + '.namespacerc.yaml'
// process.cwd() + '.namespacerc.yml'
// process.cwd() + '.namespacerc.toml'
// process.cwd() + 'namespace.config.mjs'
// process.cwd() + 'namespace.config.cjs'
// process.cwd() + 'namespace.config.js'
// ........
const data = await autoConf('namespace', {
default: {
testItem2: 'some value'
}
});
```
## Load JS
Load the JS file and return the result, support `.js`, `.cjs`, `.mjs`, `.ts`.
```js
// => ./app/app.config.js
export default {
name: 'app'
}
```
```ts
import { loadConf } from 'auto-config-loader/load-conf';
interface Config {
name: string;
}
const result = await loadConf<Config>('./app/app.config.js');
// => { name: 'app' }
```
## Option
```ts
import { LoadConfOption } from 'auto-config-loader';
export type LoaderFunc<T> = (filepath: string, content: string, jsOption?: LoadConfOption) => T | Promise<T>;
export type Loader<T> = Record<string, LoaderFunc<T>>;
export interface AutoConfOption<T> {
searchPlaces?: string[];
/** An object that maps extensions to the loader functions responsible for loading and parsing files with those extensions. */
loaders?: Loader<T>;
/** Specify default configuration. It has the lowest priority and is applied after extending config. */
default?: T;
/** Resolve configuration from this working directory. The default is `process.cwd()` */
cwd?: string;
/** Default transform js configuration */
jsOption?: LoadConfOption;
/** @deprecated use `mustExist` instead */
ignoreLog?: boolean;
mustExist?: boolean;
}
export declare const getConfigPath: () => string;
/**
* Find and load configuration from a `package.json` property, `rc` file, or `CommonJS` module.
* @param namespace {string} Configuration base name. The default is `autoconf`.
* @param option
*/
export declare function autoConf<T>(namespace?: string, option?: AutoConfOption<T>): Promise<{} & T>;
export default autoConf;
```
Discover configurations in the specified directory order. When configuring a tool, you can use multiple file formats and put these in multiple places. Usually, a tool would mention this in its own README file, but by default, these are the following places, where `${moduleName}` represents the name of the tool:
**Default `searchPlaces`:**
```js
[
'package.json',
`.${moduleName}rc`,
`.${moduleName}rc.json`,
`.${moduleName}rc.json5`,
`.${moduleName}rc.jsonc`,
`.${moduleName}rc.yaml`,
`.${moduleName}rc.yml`,
`.${moduleName}rc.toml`,
`.${moduleName}rc.ini`,
`.${moduleName}rc.js`,
`.${moduleName}rc.ts`,
`.${moduleName}rc.cjs`,
`.${moduleName}rc.mjs`,
`.config/${moduleName}rc`,
`.config/${moduleName}rc.json`,
`.config/${moduleName}rc.json5`,
`.config/${moduleName}rc.jsonc`,
`.config/${moduleName}rc.yaml`,
`.config/${moduleName}rc.yml`,
`.config/${moduleName}rc.toml`,
`.config/${moduleName}rc.ini`,
`.config/${moduleName}rc.js`,
`.config/${moduleName}rc.ts`,
`.config/${moduleName}rc.cjs`,
`.config/${moduleName}rc.mjs`,
`${moduleName}.config.js`,
`${moduleName}.config.ts`,
`${moduleName}.config.cjs`,
`${moduleName}.config.mjs`,
]
```
Configurations are loaded sequentially, and the configuration file search is terminated when a configuration file exists.
The content of these files is defined by the tool. For example, you can add a `semi` configuration value to `false` using a file called `.config/autoconfig.yml`:
```yml
semi: true
```
Additionally, you have the option to put a property named after the tool in your `package.json` file, with the contents of that property being the same as the file contents. To use the same example as above:
```js
{
"name": "your-project",
"autoconfig": {
"semi": true
}
}
```
This has the advantage that you can put the configuration of all tools (at least the ones that use `auto-config-loader`) in one file.
## Loader
### `.js`,`.ts`,`.cjs`,`.mjs`
```ts
import type jiti from 'jiti';
import { Options } from 'sucrase';
type Jiti = ReturnType<typeof jiti>;
type JITIOptions = Jiti['options'];
export interface LoadConfOption {
jiti?: boolean;
jitiOptions?: JITIOptions;
transformOption?: Options;
}
export declare function loadConf<T>(path: string, option?: LoadConfOption): Promise<T>;
export declare function jsLoader<T>(
filepath: string,
content: string,
option?: LoadConfOption
): Promise<T>;
```
Modify default `.js`,`.ts`,`.cjs`,`.mjs` loader parameters.
```js
import load, { jsLoader } from 'auto-config-loader';
function loadJS(filepath, content) {
return jsLoader(filepath, content, {
// change option...
});
}
const data = await load('namespace', {
loaders: {
'.js': loadJS,
'.ts': loadJS,
'.cjs': loadJS,
'.mjs': loadJS,
},
default: {
testItem2: 'some value'
}
});
```
example:
```ts
import { jsLoader } from 'auto-config-loader';
const data = jsLoader('/path/to/file/name.js')
```
### `.ini`
```ts
export declare function iniLoader<T>(_: string, content: string): T;
```
example:
```ts
import { iniLoader } from 'auto-config-loader';
const data = iniLoader(undefined, `...`)
```
### `.json`,`.jsonc`, `json5`
```ts
export declare function jsonLoader<T>(_: string, content: string): T;
```
example:
```ts
import { jsonLoader } from 'auto-config-loader';
const data = jsonLoader(undefined, `{ "a": 123 }`)
```
### `.toml`
```ts
export declare function tomlLoader<T>(_: string, content: string): T;
```
example:
```ts
import { tomlLoader } from 'auto-config-loader';
const data = tomlLoader(undefined, `...`)
```
### `.yaml`
```ts
export declare function yamlLoader<T>(_: string, content: string): T;
```
example:
```ts
import { yamlLoader } from 'auto-config-loader';
const data = yamlLoader(undefined, `...`)
```
## Custom `Yaml` loader
This is an example, the default `yaml`/`yml` does not require a loader.
```js
import load from 'auto-config-loader';
import yaml from 'yaml';
function loadYaml(filepath, content) {
return yaml.parse(content);
}
const data = await load('namespace', {
searchPlaces: [
'.namespacerc.yaml',
'.namespacerc.yml',
],
loaders: {
'.yaml': loadYaml,
'.yml': loadYaml,
},
default: {
testItem2: 'some value'
}
});
```
## Utils
### merge
```ts
export declare const merge: {
<TObject, TSource>(object: TObject, source: TSource): TObject & TSource;
<TObject, TSource1, TSource2>(object: TObject, source1: TSource1, source2: TSource2): TObject & TSource1 & TSource2;
<TObject, TSource1, TSource2, TSource3>(object: TObject, source1: TSource1, source2: TSource2, source3: TSource3): TObject & TSource1 & TSource2 & TSource3;
<TObject, TSource1, TSource2, TSource3, TSource4>(object: TObject, source1: TSource1, source2: TSource2, source3: TSource3, source4: TSource4): TObject & TSource1 & TSource2 & TSource3 & TSource4;
(object: any, ...otherArgs: any[]): any;
};
```
### findConfigFile
```ts
export declare function findConfigFile(
moduleName: string,
root: string,
searchPlaces?: string[]
): string;
```
### getConfigPath
```ts
export declare const getConfigPath: () => string;
```
Example:
```ts
import { autoConf, getConfigPath } from 'auto-config-loader';
const data = autoConf<Config>('idoc');
const configPath = getConfigPath();
// => /.autoconfrc.js
```
## V1 To V2 Migration
This guide provides the steps to migrate to the latest version of the configuration loader API.
### Key Changes
1. **Loader Functions Support Async**
- `LoaderFunc<T>` now supports returning `T` or `Promise<T>`.
- Update custom loaders to handle asynchronous operations if needed.
**Example:**
```ts
export type LoaderFunc<T> = (
filepath: string,
content: string,
jsOption?: LoadConfOption
) => T | Promise<T>;
```
2. **`autoConf` Returns a Promise**
- The `autoConf` function now returns a `Promise` instead of a synchronous result.
- Update your code to handle asynchronous calls.
**Example:**
```ts
export declare function autoConf<T>(
namespace?: string,
option?: AutoConfOption<T>
): Promise<{} & T>;
```
### Migration Steps
#### 1. Update Custom Loader Functions
If you have custom loaders, update their return types to support asynchronous operations:
**Example:**
```ts
const jsonLoader: LoaderFunc<MyConfig> = async (
filepath, content
) => JSON.parse(content);
```
#### 2. Handle Asynchronous `autoConf` Calls
Update all calls to `autoConf` to use `await` or `.then` to handle Promises:
**Example Using `await`:**
```ts
const config = await autoConf('myNamespace', options);
console.log(config);
```
**Example Using `.then`:**
```ts
autoConf('myNamespace', options).then(config => {
console.log(config);
});
```
## Related
- [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig) Find and load configuration from a package.json property, rc file, or CommonJS module
- [cjson](https://www.npmjs.com/package/cjson) Comments enabled json loader (Commented JavaScript Object Notation)
- [Config Loader](https://www.npmjs.com/package/@web/config-loader) Load user config files for node js projects.
- [Lilconfig](https://www.npmjs.com/package/lilconfig) Zero-dependency nodejs config seeker.
- [proload](https://github.com/natemoo-re/proload) Searches for and loads your tool's JavaScript configuration files with full support for CJS, ESM, TypeScript and more.
- [rc](https://github.com/dominictarr/rc) The non-configurable configuration loader for lazy people.
Library | Last commit | Download | loaders | config ext
:-- | --- | --- | --- | ---
**auto-config-loader** | [![GitHub last commit](https://img.shields.io/github/last-commit/jaywcjlove/auto-config-loader?style=flat&label=)](https://github.com/jaywcjlove/auto-config-loader/commits) | [![NPM Downloads](https://img.shields.io/npm/dm/auto-config-loader.svg?style=flat&label=)](https://www.npmjs.com/package/auto-config-loader) | ✅ | `.js`, `.ts`, `.cjs`, `.mjs`, `.json`, `.jsonc`, `json5`, `.ini`, `.toml`, `.yaml` ++
cosmiconfig | [![GitHub last commit](https://img.shields.io/github/last-commit/cosmiconfig/cosmiconfig?style=flat&label=)](https://github.com/cosmiconfig/cosmiconfig/commits) | [![NPM Downloads](https://img.shields.io/npm/dm/cosmiconfig.svg?style=flat&label=)](https://www.npmjs.com/package/cosmiconfig) | ✅ | `.json`, `.yaml`, `.yml`, `.js`, `.mjs`, `.cjs`
~~rc~~ | [![GitHub last commit](https://img.shields.io/github/last-commit/dominictarr/rc?style=flat&label=)](https://github.com/dominictarr/rc/commits) | [![NPM Downloads](https://img.shields.io/npm/dm/rc.svg?style=flat&label=)](https://www.npmjs.com/package/rc) | ✅ | `.json`, `.yaml`, `.yml`, `.js`, `.mjs`, `.cjs`
@proload/core | [![GitHub last commit](https://img.shields.io/github/last-commit/natemoo-re/proload?style=flat&label=)](https://github.com/natemoo-re/proload/commits) | [![NPM Downloads](https://img.shields.io/npm/dm/@proload/core.svg?style=flat&label=)](https://www.npmjs.com/package/@proload/core) | ✅ | `.js`, `.ts`, `.cjs`, `.mjs`, `.json`, `.jsonc`, `json5`, `.ini`, `.toml`, `.yaml` ++
lilconfig | [![GitHub last commit](https://img.shields.io/github/last-commit/antonk52/lilconfig?style=flat&label=)](https://github.com/antonk52/lilconfig/commits) | [![NPM Downloads](https://img.shields.io/npm/dm/lilconfig.svg?style=flat&label=)](https://www.npmjs.com/package/lilconfig) | ✅ | `.js`, `.cjs`, `.mjs`, `.json` ++
~~cjson~~ | [![GitHub last commit](https://img.shields.io/github/last-commit/kof/node-cjson?style=flat&label=)](https://github.com/kof/node-cjson/commits) | [![NPM Downloads](https://img.shields.io/npm/dm/cjson.svg?style=flat&label=)](https://www.npmjs.com/package/cjson) | ✅ | `.json`
@web/config-loader | [![GitHub last commit](https://img.shields.io/github/last-commit/modernweb-dev/web?style=flat&label=)](https://github.com/modernweb-dev/web/commits) | [![NPM Downloads](https://img.shields.io/npm/dm/@web/config-loader.svg?style=flat&label=)](https://www.npmjs.com/package/@web/config-loader) | ❌ | `.js`, `.mjs`, `.cjs`
## Contributors
As always, thanks to our amazing contributors!
<a href="https://github.com/jaywcjlove/auto-config-loader/graphs/contributors">
<img src="https://jaywcjlove.github.io/auto-config-loader/CONTRIBUTORS.svg" />
</a>
Made with [contributors](https://github.com/jaywcjlove/github-action-contributors).
## License
This package is licensed under the MIT License.

View File

@@ -0,0 +1,38 @@
import { LoadConfOption } from './loader/js';
export * from './utils';
export * from './loader/js';
export * from './loader/json';
export * from './loader/yaml';
export * from './loader/toml';
export * from './loader/ini';
export declare const merge: {
<TObject, TSource>(object: TObject, source: TSource): TObject & TSource;
<TObject, TSource1, TSource2>(object: TObject, source1: TSource1, source2: TSource2): TObject & TSource1 & TSource2;
<TObject, TSource1, TSource2, TSource3>(object: TObject, source1: TSource1, source2: TSource2, source3: TSource3): TObject & TSource1 & TSource2 & TSource3;
<TObject, TSource1, TSource2, TSource3, TSource4>(object: TObject, source1: TSource1, source2: TSource2, source3: TSource3, source4: TSource4): TObject & TSource1 & TSource2 & TSource3 & TSource4;
(object: any, ...otherArgs: any[]): any;
};
export type LoaderFunc<T> = (filepath: string, content: string, jsOption?: LoadConfOption) => T | Promise<T>;
export type Loader<T> = Record<string, LoaderFunc<T>>;
export interface AutoConfOption<T> {
searchPlaces?: string[];
/** An object that maps extensions to the loader functions responsible for loading and parsing files with those extensions. */
loaders?: Loader<T>;
/** Specify default configuration. It has the lowest priority and is applied after extending config. */
default?: T;
/** Resolve configuration from this working directory. The default is `process.cwd()` */
cwd?: string;
/** Default transform js configuration */
jsOption?: LoadConfOption;
/** @deprecated use `mustExist` instead */
ignoreLog?: boolean;
mustExist?: boolean;
}
export declare const getConfigPath: () => string;
/**
* Find and load configuration from a `package.json` property, `rc` file, or `CommonJS` module.
* @param namespace {string} Configuration base name. The default is `autoconf`.
* @param option
*/
export declare function autoConf<T>(namespace?: string, option?: AutoConfOption<T>): Promise<{} & T>;
export default autoConf;

View File

@@ -0,0 +1,103 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getConfigPath = exports.merge = void 0;
exports.autoConf = autoConf;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const lodash_merge_1 = __importDefault(require("lodash.merge"));
const js_1 = require("./loader/js");
const json_1 = require("./loader/json");
const yaml_1 = require("./loader/yaml");
const toml_1 = require("./loader/toml");
const ini_1 = require("./loader/ini");
const utils_1 = require("./utils");
__exportStar(require("./utils"), exports);
__exportStar(require("./loader/js"), exports);
__exportStar(require("./loader/json"), exports);
__exportStar(require("./loader/yaml"), exports);
__exportStar(require("./loader/toml"), exports);
__exportStar(require("./loader/ini"), exports);
exports.merge = lodash_merge_1.default;
let configPath = '';
const getConfigPath = () => configPath;
exports.getConfigPath = getConfigPath;
/**
* Find and load configuration from a `package.json` property, `rc` file, or `CommonJS` module.
* @param namespace {string} Configuration base name. The default is `autoconf`.
* @param option
*/
async function autoConf(namespace = 'autoconf', option = {}) {
const { searchPlaces = [], default: defaultValue = {}, cwd = process.cwd(), ignoreLog = false, mustExist = ignoreLog || false, jsOption, } = option;
const loaders = {
'.yml': yaml_1.yamlLoader,
'.yaml': yaml_1.yamlLoader,
'.ini': ini_1.iniLoader,
'.toml': toml_1.tomlLoader,
'.json': json_1.jsonLoader,
'.json5': json_1.jsonLoader,
'.jsonc': json_1.jsonLoader,
'.js': js_1.jsLoader,
'.ts': js_1.jsLoader,
'.cjs': js_1.jsLoader,
'.mjs': js_1.jsLoader,
...(option.loaders || {}),
};
const pkgPath = path_1.default.resolve(cwd, 'package.json');
configPath = (0, utils_1.findConfigFile)(namespace, cwd, searchPlaces);
let content = '';
let resultData;
let loaderFunc;
try {
if (configPath) {
const extname = path_1.default.extname(configPath);
const basename = path_1.default.basename(configPath);
if (new RegExp(`^(.?${namespace}rc)$`).test(basename)) {
content = fs_1.default.readFileSync(configPath, 'utf-8');
loaderFunc = loaders['.json'];
}
else if (loaders[extname]) {
content = fs_1.default.readFileSync(configPath, 'utf-8');
loaderFunc = loaders[extname];
}
}
else if (fs_1.default.existsSync(pkgPath)) {
content = fs_1.default.readFileSync(pkgPath, 'utf-8');
const result = loaders['.json'](configPath, content);
resultData = result[namespace];
}
if (content && loaderFunc) {
resultData = await loaderFunc(configPath, content, jsOption);
if (typeof resultData === 'function') {
return (0, exports.merge)(defaultValue, resultData, { default: resultData });
}
}
if (!!mustExist && !configPath && !resultData) {
return null;
}
if (resultData) {
return (0, exports.merge)(defaultValue, resultData);
}
console.log(`AUTO_CONF:ERROR: \x1b[31;1mCan't find config file\x1b[0m`);
}
catch (error) {
console.log(`AUTO_CONF:CATCH:ERROR: \x1b[31;1m${error}\x1b[0m`);
}
}
exports.default = autoConf;

View File

@@ -0,0 +1 @@
export declare function iniLoader<T>(_: string, content: string): T;

View File

@@ -0,0 +1,10 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.iniLoader = iniLoader;
const ini_1 = __importDefault(require("ini"));
function iniLoader(_, content) {
return ini_1.default.parse(content);
}

View File

@@ -0,0 +1,12 @@
import type jiti from 'jiti';
import { Options } from 'sucrase';
type Jiti = ReturnType<typeof jiti>;
type JITIOptions = Jiti['options'];
export interface LoadConfOption {
jiti?: boolean;
jitiOptions?: JITIOptions;
transformOption?: Options;
}
export declare function loadConf<T>(path: string, option?: LoadConfOption): Promise<T>;
export declare function jsLoader<T>(filepath: string, content: string, option?: LoadConfOption): Promise<T>;
export {};

View File

@@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadConf = loadConf;
exports.jsLoader = jsLoader;
const jiti_1 = require("jiti");
const sucrase_1 = require("sucrase");
let jitiInstance = null;
function lazyJiti(option = {}, transformOpt = {}) {
return (jitiInstance ??
(jitiInstance = (0, jiti_1.createJiti)(__filename, {
interopDefault: true,
...option,
transform: (opts) => {
return (0, sucrase_1.transform)(opts.source, {
transforms: ['typescript', 'imports'],
...transformOpt,
});
},
})));
}
async function loadConf(path, option = {}) {
const { jiti = true, jitiOptions, transformOption } = option;
let config = await (async function () {
try {
if (jiti) {
return path ? await lazyJiti(jitiOptions, transformOption).import(path) : {};
}
else {
return path ? require(path) : {};
}
}
catch {
return await lazyJiti(jitiOptions, transformOption).import(path);
}
})();
// Ensure both default export and named exports are handled
if (config.default) {
if (typeof config.default === 'function') {
const defaultFunc = config.default;
Object.assign(defaultFunc, config);
return defaultFunc;
}
else {
config = { ...config.default, ...config };
}
}
// ⚠️ 🚨 For some reason, the CI test default does not exist on the Windows platform.
// To ensure platform consistency, default has been removed.
if (path && path.endsWith('.cjs')) {
delete config.default;
}
return config;
}
async function jsLoader(filepath, content, option = {}) {
return await loadConf(filepath, option);
}

View File

@@ -0,0 +1 @@
export declare function jsonLoader<T>(_: string, content: string): T;

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.jsonLoader = jsonLoader;
const jsonc_eslint_parser_1 = require("jsonc-eslint-parser");
function jsonLoader(_, content) {
const ast = (0, jsonc_eslint_parser_1.parseJSON)(content);
return (0, jsonc_eslint_parser_1.getStaticJSONValue)(ast);
}

View File

@@ -0,0 +1 @@
export declare function tomlLoader<T>(_: string, content: string): T;

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.tomlLoader = tomlLoader;
const toml_eslint_parser_1 = require("toml-eslint-parser");
function tomlLoader(_, content) {
const ast = (0, toml_eslint_parser_1.parseTOML)(content);
return (0, toml_eslint_parser_1.getStaticTOMLValue)(ast);
}

View File

@@ -0,0 +1 @@
export declare function yamlLoader<T>(_: string, content: string): T;

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.yamlLoader = yamlLoader;
const yaml_eslint_parser_1 = require("yaml-eslint-parser");
function yamlLoader(_, content) {
const ast = (0, yaml_eslint_parser_1.parseYAML)(content);
return (0, yaml_eslint_parser_1.getStaticYAMLValue)(ast);
}

View File

@@ -0,0 +1 @@
export declare function findConfigFile(moduleName: string, root: string, searchPlaces?: string[]): string;

View File

@@ -0,0 +1,48 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findConfigFile = findConfigFile;
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
function findConfigFile(moduleName, root, searchPlaces = []) {
const data = [
...searchPlaces,
`.${moduleName}rc`,
`.${moduleName}rc.json`,
`.${moduleName}rc.json5`,
`.${moduleName}rc.jsonc`,
`.${moduleName}rc.yaml`,
`.${moduleName}rc.yml`,
`.${moduleName}rc.toml`,
`.${moduleName}rc.ini`,
`.${moduleName}rc.js`,
`.${moduleName}rc.ts`,
`.${moduleName}rc.cjs`,
`.${moduleName}rc.mjs`,
`.config/${moduleName}rc`,
`.config/${moduleName}rc.json`,
`.config/${moduleName}rc.json5`,
`.config/${moduleName}rc.jsonc`,
`.config/${moduleName}rc.yaml`,
`.config/${moduleName}rc.yml`,
`.config/${moduleName}rc.toml`,
`.config/${moduleName}rc.ini`,
`.config/${moduleName}rc.js`,
`.config/${moduleName}rc.ts`,
`.config/${moduleName}rc.cjs`,
`.config/${moduleName}rc.mjs`,
`${moduleName}.config.js`,
`${moduleName}.config.ts`,
`${moduleName}.config.cjs`,
`${moduleName}.config.mjs`,
];
for (const file of data) {
const filePath = path_1.default.resolve(root, file);
if (fs_1.default.existsSync(filePath)) {
return filePath;
}
}
return null;
}

View File

@@ -0,0 +1,2 @@
export declare function loadConf<T>(path: string): T;
export declare function importDefault<T>(filepath: string): T;

View File

@@ -0,0 +1,68 @@
{
"name": "auto-config-loader",
"version": "2.0.2",
"description": "Find and load configuration from a package.json property, rc file, or CommonJS module.",
"main": "./lib/index.js",
"type": "commonjs",
"types": "./lib/index.d.ts",
"exports": {
".": "./lib/index.js",
"./load-conf": "./lib/loader/js.js"
},
"homepage": "https://jaywcjlove.github.io/auto-config-loader",
"funding": "https://jaywcjlove.github.io/#/sponsor",
"repository": {
"type": "git",
"url": "https://github.com/jaywcjlove/auto-config-loader.git"
},
"keywords": [
"rc",
"config",
"loader",
"load",
"configuration",
"cjs",
"commonjs",
"esm",
"es module",
"yml-config",
"js-config",
"ts-config",
"toml-config",
"yml",
"js",
"ts",
"toml",
"typescript",
"config-loader",
"auto-config-loader"
],
"author": "jaywcjlove",
"license": "MIT",
"files": [
"load-conf.d.ts",
"lib",
"src"
],
"jest": {
"transformIgnorePatterns": [
"<rootDir>/node_modules/?!(.*)"
]
},
"engines": {
"node": ">=16.0.0"
},
"dependencies": {
"ini": "^5.0.0",
"jiti": "^2.4.1",
"jsonc-eslint-parser": "^2.3.0",
"lodash.merge": "^4.6.2",
"sucrase": "^3.35.0",
"toml-eslint-parser": "^0.10.0",
"yaml-eslint-parser": "^1.2.2"
},
"devDependencies": {
"@types/ini": "^4.0.0",
"@types/lodash.merge": "^4.6.7"
}
}

View File

@@ -0,0 +1,109 @@
import fs from 'fs';
import path from 'path';
import mergeFun from 'lodash.merge';
import { jsLoader, LoadConfOption } from './loader/js';
import { jsonLoader } from './loader/json';
import { yamlLoader } from './loader/yaml';
import { tomlLoader } from './loader/toml';
import { iniLoader } from './loader/ini';
import { findConfigFile } from './utils';
export * from './utils';
export * from './loader/js';
export * from './loader/json';
export * from './loader/yaml';
export * from './loader/toml';
export * from './loader/ini';
export const merge = mergeFun;
export type LoaderFunc<T> = (filepath: string, content: string, jsOption?: LoadConfOption) => T | Promise<T>;
export type Loader<T> = Record<string, LoaderFunc<T>>;
export interface AutoConfOption<T> {
searchPlaces?: string[];
/** An object that maps extensions to the loader functions responsible for loading and parsing files with those extensions. */
loaders?: Loader<T>;
/** Specify default configuration. It has the lowest priority and is applied after extending config. */
default?: T;
/** Resolve configuration from this working directory. The default is `process.cwd()` */
cwd?: string;
/** Default transform js configuration */
jsOption?: LoadConfOption;
/** @deprecated use `mustExist` instead */
ignoreLog?: boolean;
mustExist?: boolean;
}
let configPath = '';
export const getConfigPath = () => configPath;
/**
* Find and load configuration from a `package.json` property, `rc` file, or `CommonJS` module.
* @param namespace {string} Configuration base name. The default is `autoconf`.
* @param option
*/
export async function autoConf<T>(namespace: string = 'autoconf', option: AutoConfOption<T> = {}) {
const {
searchPlaces = [],
default: defaultValue = {},
cwd = process.cwd(),
ignoreLog = false,
mustExist = ignoreLog || false,
jsOption,
} = option;
const loaders: Loader<T> = {
'.yml': yamlLoader,
'.yaml': yamlLoader,
'.ini': iniLoader,
'.toml': tomlLoader,
'.json': jsonLoader,
'.json5': jsonLoader,
'.jsonc': jsonLoader,
'.js': jsLoader,
'.ts': jsLoader,
'.cjs': jsLoader,
'.mjs': jsLoader,
...(option.loaders || {}),
};
const pkgPath = path.resolve(cwd, 'package.json');
configPath = findConfigFile(namespace, cwd, searchPlaces);
let content = '';
let resultData: T;
let loaderFunc: LoaderFunc<T>;
try {
if (configPath) {
const extname = path.extname(configPath);
const basename = path.basename(configPath);
if (new RegExp(`^(.?${namespace}rc)$`).test(basename)) {
content = fs.readFileSync(configPath, 'utf-8');
loaderFunc = loaders['.json'];
} else if (loaders[extname]) {
content = fs.readFileSync(configPath, 'utf-8');
loaderFunc = loaders[extname];
}
} else if (fs.existsSync(pkgPath)) {
content = fs.readFileSync(pkgPath, 'utf-8');
const result = loaders['.json'](configPath, content);
resultData = (result as Record<string, T>)[namespace];
}
if (content && loaderFunc) {
resultData = await loaderFunc(configPath, content, jsOption);
if (typeof resultData === 'function') {
return merge(defaultValue, resultData, { default: resultData });
}
}
if (!!mustExist && !configPath && !resultData) {
return null;
}
if (resultData) {
return merge(defaultValue, resultData);
}
console.log(`AUTO_CONF:ERROR: \x1b[31;1mCan't find config file\x1b[0m`);
} catch (error) {
console.log(`AUTO_CONF:CATCH:ERROR: \x1b[31;1m${error}\x1b[0m`);
}
}
export default autoConf;

View File

@@ -0,0 +1,5 @@
import ini from 'ini';
export function iniLoader<T>(_: string, content: string): T {
return ini.parse(content) as T;
}

View File

@@ -0,0 +1,67 @@
import { createJiti } from 'jiti';
import type jiti from 'jiti';
import { transform, Options } from 'sucrase';
type Jiti = ReturnType<typeof jiti>;
type JITIOptions = Jiti['options'];
let jitiInstance: ReturnType<typeof jiti> | null = null;
function lazyJiti(option: JITIOptions = {}, transformOpt = {} as Options) {
return (
jitiInstance ??
(jitiInstance = createJiti(__filename, {
interopDefault: true,
...option,
transform: (opts) => {
return transform(opts.source, {
transforms: ['typescript', 'imports'],
...transformOpt,
});
},
}))
);
}
export interface LoadConfOption {
jiti?: boolean;
jitiOptions?: JITIOptions;
transformOption?: Options;
}
export async function loadConf<T>(path: string, option: LoadConfOption = {}): Promise<T> {
const { jiti = true, jitiOptions, transformOption } = option;
let config = await (async function () {
try {
if (jiti) {
return path ? await lazyJiti(jitiOptions, transformOption).import(path) : {};
} else {
return path ? require(path) : {};
}
} catch {
return await lazyJiti(jitiOptions, transformOption).import(path);
}
})();
// Ensure both default export and named exports are handled
if (config.default) {
if (typeof config.default === 'function') {
const defaultFunc = config.default;
Object.assign(defaultFunc, config);
return defaultFunc;
} else {
config = { ...config.default, ...config };
}
}
// ⚠️ 🚨 For some reason, the CI test default does not exist on the Windows platform.
// To ensure platform consistency, default has been removed.
if (path && path.endsWith('.cjs')) {
delete config.default;
}
return config;
}
export async function jsLoader<T>(filepath: string, content: string, option: LoadConfOption = {}): Promise<T> {
return await loadConf<T>(filepath, option);
}

View File

@@ -0,0 +1,7 @@
import type { AST } from 'jsonc-eslint-parser';
import { parseJSON, getStaticJSONValue } from 'jsonc-eslint-parser';
export function jsonLoader<T>(_: string, content: string): T {
const ast: AST.JSONProgram = parseJSON(content);
return getStaticJSONValue(ast) as T;
}

View File

@@ -0,0 +1,7 @@
import type { AST } from 'toml-eslint-parser';
import { parseTOML, getStaticTOMLValue } from 'toml-eslint-parser';
export function tomlLoader<T>(_: string, content: string): T {
const ast: AST.TOMLProgram = parseTOML(content);
return getStaticTOMLValue(ast) as T;
}

View File

@@ -0,0 +1,7 @@
import type { AST } from 'yaml-eslint-parser';
import { parseYAML, getStaticYAMLValue } from 'yaml-eslint-parser';
export function yamlLoader<T>(_: string, content: string): T {
const ast: AST.YAMLProgram = parseYAML(content);
return getStaticYAMLValue(ast) as T;
}

View File

@@ -0,0 +1,44 @@
import path from 'path';
import fs from 'fs';
export function findConfigFile(moduleName: string, root: string, searchPlaces: string[] = []) {
const data = [
...searchPlaces,
`.${moduleName}rc`,
`.${moduleName}rc.json`,
`.${moduleName}rc.json5`,
`.${moduleName}rc.jsonc`,
`.${moduleName}rc.yaml`,
`.${moduleName}rc.yml`,
`.${moduleName}rc.toml`,
`.${moduleName}rc.ini`,
`.${moduleName}rc.js`,
`.${moduleName}rc.ts`,
`.${moduleName}rc.cjs`,
`.${moduleName}rc.mjs`,
`.config/${moduleName}rc`,
`.config/${moduleName}rc.json`,
`.config/${moduleName}rc.json5`,
`.config/${moduleName}rc.jsonc`,
`.config/${moduleName}rc.yaml`,
`.config/${moduleName}rc.yml`,
`.config/${moduleName}rc.toml`,
`.config/${moduleName}rc.ini`,
`.config/${moduleName}rc.js`,
`.config/${moduleName}rc.ts`,
`.config/${moduleName}rc.cjs`,
`.config/${moduleName}rc.mjs`,
`${moduleName}.config.js`,
`${moduleName}.config.ts`,
`${moduleName}.config.cjs`,
`${moduleName}.config.mjs`,
];
for (const file of data) {
const filePath = path.resolve(root, file);
if (fs.existsSync(filePath)) {
return filePath;
}
}
return null;
}