All files / src index.ts

100% Statements 33/33
94.11% Branches 32/34
100% Functions 2/2
100% Lines 32/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112                                  3x                                     3x   3x                             42x 42x                           42x 42x 42x     42x 42x 35x 35x 35x 4x 4x 31x 31x 31x       42x 4x 4x 4x     42x 35x 34x 2x     39x 2x   37x 2x   35x   1x          
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 Eif (loaders[extname]) {
        content = fs.readFileSync(configPath, 'utf-8');
        loaderFunc = loaders[extname];
      }
    }
    let pkgContent: T;
    if (fs.existsSync(pkgPath)) {
      content = fs.readFileSync(pkgPath, 'utf-8');
      const result = loaders['.json'](pkgPath, content);
      pkgContent = (result as Record<string, T>)[namespace];
    }
 
    if (content && loaderFunc) {
      resultData = await loaderFunc(configPath, content, jsOption);
      if (typeof resultData === 'function') {
        return merge(defaultValue, resultData ?? {}, { ...pkgContent }, { default: resultData });
      }
    }
    if (pkgContent) {
      return merge(defaultValue, { ...resultData }, { ...pkgContent });
    }
    if (!!mustExist && !configPath && !resultData) {
      return null;
    }
    return merge(defaultValue, { ...resultData }, { ...pkgContent });
  } catch (error) {
    console.log(`AUTO_CONF:CATCH:ERROR: \x1b[31;1m${error}\x1b[0m`);
  }
}
 
export default autoConf;