text: 
  import type Logger from "../logger";
  import resolve from "../../../helpers/resolve";
  import json5 from "json5";
  import isAbsolute from "path-is-absolute";
  import path from "path";
  import fs from "fs";
  
  const existsCache = {};
  const jsonCache   = {};
  
  const BABELIGNORE_FILENAME = ".babelignore";
  const BABELRC_FILENAME     = ".babelrc";
  const PACKAGE_FILENAME     = "package.json";
  
  function exists(filename) {
    const cached = existsCache[filename];
    if (cached == null) {
      return existsCache[filename] = fs.existsSync(filename);
    } else {
      return cached;
    }
  }
  
  export default function buildConfigChain(opts: Object = {}, log?: Logger) {
    const filename = opts.filename;
    const builder = new ConfigChainBuilder(log);
  
    // resolve all .babelrc files
    if (opts.babelrc !== false) {
      builder.findConfigs(filename);
    }
  
    builder.mergeConfig({
      options: opts,
      alias: "base",
      dirname: filename && path.dirname(filename)
    });
  
    return builder.configs;
  }
  
  class ConfigChainBuilder {
    constructor(log?: Logger) {
      this.resolvedConfigs = [];
      this.configs = [];
      this.log = log;
    }
  
    findConfigs(loc) {
      if (!loc) return;
  
      if (!isAbsolute(loc)) {
        loc = path.join(process.cwd(), loc);
      }
  
      let foundConfig = false;
      let foundIgnore = false;
  
      while (loc !== (loc = path.dirname(loc))) {
        if (!foundConfig) {
          const configLoc = path.join(loc, BABELRC_FILENAME);
          if (exists(configLoc)) {
            this.addConfig(configLoc);
            foundConfig = true;
          }
  
          const pkgLoc = path.join(loc, PACKAGE_FILENAME);
          if (!foundConfig && exists(pkgLoc)) {
            foundConfig = this.addConfig(pkgLoc, "babel", JSON);
          }
        }
  
        if (!foundIgnore) {
          const ignoreLoc = path.join(loc, BABELIGNORE_FILENAME);
          if (exists(ignoreLoc)) {
            this.addIgnoreConfig(ignoreLoc);
            foundIgnore = true;
          }
        }
  
        if (foundIgnore && foundConfig) return;
      }
    }
  
    addIgnoreConfig(loc) {
      const file  = fs.readFileSync(loc, "utf8");
      let lines = file.split("\n");
  
      lines = lines
        .map((line) => line.replace(/#(.*?)$/, "").trim())
        .filter((line) => !!line);
  
      if (lines.length) {
        this.mergeConfig({
          options: { ignore: lines },
          alias: loc,
          dirname: path.dirname(loc)
        });
      }
    }
  
    addConfig(loc: string, key?: string, json = json5): boolean {
      if (this.resolvedConfigs.indexOf(loc) >= 0) {
        return false;
      }
  
      this.resolvedConfigs.push(loc);
  
      const content = fs.readFileSync(loc, "utf8");
      let options;
  
      try {
        options = jsonCache[content] = jsonCache[content] || json.parse(content);
        if (key) options = options[key];
      } catch (err) {
        err.message = `${loc}: Error while parsing JSON - ${err.message}`;
        throw err;
      }
  
      this.mergeConfig({
        options,
        alias: loc,
        dirname: path.dirname(loc)
      });
  
      return !!options;
    }
  
    mergeConfig({
      options,
      alias,
      loc,
      dirname
    }) {
      if (!options) {
        return false;
      }
  
      options = Object.assign({}, options);
  
      dirname = dirname || process.cwd();
      loc = loc || alias;
  
      // add extends clause
      if (options.extends) {
        const extendsLoc = resolve(options.extends, dirname);
        if (extendsLoc) {
          this.addConfig(extendsLoc);
        } else {
          if (this.log) this.log.error(`Couldn't resolve extends clause of ${options.extends} in ${alias}`);
        }
        delete options.extends;
      }
  
      this.configs.push({
        options,
        alias,
        loc,
        dirname
      });
  
      // env
      let envOpts;
      const envKey = process.env.BABEL_ENV || process.env.NODE_ENV || "development";
      if (options.env) {
        envOpts = options.env[envKey];
        delete options.env;
      }
  
      this.mergeConfig({
        options: envOpts,
        alias: `${alias}.env.${envKey}`,
        dirname: dirname
      });
    }
  }
  
  