#!/usr/bin/env node
import fs from 'node:fs';
import * as commander from 'commander';
import * as babel from '@babel/core';
import { DEFAULT_EXTENSIONS, version } from '@babel/core';
import * as glob from 'glob';
import path from 'node:path';
import { createRequire } from 'node:module';
import slash from 'slash';
import convertSourceMap from 'convert-source-map';
import { encodedMap, AnyMap } from '@jridgewell/trace-mapping';

const fileToDeps = new Map();
const depToFiles = new Map();
let isWatchMode = false;
let watcher;
const watchQueue = new Set();
let hasStarted = false;
function enable({
  enableGlobbing
}) {
  isWatchMode = true;
  const {
    FSWatcher
  } = requireChokidar();
  const options = {
    disableGlobbing: !enableGlobbing,
    persistent: true,
    ignoreInitial: true,
    awaitWriteFinish: {
      stabilityThreshold: 50,
      pollInterval: 10
    }
  };
  watcher = new FSWatcher(options);
  watcher.on("unlink", unwatchFile);
}
function startWatcher() {
  hasStarted = true;
  for (const dep of watchQueue) {
    watcher.add(dep);
  }
  watchQueue.clear();
  watcher.on("ready", () => {
    console.log("The watcher is ready.");
  });
}
function watch(filename) {
  if (!isWatchMode) {
    throw new Error("Internal Babel error: .watch called when not in watch mode.");
  }
  if (!hasStarted) {
    watchQueue.add(path.resolve(filename));
  } else {
    watcher.add(path.resolve(filename));
  }
}
function onFilesChange(callback) {
  if (!isWatchMode) {
    throw new Error("Internal Babel error: .onFilesChange called when not in watch mode.");
  }
  watcher.on("all", (event, filename) => {
    if (event !== "change" && event !== "add") return;
    const absoluteFile = path.resolve(filename);
    callback([absoluteFile, ...(depToFiles.get(absoluteFile) ?? [])], event, absoluteFile);
  });
}
function updateExternalDependencies(filename, dependencies) {
  if (!isWatchMode) return;
  const absFilename = path.resolve(filename);
  const absDependencies = new Set(Array.from(dependencies, dep => path.resolve(dep)));
  const deps = fileToDeps.get(absFilename);
  if (deps) {
    for (const dep of deps) {
      if (!absDependencies.has(dep)) {
        removeFileDependency(absFilename, dep);
      }
    }
  }
  for (const dep of absDependencies) {
    let deps = depToFiles.get(dep);
    if (!deps) {
      depToFiles.set(dep, deps = new Set());
      if (!hasStarted) {
        watchQueue.add(dep);
      } else {
        watcher.add(dep);
      }
    }
    deps.add(absFilename);
  }
  fileToDeps.set(absFilename, absDependencies);
}
function removeFileDependency(filename, dep) {
  const deps = depToFiles.get(dep);
  deps.delete(filename);
  if (deps.size === 0) {
    depToFiles.delete(dep);
    if (!hasStarted) {
      watchQueue.delete(dep);
    } else {
      watcher.unwatch(dep);
    }
  }
}
function unwatchFile(filename) {
  const deps = fileToDeps.get(filename);
  if (!deps) return;
  for (const dep of deps) {
    removeFileDependency(filename, dep);
  }
  fileToDeps.delete(filename);
}
function requireChokidar() {
  const require = createRequire(import.meta.url);
  try {
    return require("chokidar");
  } catch (err) {
    console.error("The optional dependency chokidar failed to install and is required for " + "--watch. Chokidar is likely not supported on your platform.");
    throw err;
  }
}

function chmod(src, dest) {
  try {
    fs.chmodSync(dest, fs.statSync(src).mode);
  } catch (_) {
    console.warn(`Cannot change permissions of ${dest}`);
  }
}
function alphasort(a, b) {
  return a.localeCompare(b, "en");
}
function readdir(dirname, includeDotfiles, filter) {
  return fs.readdirSync(dirname, {
    recursive: true,
    withFileTypes: true
  }).filter(dirent => {
    if (dirent.isDirectory()) return false;
    const filename = dirent.name;
    return (includeDotfiles || !filename.startsWith(".")) && (!filter || filter(filename));
  }).map(dirent => path.join(dirent.parentPath, dirent.name)).sort(alphasort);
}
function readdirForCompilable(dirname, includeDotfiles, altExts) {
  return readdir(dirname, includeDotfiles, function (filename) {
    return isCompilableExtension(filename, altExts);
  });
}
function isCompilableExtension(filename, altExts) {
  const exts = altExts || babel.DEFAULT_EXTENSIONS;
  const ext = path.extname(filename);
  return exts.includes(ext);
}
function addSourceMappingUrl(code, loc) {
  return code + "\n//# sourceMappingURL=" + path.basename(loc);
}
function hasDataSourcemap(code) {
  const pos = code.lastIndexOf("\n", code.length - 2);
  return pos !== -1 && code.lastIndexOf("//# sourceMappingURL") < pos;
}
const REPL_CALLER = {
  name: "@babel/cli",
  supportsStaticESM: false,
  supportsDynamicImport: false,
  supportsExportNamespaceFrom: false
};
const COMPILE_CALLER = {
  name: "@babel/cli"
};
function transformRepl(filename, code, opts) {
  opts = {
    ...opts,
    sourceMaps: opts.sourceMaps === "inline" ? true : opts.sourceMaps,
    caller: REPL_CALLER,
    filename
  };
  return new Promise((resolve, reject) => {
    babel.transform(code, opts, (err, result) => {
      if (err) reject(err);else resolve(result);
    });
  });
}
async function compile(filename, opts) {
  opts = {
    ...opts,
    caller: COMPILE_CALLER
  };
  const result = await babel.transformFileAsync(filename, opts);
  if (result) {
    updateExternalDependencies(filename, result.externalDependencies);
  }
  return result;
}
function deleteDir(path) {
  fs.rmSync(path, {
    force: true,
    recursive: true
  });
}
process.on("uncaughtException", function (err) {
  console.error(err);
  process.exitCode = 1;
});
function withExtension(filename, ext = ".js") {
  const newBasename = path.basename(filename, path.extname(filename)) + ext;
  return path.join(path.dirname(filename), newBasename);
}
function debounce(fn, time) {
  let timer;
  function debounced() {
    clearTimeout(timer);
    timer = setTimeout(fn, time);
  }
  debounced.flush = () => {
    clearTimeout(timer);
    fn();
  };
  return debounced;
}

const program = commander.program;
program.option("-f, --filename [filename]", "The filename to use when reading from stdin. This will be used in source-maps, errors etc.");
program.option("--presets [list]", "A comma-separated list of preset names.", collect);
program.option("--plugins [list]", "A comma-separated list of plugin names.", collect);
program.option("--config-file [path]", "Path to a .babelrc file to use.");
program.option("--env-name [name]", "The name of the 'env' to use when loading configs and plugins. " + "Defaults to the value of BABEL_ENV, or else NODE_ENV, or else 'development'.");
program.option("--root-mode [mode]", "The project-root resolution mode. " + "One of 'root' (the default), 'upward', or 'upward-optional'.");
program.option("--source-type [script|module]", "");
program.option("--no-babelrc", "Whether or not to look up .babelrc and .babelignore files.");
program.option("--ignore [list]", "List of glob paths to **not** compile.", collect);
program.option("--only [list]", "List of glob paths to **only** compile.", collect);
program.option("--no-highlight-code", "Enable or disable ANSI syntax highlighting of code frames. (on by default)");
program.option("--no-comments", "Write comments to generated output. (true by default)");
program.option("--retain-lines", "Retain line numbers. This will result in really ugly code.");
program.option("--compact [true|false|auto]", "Do not include superfluous whitespace characters and line terminators.", booleanify);
program.option("--minified", "Save as many bytes when printing. (false by default)");
program.option("--auxiliary-comment-before [string]", "Print a comment before any injected non-user code.");
program.option("--auxiliary-comment-after [string]", "Print a comment after any injected non-user code.");
program.option("-s, --source-maps [true|false|inline|both]", "", booleanify, undefined);
program.option("--source-map-target [string]", "Set `file` on returned source map.");
program.option("--source-file-name [string]", "Set `sources[0]` on returned source map.");
program.option("--source-root [filename]", "The root from which all sources are relative.");
program.option("-x, --extensions [extensions]", "List of extensions to compile when a directory has been the input. [" + DEFAULT_EXTENSIONS.join() + "]", collect);
program.option("--keep-file-extension", "Preserve the file extensions of the input files.");
program.option("-w, --watch", "Recompile files on changes.");
program.option("--skip-initial-build", "Do not compile files before watching.");
program.option("-o, --out-file [out]", "Compile all input files into a single file.");
program.option("-d, --out-dir [out]", "Compile an input directory of modules into an output directory.");
program.option("--relative", "Compile into an output directory relative to input directory or file. Requires --out-dir [out]");
program.option("-D, --copy-files", "When compiling a directory copy over non-compilable files.");
program.option("--include-dotfiles", "Include dotfiles when compiling and copying non-compilable files.");
program.option("--no-copy-ignored", "Exclude ignored files when copying non-compilable files.");
program.option("--verbose", "Log everything. This option conflicts with --quiet");
program.option("--quiet", "Don't log anything. This option conflicts with --verbose");
program.option("--delete-dir-on-start", "Delete the out directory before compilation.");
program.option("--out-file-extension [string]", "Use a specific extension for the output files");
program.argument("[files...]", "List of files to compile.");
program.version("8.0.0" + " (@babel/core " + version + ")");
program.usage("[options] [files...]");
function parseArgv(args) {
  program.parse(args);
  const opts = program.opts();
  const errors = [];
  let filenames = program.args.reduce(function (globbed, input) {
    let files = glob.sync(input, {
      dotRelative: true
    }).sort(alphasort);
    if (!files.length) files = [input];
    globbed.push(...files);
    return globbed;
  }, []);
  filenames = Array.from(new Set(filenames));
  filenames.forEach(function (filename) {
    if (!fs.existsSync(filename)) {
      errors.push(filename + " does not exist");
    }
  });
  if (opts.outDir && !filenames.length) {
    errors.push("--out-dir requires filenames");
  }
  if (opts.outFile && opts.outDir) {
    errors.push("--out-file and --out-dir cannot be used together");
  }
  if (opts.relative && !opts.outDir) {
    errors.push("--relative requires --out-dir usage");
  }
  if (opts.watch) {
    if (!opts.outFile && !opts.outDir) {
      errors.push("--watch requires --out-file or --out-dir");
    }
    if (!filenames.length) {
      errors.push("--watch requires filenames");
    }
  }
  if (opts.skipInitialBuild && !opts.watch) {
    errors.push("--skip-initial-build requires --watch");
  }
  if (opts.deleteDirOnStart && !opts.outDir) {
    errors.push("--delete-dir-on-start requires --out-dir");
  }
  if (opts.verbose && opts.quiet) {
    errors.push("--verbose and --quiet cannot be used together");
  }
  if (!opts.outDir && filenames.length === 0 && typeof opts.filename !== "string" && opts.babelrc !== false) {
    errors.push("stdin compilation requires either -f/--filename [filename] or --no-babelrc");
  }
  if (opts.keepFileExtension && opts.outFileExtension) {
    errors.push("--out-file-extension cannot be used with --keep-file-extension");
  }
  if (errors.length) {
    console.error("babel:");
    errors.forEach(function (e) {
      console.error("  " + e);
    });
    return null;
  }
  const babelOptions = {
    presets: opts.presets,
    plugins: opts.plugins,
    rootMode: opts.rootMode,
    configFile: opts.configFile,
    envName: opts.envName,
    sourceType: opts.sourceType,
    ignore: opts.ignore,
    only: opts.only,
    retainLines: opts.retainLines,
    compact: opts.compact,
    minified: opts.minified,
    auxiliaryCommentBefore: opts.auxiliaryCommentBefore,
    auxiliaryCommentAfter: opts.auxiliaryCommentAfter,
    sourceMaps: opts.sourceMaps,
    sourceFileName: opts.sourceFileName,
    sourceRoot: opts.sourceRoot,
    babelrc: opts.babelrc === true ? undefined : opts.babelrc,
    highlightCode: opts.highlightCode === true ? undefined : opts.highlightCode,
    comments: opts.comments === true ? undefined : opts.comments
  };
  for (const key of Object.keys(babelOptions)) {
    if (babelOptions[key] === undefined) {
      delete babelOptions[key];
    }
  }
  return {
    babelOptions,
    cliOptions: {
      filename: opts.filename,
      filenames,
      extensions: opts.extensions,
      keepFileExtension: opts.keepFileExtension,
      outFileExtension: opts.outFileExtension,
      watch: opts.watch,
      skipInitialBuild: opts.skipInitialBuild,
      outFile: opts.outFile,
      outDir: opts.outDir,
      relative: opts.relative,
      copyFiles: opts.copyFiles,
      copyIgnored: opts.copyFiles && opts.copyIgnored,
      includeDotfiles: opts.includeDotfiles,
      verbose: opts.verbose,
      quiet: opts.quiet,
      deleteDirOnStart: opts.deleteDirOnStart,
      sourceMapTarget: opts.sourceMapTarget
    }
  };
}
function booleanify(val) {
  if (val === "true" || val === "1") {
    return true;
  }
  if (val === "false" || val === "0" || val === "") {
    return false;
  }
  return val;
}
function collect(value, previousValue) {
  if (typeof value !== "string") return previousValue;
  const values = value.split(",");
  if (previousValue) {
    previousValue.push(...values);
    return previousValue;
  }
  return values;
}

const FILE_TYPE = Object.freeze({
  NON_COMPILABLE: "NON_COMPILABLE",
  COMPILED: "COMPILED",
  IGNORED: "IGNORED",
  ERR_COMPILATION: "ERR_COMPILATION"
});
function outputFileSync(filePath, data) {
  fs.mkdirSync(path.dirname(filePath), {
    recursive: true
  });
  fs.writeFileSync(filePath, data);
}
async function dirCommand ({
  cliOptions,
  babelOptions
}) {
  async function write(src, base) {
    let relative = path.relative(base, src);
    if (!isCompilableExtension(relative, cliOptions.extensions)) {
      return FILE_TYPE.NON_COMPILABLE;
    }
    relative = withExtension(relative, cliOptions.keepFileExtension ? path.extname(relative) : cliOptions.outFileExtension);
    const dest = getDest(relative, base);
    try {
      const res = await compile(src, {
        ...babelOptions,
        sourceFileName: slash(path.relative(dest + "/..", src))
      });
      if (!res) return FILE_TYPE.IGNORED;
      if (res.map) {
        let outputMap = false;
        if (babelOptions.sourceMaps && babelOptions.sourceMaps !== "inline") {
          outputMap = "external";
        } else if (babelOptions.sourceMaps == null) {
          outputMap = hasDataSourcemap(res.code) ? "external" : "both";
        }
        if (outputMap) {
          const mapLoc = dest + ".map";
          if (outputMap === "external") {
            res.code = addSourceMappingUrl(res.code, mapLoc);
          }
          res.map.file = path.basename(relative);
          outputFileSync(mapLoc, JSON.stringify(res.map));
        }
      }
      outputFileSync(dest, res.code);
      chmod(src, dest);
      if (cliOptions.verbose) {
        console.log(path.relative(process.cwd(), src) + " -> " + dest);
      }
      return FILE_TYPE.COMPILED;
    } catch (err) {
      if (cliOptions.watch) {
        console.error(err);
        return FILE_TYPE.ERR_COMPILATION;
      }
      throw err;
    }
  }
  function getDest(filename, base) {
    if (cliOptions.relative) {
      return path.join(base, cliOptions.outDir, filename);
    }
    return path.join(cliOptions.outDir, filename);
  }
  async function handleFile(src, base) {
    const written = await write(src, base);
    if (cliOptions.copyFiles && written === FILE_TYPE.NON_COMPILABLE || cliOptions.copyIgnored && written === FILE_TYPE.IGNORED) {
      const filename = path.relative(base, src);
      const dest = getDest(filename, base);
      outputFileSync(dest, fs.readFileSync(src));
      chmod(src, dest);
    }
    return written === FILE_TYPE.COMPILED;
  }
  async function handle(filenameOrDir) {
    if (!fs.existsSync(filenameOrDir)) return 0;
    const stat = fs.statSync(filenameOrDir);
    if (stat.isDirectory()) {
      const dirname = filenameOrDir;
      let count = 0;
      const files = readdir(dirname, cliOptions.includeDotfiles);
      for (const filename of files) {
        const written = await handleFile(filename, dirname);
        if (written) count += 1;
      }
      return count;
    } else {
      const filename = filenameOrDir;
      const written = await handleFile(filename, path.dirname(filename));
      return written ? 1 : 0;
    }
  }
  let compiledFiles = 0;
  let startTime = null;
  const logSuccess = debounce(function () {
    if (startTime === null) {
      return;
    }
    const diff = process.hrtime(startTime);
    console.log(`Successfully compiled ${compiledFiles} ${compiledFiles !== 1 ? "files" : "file"} with Babel (${diff[0] * 1e3 + Math.round(diff[1] / 1e6)}ms).`);
    compiledFiles = 0;
    startTime = null;
  }, 100);
  if (cliOptions.watch) enable({
    enableGlobbing: true
  });
  if (!cliOptions.skipInitialBuild) {
    if (cliOptions.deleteDirOnStart) {
      deleteDir(cliOptions.outDir);
    }
    fs.mkdirSync(cliOptions.outDir, {
      recursive: true
    });
    startTime = process.hrtime();
    for (const filename of cliOptions.filenames) {
      compiledFiles += await handle(filename);
    }
    if (!cliOptions.quiet) {
      logSuccess();
      logSuccess.flush();
    }
  }
  if (cliOptions.watch) {
    let processing = 0;
    const {
      filenames
    } = cliOptions;
    let getBase;
    if (filenames.length === 1) {
      const base = filenames[0];
      const absoluteBase = path.resolve(base);
      getBase = filename => {
        return filename === absoluteBase ? path.dirname(base) : base;
      };
    } else {
      const filenameToBaseMap = new Map(filenames.map(filename => {
        const absoluteFilename = path.resolve(filename);
        return [absoluteFilename, path.dirname(filename)];
      }));
      const absoluteFilenames = new Map(filenames.map(filename => {
        const absoluteFilename = path.resolve(filename);
        return [absoluteFilename, filename];
      }));
      const {
        sep
      } = path;
      getBase = filename => {
        const base = filenameToBaseMap.get(filename);
        if (base !== undefined) {
          return base;
        }
        for (const [absoluteFilenameOrDir, relative] of absoluteFilenames) {
          if (filename.startsWith(absoluteFilenameOrDir + sep)) {
            filenameToBaseMap.set(filename, relative);
            return relative;
          }
        }
        return "";
      };
    }
    filenames.forEach(filenameOrDir => {
      watch(filenameOrDir);
    });
    startWatcher();
    onFilesChange(async filenames => {
      processing++;
      if (startTime === null) startTime = process.hrtime();
      try {
        const written = await Promise.all(filenames.map(filename => handleFile(filename, getBase(filename))));
        compiledFiles += written.filter(Boolean).length;
      } catch (err) {
        console.error(err);
      }
      processing--;
      if (processing === 0 && !cliOptions.quiet) logSuccess();
    });
  }
}

async function fileCommand ({
  cliOptions,
  babelOptions
}) {
  function buildResult(fileResults) {
    const mapSections = [];
    let code = "";
    let offset = 0;
    let hasRawMap = false;
    for (const result of fileResults) {
      if (!result) continue;
      hasRawMap = !!result.map;
      mapSections.push({
        offset: {
          line: offset,
          column: 0
        },
        map: result.map || {
          version: 3,
          names: [],
          sources: [],
          mappings: []
        }
      });
      code += result.code + "\n";
      offset += countNewlines(result.code) + 1;
    }
    const map = new AnyMap({
      version: 3,
      file: cliOptions.sourceMapTarget || path.basename(cliOptions.outFile || "") || "stdout",
      sections: mapSections
    });
    map.sourceRoot = babelOptions.sourceRoot;
    if (babelOptions.sourceMaps === "inline" || !cliOptions.outFile && babelOptions.sourceMaps) {
      code += "\n" + convertSourceMap.fromObject(encodedMap(map)).toComment();
    }
    return {
      map: map,
      code: code,
      hasRawMap: hasRawMap
    };
  }
  function countNewlines(code) {
    let count = 0;
    let index = -1;
    while ((index = code.indexOf("\n", index + 1)) !== -1) {
      count++;
    }
    return count;
  }
  function output(fileResults) {
    const result = buildResult(fileResults);
    if (cliOptions.outFile) {
      fs.mkdirSync(path.dirname(cliOptions.outFile), {
        recursive: true
      });
      let outputMap = false;
      if (babelOptions.sourceMaps && babelOptions.sourceMaps !== "inline") {
        outputMap = "external";
      } else if (babelOptions.sourceMaps == null && result.hasRawMap) {
        outputMap = hasDataSourcemap(result.code) ? "external" : "both";
      }
      if (outputMap) {
        const mapLoc = cliOptions.outFile + ".map";
        if (outputMap === "external") {
          result.code = addSourceMappingUrl(result.code, mapLoc);
        }
        fs.writeFileSync(mapLoc, JSON.stringify(encodedMap(result.map)));
      }
      fs.writeFileSync(cliOptions.outFile, result.code);
    } else {
      process.stdout.write(result.code + "\n");
    }
  }
  function readStdin() {
    return new Promise((resolve, reject) => {
      let code = "";
      process.stdin.setEncoding("utf8");
      process.stdin.on("readable", function () {
        const chunk = process.stdin.read();
        if (chunk !== null) code += chunk;
      });
      process.stdin.on("end", function () {
        resolve(code);
      });
      process.stdin.on("error", reject);
    });
  }
  async function stdin() {
    const code = await readStdin();
    const res = await transformRepl(cliOptions.filename, code, {
      ...babelOptions,
      sourceFileName: "stdin"
    });
    output([res]);
  }
  async function walk(filenames) {
    const _filenames = [];
    filenames.forEach(function (filename) {
      if (!fs.existsSync(filename)) return;
      const stat = fs.statSync(filename);
      if (stat.isDirectory()) {
        _filenames.push(...readdirForCompilable(filename, cliOptions.includeDotfiles, cliOptions.extensions));
      } else {
        _filenames.push(filename);
      }
    });
    const results = await Promise.all(_filenames.map(async function (filename) {
      let sourceFilename = filename;
      if (cliOptions.outFile) {
        sourceFilename = path.relative(path.dirname(cliOptions.outFile), sourceFilename);
      }
      sourceFilename = slash(sourceFilename);
      try {
        return await compile(filename, {
          ...babelOptions,
          sourceFileName: sourceFilename,
          sourceMaps: babelOptions.sourceMaps === "inline" ? true : babelOptions.sourceMaps
        });
      } catch (err) {
        if (!cliOptions.watch) {
          throw err;
        }
        console.error(err);
        return null;
      }
    }));
    output(results);
  }
  async function files(filenames) {
    if (cliOptions.watch) {
      enable({
        enableGlobbing: false
      });
    }
    if (!cliOptions.skipInitialBuild) {
      await walk(filenames);
    }
    if (cliOptions.watch) {
      filenames.forEach(watch);
      startWatcher();
      onFilesChange((changes, event, cause) => {
        const actionableChange = changes.some(filename => isCompilableExtension(filename, cliOptions.extensions) || filenames.includes(filename));
        if (!actionableChange) return;
        if (cliOptions.verbose) {
          console.log(`${event} ${cause}`);
        }
        walk(filenames).catch(err => {
          console.error(err);
        });
      });
    }
  }
  if (cliOptions.filenames.length) {
    await files(cliOptions.filenames);
  } else {
    await stdin();
  }
}

const opts = parseArgv(process.argv);
if (opts) {
  const fn = opts.cliOptions.outDir ? dirCommand : fileCommand;
  fn(opts).catch(err => {
    console.error(err);
    process.exitCode = 1;
  });
} else {
  process.exitCode = 2;
}
//# sourceMappingURL=index.js.map
