<commit_before>import { readFile, readJSON } from "fs-extra"
import { parse as dotenvParse } from "dotenv"
import { stringFormat } from "@brycemarshall/string-format"
import minimatch from "minimatch"

export interface IStringLeafNodeProcessor {
  (stringNodePath: string, toInterpolate: string): string
}

export interface IStringLeafNodeInterpolator {
  (dictionary: object): IStringLeafNodeProcessor
}

export interface IPathMatcher {
  (currentPath: string): boolean
}

export interface IGlobPathMatcher {
  (matchPathGlob: string): IPathMatcher
}

export const globPathMatcher: IGlobPathMatcher = (matchPathGlob: string) => (
  currentPath: string
) => minimatch(currentPath, matchPathGlob) as boolean

export const matchedLeafInterpolator = (
  dictionary: object,
  matches: IPathMatcher = globPathMatcher("**")
) => (stringNodePath: string, stringLeafNode: string): string =>
  matches(stringNodePath)
    ? stringFormat(stringLeafNode, dictionary)
    : stringLeafNode

interface IMapper {
  (key: string, val: any): any
}

function mapObj(obj: object, mapper: IMapper) {
  return Object.assign(
    {},
    ...Object.entries(obj).map(([key, val]: [string, any]) => ({
      [key]: mapper(key, val)
    }))
  )
}

export function stringProcessingTraverser(
  obj: any,
  stringLeafNodeProcessor: IStringLeafNodeProcessor,
  parentKeys: string[] = []
) {
  const mapper: IMapper = (key: string, val: any) =>
    val === null
      ? null
      : typeof val === "string"
        ? stringLeafNodeProcessor(parentKeys.join("."), val as string)
        : typeof val === "object"
          ? stringProcessingTraverser(val, stringLeafNodeProcessor, parentKeys)
          : val
  return mapObj(obj, mapper)
}

export function interpolateStringLeafNodes(obj: object, dict: object): object {
  return stringProcessingTraverser(obj, matchedLeafInterpolator(dict))
}

export async function interpolateJson(
  pathToFileToInterpolate: string,
  pathToDotEnvFile: string
) {
  const fileToInterpolatePromise = readJSON(pathToFileToInterpolate).catch(
    err => err
  )
  const dotEnvfile = await readFile(pathToDotEnvFile).catch(err => err)
  const varsToInterpolate = dotenvParse(dotEnvfile)
  const fileToInterpolate = await fileToInterpolatePromise
  return interpolateStringLeafNodes(fileToInterpolate, varsToInterpolate)
}<commit_msg>build(fs-extra): switch to using default export<commit_after>import fs from "fs-extra"
import { parse as dotenvParse } from "dotenv"
import { stringFormat } from "@brycemarshall/string-format"
import minimatch from "minimatch"

export interface IStringLeafNodeProcessor {
  (stringNodePath: string, toInterpolate: string): string
}

export interface IStringLeafNodeInterpolator {
  (dictionary: object): IStringLeafNodeProcessor
}

export interface IPathMatcher {
  (currentPath: string): boolean
}

export interface IGlobPathMatcher {
  (matchPathGlob: string): IPathMatcher
}

export const globPathMatcher: IGlobPathMatcher = (matchPathGlob: string) => (
  currentPath: string
) => minimatch(currentPath, matchPathGlob) as boolean

export const matchedLeafInterpolator = (
  dictionary: object,
  matches: IPathMatcher = globPathMatcher("**")
) => (stringNodePath: string, stringLeafNode: string): string =>
  matches(stringNodePath)
    ? stringFormat(stringLeafNode, dictionary)
    : stringLeafNode

interface IMapper {
  (key: string, val: any): any
}

function mapObj(obj: object, mapper: IMapper) {
  return Object.assign(
    {},
    ...Object.entries(obj).map(([key, val]: [string, any]) => ({
      [key]: mapper(key, val)
    }))
  )
}

export function stringProcessingTraverser(
  obj: any,
  stringLeafNodeProcessor: IStringLeafNodeProcessor,
  parentKeys: string[] = []
) {
  const mapper: IMapper = (key: string, val: any) =>
    val === null
      ? null
      : typeof val === "string"
        ? stringLeafNodeProcessor(parentKeys.join("."), val as string)
        : typeof val === "object"
          ? stringProcessingTraverser(val, stringLeafNodeProcessor, parentKeys)
          : val
  return mapObj(obj, mapper)
}

export function interpolateStringLeafNodes(obj: object, dict: object): object {
  return stringProcessingTraverser(obj, matchedLeafInterpolator(dict))
}

export async function interpolateJson(
  pathToFileToInterpolate: string,
  pathToDotEnvFile: string
) {
  const fileToInterpolatePromise = fs
    .readJSON(pathToFileToInterpolate)
    .catch(err => err)
  const dotEnvfile = await fs.readFile(pathToDotEnvFile).catch(err => err)
  const varsToInterpolate = dotenvParse(dotEnvfile)
  const fileToInterpolate = await fileToInterpolatePromise
  return interpolateStringLeafNodes(fileToInterpolate, varsToInterpolate)
}
{'fraction_non_alphanumeric': 0.10507246376811594, 'fraction_numerical': 0.0, 'mean_word_length': 4.985969387755102, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3471350', 'n_tokens_mistral': 1448, 'n_tokens_neox': 1436, 'n_words': 343}