<commit_before>import path = require('path')

import json5 = require('json5')

import BabelOptions, {ReducedOptions} from './BabelOptions'
import Cache from './Cache'
import {ExtendsError, InvalidFileError, MultipleSourcesError, NoSourceFileError, ParseError} from './errors'
import readSafe from './readSafe'

function makeValid (source: string, options: any): BabelOptions {
  // Arrays are never valid options.
  if (Array.isArray(options)) throw new InvalidFileError(source)

  // Force options to be an object. Babel itself ignores falsy values when
  // resolving config chains. Here such files still need to be included
  // for cache busting purposes.
  if (!options || typeof options !== 'object') return {}

  return options
}

function parseFile (source: string, buffer: Buffer): BabelOptions {
  let options
  try {
    options = json5.parse(buffer.toString('utf8'))
  } catch (err) {
    throw new ParseError(source, err)
  }

  return makeValid(source, options)
}

function parsePackage (source: string, buffer: Buffer): BabelOptions | null {
  let options
  try {
    const pkg = JSON.parse(buffer.toString('utf8'))
    if (!pkg || typeof pkg !== 'object' || !has(pkg, 'babel')) return null

    options = pkg.babel
  } catch (err) {
    throw new ParseError(source, err)
  }

  return makeValid(source, options)
}

export class Config {
  public readonly dir: string
  public readonly env: string | null
  public readonly hash: string | null
  public readonly json5: boolean
  public readonly options: BabelOptions
  public readonly source: string

  public babelrcPointer: number | null
  public extends: Config | null
  public extendsPointer: number | null
  public readonly envPointers: Map<string, number>

  public constructor (
    dir: string,
    env: string | null,
    hash: string | null,
    json5: boolean,
    options: BabelOptions,
    source: string
  ) {
    this.dir = dir
    this.env = env
    this.hash = hash
    this.json5 = json5
    this.options = options
    this.source = source

    this.babelrcPointer = null
    this.envPointers = new Map()
    this.extends = null
    this.extendsPointer = null
  }

  public copyWithEnv (env: string, options: BabelOptions): Config {
    return new Config(this.dir, env, this.hash, this.json5, options, this.source)
  }

  public extend (config: Config): void {
    const clause = this.takeExtends()
    if (clause) {
      throw new TypeError(`Cannot extend config: there is an extends clause in the current options: ${clause}`)
    }
    if (this.extends) {
      throw new Error('Cannot extend config: already extended')
    }

    this.extends = config
  }

  public takeEnvs (): Map<string, ReducedOptions> {
    const env = this.options.env
    delete this.options.env

    if (!env) return new Map()

    const take = Object.keys(env).filter(Boolean).map<[string, ReducedOptions]>(name => [name, env[name]])
    return new Map(take)
  }

  public takeExtends (): string | undefined {
    const clause = this.options.extends
    delete this.options.extends
    return clause
  }<commit_msg>Allow env keys that are empty strings<commit_after>import path = require('path')

import json5 = require('json5')

import BabelOptions, {ReducedOptions} from './BabelOptions'
import Cache from './Cache'
import {ExtendsError, InvalidFileError, MultipleSourcesError, NoSourceFileError, ParseError} from './errors'
import readSafe from './readSafe'

function makeValid (source: string, options: any): BabelOptions {
  // Arrays are never valid options.
  if (Array.isArray(options)) throw new InvalidFileError(source)

  // Force options to be an object. Babel itself ignores falsy values when
  // resolving config chains. Here such files still need to be included
  // for cache busting purposes.
  if (!options || typeof options !== 'object') return {}

  return options
}

function parseFile (source: string, buffer: Buffer): BabelOptions {
  let options
  try {
    options = json5.parse(buffer.toString('utf8'))
  } catch (err) {
    throw new ParseError(source, err)
  }

  return makeValid(source, options)
}

function parsePackage (source: string, buffer: Buffer): BabelOptions | null {
  let options
  try {
    const pkg = JSON.parse(buffer.toString('utf8'))
    if (!pkg || typeof pkg !== 'object' || !has(pkg, 'babel')) return null

    options = pkg.babel
  } catch (err) {
    throw new ParseError(source, err)
  }

  return makeValid(source, options)
}

export class Config {
  public readonly dir: string
  public readonly env: string | null
  public readonly hash: string | null
  public readonly json5: boolean
  public readonly options: BabelOptions
  public readonly source: string

  public babelrcPointer: number | null
  public extends: Config | null
  public extendsPointer: number | null
  public readonly envPointers: Map<string, number>

  public constructor (
    dir: string,
    env: string | null,
    hash: string | null,
    json5: boolean,
    options: BabelOptions,
    source: string
  ) {
    this.dir = dir
    this.env = env
    this.hash = hash
    this.json5 = json5
    this.options = options
    this.source = source

    this.babelrcPointer = null
    this.envPointers = new Map()
    this.extends = null
    this.extendsPointer = null
  }

  public copyWithEnv (env: string, options: BabelOptions): Config {
    return new Config(this.dir, env, this.hash, this.json5, options, this.source)
  }

  public extend (config: Config): void {
    const clause = this.takeExtends()
    if (clause) {
      throw new TypeError(`Cannot extend config: there is an extends clause in the current options: ${clause}`)
    }
    if (this.extends) {
      throw new Error('Cannot extend config: already extended')
    }

    this.extends = config
  }

  public takeEnvs (): Map<string, ReducedOptions> {
    const env = this.options.env
    delete this.options.env

    if (!env) return new Map()

    const take = Object.keys(env).map<[string, ReducedOptions]>(name => [name, env[name]])
    return new Map(take)
  }

  public takeExtends (): string | undefined {
    const clause = this.options.extends
    delete this.options.extends
    return clause
  }
{'fraction_non_alphanumeric': 0.10370009737098344, 'fraction_numerical': 0.0032456994482310936, 'mean_word_length': 3.478924418604651, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 11, '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': '2922408', 'n_tokens_mistral': 1825, 'n_tokens_neox': 1775, 'n_words': 610}