"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
var __typeError = (msg) => {
  throw TypeError(msg);
};
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __using = (stack, value, async) => {
  if (value != null) {
    if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
    var dispose, inner;
    if (async) dispose = value[__knownSymbol("asyncDispose")];
    if (dispose === void 0) {
      dispose = value[__knownSymbol("dispose")];
      if (async) inner = dispose;
    }
    if (typeof dispose !== "function") __typeError("Object not disposable");
    if (inner) dispose = function() {
      try {
        inner.call(this);
      } catch (e) {
        return Promise.reject(e);
      }
    };
    stack.push([async, dispose, value]);
  } else if (async) {
    stack.push([async]);
  }
  return value;
};
var __callDispose = (stack, error, hasError) => {
  var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
    return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
  };
  var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
  var next = (it) => {
    while (it = stack.pop()) {
      try {
        var result = it[1] && it[1].call(it[2]);
        if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
      } catch (e) {
        fail(e);
      }
    }
    if (hasError) throw error;
  };
  return next();
};
var graph_exports = {};
__export(graph_exports, {
  Graph: () => Graph
});
module.exports = __toCommonJS(graph_exports);
var import_ansi_colors = __toESM(require("ansi-colors"), 1);
var import_package = require("../util/package.js");
var import_progress = require("../util/progress.js");
var import_error = require("./error.js");
var import_project_builder = require("./project-builder.js");
var import_project = require("./project.js");
var import_tsgo = require("./typescript/tsgo.js");
/**
 * @license
 * Copyright 2022-2026 Greg Lauckhart <greg@lauckhart.com>
 * SPDX-License-Identifier: Apache-2.0
 */
class Graph {
  constructor(root, nodes) {
    this.nodes = nodes;
    this.root = root;
  }
  nodes;
  root;
  get(name) {
    const node = this.nodes.find((node2) => node2.pkg.name === name);
    if (node === void 0) {
      throw new Error(`Cannot locate package "${name}"`);
    }
    return node;
  }
  static async load(pkg = import_package.Package.workspace) {
    const workspace = pkg.workspace;
    const nodeMap = await this.#loadNodes(workspace);
    return await this.#createGraph(workspace, Object.values(nodeMap));
  }
  static async forProject(path) {
    let workspace;
    try {
      workspace = import_package.Package.workspaceFor(path);
    } catch (e) {
      if (e instanceof import_package.JsonNotFoundError) {
        return;
      }
      throw e;
    }
    const nodeMap = await this.#loadNodes(workspace);
    const rootPkg = new import_package.Package({ path });
    const rootNode = nodeMap[rootPkg.name];
    if (!rootNode) {
      return;
    }
    const nodes = /* @__PURE__ */ new Set();
    function addNode(node) {
      if (nodes.has(node)) {
        return;
      }
      nodes.add(node);
      for (const dependency of node.dependencies) {
        addNode(dependency);
      }
    }
    addNode(rootNode);
    return await this.#createGraph(rootPkg, [...nodes]);
  }
  async build(builder) {
    if (!builder.hasTarget(import_project_builder.Target.types) && !builder.hasTarget(import_project_builder.Target.esm) && !builder.hasTarget(import_project_builder.Target.cjs)) {
      var _stack = [];
      try {
        const progress = __using(_stack, new import_progress.Progress());
        progress.startup("Building", this.root);
        await this.#prebuild(builder, progress);
        return;
      } catch (_) {
        var _error = _, _hasError = true;
      } finally {
        __callDispose(_stack, _error, _hasError);
      }
    }
    if (builder.tsgo) {
      await this.#buildWithTsgo(builder);
    } else {
      await this.#prebuild(builder);
      await this.#buildSequential(builder);
    }
  }
  async #prebuild(builder, progress) {
    const wantsClean = builder.hasClean || builder.hasTarget(import_project_builder.Target.clean);
    const needsConfig = this.nodes.find((node) => node.pkg.hasConfig);
    if (!wantsClean && !needsConfig) {
      return;
    }
    try {
      if (wantsClean) {
        builder.clearClean();
        const doClean = async () => {
          for (const node of this.nodes) {
            await node.project.clean();
            node.info = {};
          }
        };
        if (progress) {
          await progress.run("Clean", doClean);
        } else {
          await doClean();
        }
      }
      for (const node of this.nodes) {
        if (!node.pkg.hasConfig) {
          continue;
        }
        await builder.configure(node.project);
      }
    } catch (e) {
      console.error("Terminating due to prebuild error:", e);
      process.exit(1);
    }
  }
  async #buildSequential(builder) {
    const toBuild = new Set(this.nodes);
    while (toBuild.size) {
      let node;
      nodes: for (node of toBuild) {
        for (const dep of node.dependencies) {
          if (dep.isDirty) {
            continue nodes;
          }
        }
        break;
      }
      if (!node) {
        throw new Error("Internal logic error: No unbuilt project has fully built dependencies");
      }
      if (node.isDirty || builder.unconditional) {
        await builder.build(node.project);
        node.info.timestamp = (/* @__PURE__ */ new Date()).toISOString();
      }
      toBuild.delete(node);
    }
  }
  async #buildWithTsgo(builder) {
    var _stack2 = [];
    try {
      const dirtyNodes = this.nodes.filter((node) => node.isDirty || builder.unconditional);
      if (!dirtyNodes.length && !builder.hasClean) {
        var _stack = [];
        try {
          const progress2 = __using(_stack, new import_progress.Progress());
          progress2.startup("Building", this.root);
          progress2.success("Up to date");
          return;
        } catch (_) {
          var _error = _, _hasError = true;
        } finally {
          __callDispose(_stack, _error, _hasError);
        }
      }
      const workspace = this.root.isWorkspace ? this.root : this.root.workspace;
      const tsconfigPath = this.root.resolve("tsconfig.json");
      const progress = __using(_stack2, new import_progress.Progress());
      progress.startup("Building", this.root);
      await this.#prebuild(builder, progress);
      progress.update("Type check");
      let result;
      try {
        result = await (0, import_tsgo.tsgoSolutionBuild)(workspace, tsconfigPath);
      } catch (e) {
        progress.failure("Type check");
        throw e;
      }
      if (result.ok) {
        progress.success("Type check");
      } else {
        progress.failure("Type check");
        if (result.errorsByPackage.size > 0) {
          for (const errors of result.errorsByPackage.values()) {
            process.stderr.write(`${errors}
`);
          }
        } else if (result.rawOutput) {
          process.stderr.write(result.rawOutput);
        }
      }
      if (result.outputsSkipped) {
        let propagateFailure2 = function(failedNode, nodes) {
          for (const node of nodes) {
            if (failed.has(node)) {
              continue;
            }
            if (node.dependencies.includes(failedNode)) {
              failed.add(node);
              propagateFailure2(node, nodes);
            }
          }
        };
        var propagateFailure = propagateFailure2;
        const failed = /* @__PURE__ */ new Set();
        for (const node of this.nodes) {
          if (result.errorsByPackage.has(node.pkg.path)) {
            failed.add(node);
          }
        }
        for (const node of failed) {
          propagateFailure2(node, this.nodes);
        }
        for (const node of failed) {
          const idx = dirtyNodes.indexOf(node);
          if (idx !== -1) {
            dirtyNodes.splice(idx, 1);
          }
        }
      }
      builder.typesPrebuilt = true;
      for (const node of dirtyNodes) {
        await builder.build(node.project, progress);
        node.info.timestamp = (/* @__PURE__ */ new Date()).toISOString();
      }
      await progress.run("Transpile", () => builder.flushWork());
      if (!result.ok) {
        process.exit(1);
      }
    } catch (_2) {
      var _error2 = _2, _hasError2 = true;
    } finally {
      __callDispose(_stack2, _error2, _hasError2);
    }
  }
  display() {
    for (const node of this.nodes) {
      const progress = node.pkg.start("Node");
      progress.info("path", node.pkg.path);
      progress.info("modified", formatTime(node.modifyTime));
      progress.info("built", formatTime(node.info.timestamp ?? 0));
      progress.info("dirty", node.isDirty ? import_ansi_colors.default.dim.red("yes") : import_ansi_colors.default.dim.green("no"));
      progress.info("dependencies", node.dependencies.map(formatDep).join(", "));
      progress.close();
    }
  }
  static async #createGraph(root, nodes) {
    const graph = new Graph(root, nodes);
    await Promise.all(
      graph.nodes.map(async (node) => {
        if (node.pkg.hasFile(import_project.BUILD_INFO_LOCATION)) {
          node.info = await node.pkg.readJson(import_project.BUILD_INFO_LOCATION);
        }
        node.modifyTime = await node.pkg.lastModified("package.json", "src", "test");
        return node;
      })
    );
    const stack = Array();
    function findCircular(node) {
      if (stack.indexOf(node) !== -1) {
        stack.push(node);
        throw new import_error.InternalBuildError(`Circular dependency: ${stack.map(formatDep).join(" \u25B8 ")}`);
      }
      stack.push(node);
      for (const dep of node.dependencies) {
        findCircular(dep);
      }
      stack.pop();
    }
    for (const node of graph.nodes) {
      findCircular(node);
    }
    return graph;
  }
  static async #loadNodes(workspace) {
    const workspaces = workspace.json.workspaces;
    const paths = workspaces === void 0 || workspaces.length === 0 ? ["."] : [...workspaces];
    const nodeMap = {};
    const allDeps = {};
    for (const path of paths) {
      const pkg = new import_package.Package({ path: workspace.resolve(path) });
      allDeps[pkg.name] = pkg.dependencies;
      nodeMap[pkg.name] = {
        pkg,
        project: new import_project.Project(pkg),
        dependencies: [],
        info: {},
        modifyTime: 0,
        get buildTime() {
          return this.info.timestamp ? new Date(this.info.timestamp).getTime() : 0;
        },
        get isDirty() {
          return this.modifyTime > this.buildTime || this.dependencies.some((dep) => dep.isDirty);
        }
      };
    }
    for (const name in allDeps) {
      for (const dep of allDeps[name]) {
        const depNode = nodeMap[dep];
        if (depNode && depNode !== nodeMap[name]) {
          nodeMap[name].dependencies.push(depNode);
        }
      }
    }
    return nodeMap;
  }
}
function formatTime(time) {
  if (!time) {
    return import_ansi_colors.default.dim.red("never");
  }
  if (typeof time === "string") {
    time = new Date(time).getTime();
  }
  return new Date(time - (/* @__PURE__ */ new Date()).getTimezoneOffset()).toISOString().split(".")[0].replace("T", " ");
}
function formatDep(node) {
  return node.pkg.name.replace(/^@[^/]+\//, "");
}
//# sourceMappingURL=graph.js.map
