"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target2, all) => {
  for (var name in all)
    __defProp(target2, 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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var package_exports = {};
__export(package_exports, {
  CODEGEN_PATH: () => CODEGEN_PATH,
  CONFIG_PATH: () => CONFIG_PATH,
  JsonNotFoundError: () => JsonNotFoundError,
  Package: () => Package
});
module.exports = __toCommonJS(package_exports);
var import_node_fs = require("node:fs");
var import_promises = require("node:fs/promises");
var import_node_path = require("node:path");
var import_errors = require("./errors.js");
var import_file = require("./file.js");
var import_glob = require("./glob.js");
var import_import_aliases = require("./import-aliases.js");
var import_progress = require("./progress.js");
var import_tools_path = require("./tools-path.cjs");
/**
 * @license
 * Copyright 2022-2026 Greg Lauckhart <greg@lauckhart.com>
 * SPDX-License-Identifier: Apache-2.0
 */
class JsonNotFoundError extends Error {
}
const CONFIG_PATH = `src/build.config.ts`;
const CODEGEN_PATH = `codegen`;
const packageForPath = {};
function isDirectory(path) {
  return !!(0, import_errors.ignoreErrorSync)("ENOENT", () => (0, import_node_fs.statSync)(path).isDirectory());
}
class Package {
  path;
  json;
  supportsEsm;
  supportsCjs;
  hasSrc;
  hasTests;
  hasConfig;
  isLibrary;
  #importAliases;
  constructor({
    path = ".",
    name
  } = {}) {
    const { root, json } = this.findJson("package.json", path, name);
    this.path = root;
    this.json = json;
    const { esm, cjs } = selectFormats(this.json);
    this.supportsEsm = esm;
    this.supportsCjs = cjs;
    this.hasSrc = isDirectory(this.resolve("src"));
    this.hasTests = isDirectory(this.resolve("test"));
    const refs = (0, import_file.maybeReadJsonSync)(this.resolve("tsconfig.json"))?.references;
    if (refs !== void 0) {
      if (!refs.find((ref) => this.resolve(ref.path) === this.resolve("src"))) {
        this.hasSrc = false;
      }
      if (!refs.find((ref) => this.resolve(ref.path) === this.resolve("test"))) {
        this.hasTests = false;
      }
    }
    const kind = this.json.nacho?.kind;
    this.isLibrary = kind === "lib" ? true : kind === "app" ? false : !!this.json.exports;
    this.hasConfig = this.hasFile(this.resolve(CONFIG_PATH));
  }
  findJson(filename, path = this.path, title) {
    path = (0, import_node_path.resolve)(path);
    while (true) {
      const json = (0, import_errors.ignoreErrorSync)(
        ["ENOENT", "ENOTDIR"],
        () => JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.resolve)(path, filename)).toString())
      );
      if (json) {
        if (title === void 0 || json.name === title) {
          return { root: path, json };
        }
      }
      const parent = (0, import_node_path.dirname)(path);
      if (parent === path) {
        throw new JsonNotFoundError(`Could not locate ${title ?? filename}`);
      }
      path = parent;
    }
  }
  get name() {
    return this.json.name ?? `<${this.path}>`;
  }
  get version() {
    return this.json.version ?? "?";
  }
  get exports() {
    return this.json.exports;
  }
  get hasCodegen() {
    return this.hasDirectory(CODEGEN_PATH);
  }
  resolve(...paths) {
    return (0, import_node_path.resolve)(this.path, ...paths);
  }
  relative(path) {
    return (0, import_node_path.relative)(this.path, path);
  }
  async glob(pattern) {
    if (typeof pattern === "string") {
      pattern = this.resolve(pattern).replace(/\\/g, "/");
    } else {
      pattern = pattern.map((s) => this.resolve(s).replace(/\\/g, "/"));
    }
    return (0, import_glob.globSync)(pattern);
  }
  start(what) {
    const progress = new import_progress.Progress();
    progress.startup(what, this);
    return progress;
  }
  async lastModified(...paths) {
    return this.lastModifiedAbsolute(paths.map((p) => this.resolve(p)));
  }
  async lastModifiedAbsolute(paths) {
    let mtime = 0;
    await Promise.all(
      paths.map(async (p) => {
        const stats = await (0, import_errors.ignoreError)("ENOENT", async () => await (0, import_promises.stat)(p));
        if (!stats) {
          return;
        }
        let thisMtime;
        if (stats.isDirectory()) {
          const paths2 = (await (0, import_promises.readdir)(p)).map((p2) => (0, import_node_path.resolve)(p, p2));
          thisMtime = await this.lastModifiedAbsolute(paths2);
        } else {
          thisMtime = stats.mtimeMs;
        }
        if (thisMtime > mtime) {
          mtime = thisMtime;
        }
      })
    );
    return mtime;
  }
  get dependencies() {
    let result = Array();
    for (const type of ["dependencies", "optionalDependencies", "devDependencies", "peerDependencies"]) {
      if (typeof this.json[type] === "object" && this.json[type] !== null) {
        result = [...result, ...Object.keys(this.json[type])];
      }
    }
    return [...new Set(result)];
  }
  get workspace() {
    return Package.workspaceFor(this.path);
  }
  get isWorkspace() {
    return Array.isArray(this.json.workspaces);
  }
  get root() {
    try {
      return this.workspace;
    } catch (e) {
      if (!(e instanceof JsonNotFoundError)) {
        throw e;
      }
    }
    return this;
  }
  static set workingDir(wd) {
    workingDir = wd;
    workspace = void 0;
    target = void 0;
  }
  static get workspace() {
    return this.workspaceFor(workingDir);
  }
  static workspaceFor(cwd) {
    if (!workspace) {
      const nearest = new Package({ path: cwd });
      let pkg = nearest;
      while (!Array.isArray(pkg.json.workspaces)) {
        try {
          pkg = new Package({ path: (0, import_node_path.dirname)(pkg.path) });
        } catch (e) {
          if (e instanceof JsonNotFoundError) {
            workspace = nearest;
            return workspace;
          }
          throw e;
        }
      }
      workspace = pkg;
    }
    return workspace;
  }
  /**
   * The package for the current working directory.  Resets when {@link workingDir} changes.
   */
  static get target() {
    if (!target) {
      target = new Package({ path: workingDir });
    }
    return target;
  }
  static get tools() {
    if (!tools) {
      tools = new Package({ path: import_tools_path.toolsPath });
    }
    return tools;
  }
  static findExport(name, type = "esm") {
    return this.workspace.resolveImport(name, type);
  }
  /**
   * Find an installed package by name, resolving from the {@link target} package.
   */
  static findPackage(name) {
    return this.target.findPackage(name);
  }
  resolveExport(name, type = "esm") {
    if (!name.startsWith(".")) {
      name = `./${name}`;
    }
    const exportDetail = typeof this.exports === "object" ? this.exports?.[name] : void 0;
    if (typeof exportDetail === "object") {
      const exp = findExportCondition(exportDetail, type);
      if (exp) {
        return this.resolve(exp);
      }
    }
    if (name === ".") {
      if (type === "esm" && this.json.module) {
        return this.resolve(this.json.module);
      }
      if (this.json.main) {
        return this.resolve(this.json.main);
      }
    }
    throw new Error(`Cannot resolve export ${name} in package ${this.name}`);
  }
  findPackage(name) {
    let resolveIn = this.path;
    while (true) {
      if (isDirectory((0, import_node_path.resolve)(resolveIn, "node_modules", name))) {
        break;
      }
      const nextResolveIn = (0, import_node_path.dirname)(resolveIn);
      if (nextResolveIn === resolveIn) {
        throw new Error(`Cannot find module ${name} from ${this.path}`);
      }
      resolveIn = nextResolveIn;
    }
    return Package.forPath((0, import_node_path.resolve)(resolveIn, "node_modules", name));
  }
  resolveImport(name, type = "esm") {
    const segments = name.split("/");
    let packageName = segments.shift();
    if (packageName.startsWith("@") && segments.length) {
      packageName = `${packageName}/${segments.shift()}`;
    }
    const pkg = this.findPackage(packageName);
    return pkg.resolveExport(segments.length ? segments.join("/") : ".", type);
  }
  hasFile(path) {
    return !!this.#maybeStat(path)?.isFile();
  }
  hasDirectory(path) {
    return !!this.#maybeStat(path)?.isDirectory();
  }
  async readFile(path) {
    return (0, import_promises.readFile)(this.resolve(path), "utf-8");
  }
  readFileSync(path) {
    return (0, import_node_fs.readFileSync)(this.resolve(path), "utf-8");
  }
  async writeFile(path, contents) {
    await (0, import_promises.writeFile)(this.resolve(path), `${contents}`);
  }
  async save() {
    await this.writeFile((0, import_node_path.join)(this.path, "package.json"), JSON.stringify(this.json, void 0, 4));
  }
  async readJson(path) {
    const text = await this.readFile(path);
    try {
      return JSON.parse(stripJsonComments(text));
    } catch (e) {
      if (!(e instanceof Error)) {
        e = new Error(`${e}`);
      }
      e.message = `Error parsing "${this.resolve(path)}": ${e.message}`;
      throw e;
    }
  }
  async writeJson(path, value) {
    await this.writeFile(path, JSON.stringify(value, void 0, 4));
  }
  static maybeForPath(path) {
    function find(path2) {
      let result2 = packageForPath[path2];
      if (result2 === void 0) {
        if ((0, import_node_fs.existsSync)((0, import_node_path.join)(path2, "package.json"))) {
          result2 = new Package({ path: path2 });
        } else {
          const parentDir = (0, import_node_path.dirname)(path2);
          if (parentDir === path2) {
            return null;
          }
          result2 = find(parentDir);
        }
        packageForPath[path2] = result2;
      }
      return result2;
    }
    const result = find(path);
    return result ?? void 0;
  }
  static forPath(path) {
    const result = this.maybeForPath(path);
    if (result !== void 0) {
      return result;
    }
    throw new Error(`Cannot find package.json for "${path}"`);
  }
  get importAliases() {
    if (this.#importAliases !== void 0) {
      return this.#importAliases;
    }
    this.#importAliases = new import_import_aliases.ImportAliases(
      this.json.imports,
      Package.maybeForPath((0, import_node_path.dirname)(this.path))?.importAliases
    );
    return this.#importAliases;
  }
  get modules() {
    return this.listModules(false);
  }
  get sourceModules() {
    return this.listModules(true);
  }
  /**
   * Create a map of module name -> implementation file.  If "source" is true, searches source files rather than
   * transpiled files.  We do this rather than finding transpilation files then mapping to source files so this works
   * even if there isn't a build.
   */
  listModules(source, ...conditions) {
    if (!conditions.length) {
      conditions = [this.supportsEsm ? "import" : "require", "default"];
    }
    const modules = {};
    const exports = this.exports;
    if (typeof exports === "object" && exports !== null) {
      findModules(source, new Set(conditions), modules, this.name, this.path, exports);
    }
    return modules;
  }
  #maybeStat(path) {
    return (0, import_file.maybeStatSync)(this.resolve(path));
  }
}
let workingDir = ".";
let workspace;
let target;
let tools;
function stripJsonComments(text) {
  return text.replace(
    /"(?:[^"\\]|\\.)*"|\/\/[^\n]*|\/\*[\s\S]*?\*\//g,
    (match) => match.startsWith('"') ? match : ""
  );
}
function selectFormats(json) {
  let esm, cjs;
  if (typeof json !== "object") {
    throw new Error(`Invalid package JSON ${typeof json}`);
  }
  if (json.type === "module") {
    esm = true;
    cjs = json.main !== void 0 && json.module !== void 0 || !!Object.values(json.exports ?? {}).find((exp) => exp.require);
  } else {
    cjs = true;
    esm = !!json.module || !!Object.values(json.exports ?? {}).find((exp) => exp.import);
  }
  return { esm, cjs };
}
function findExportCondition(detail, type) {
  if (type === "esm" && detail.import) {
    let exp2 = detail.import;
    if (exp2 && typeof exp2 !== "string") {
      exp2 = findExportCondition(exp2, type);
    }
    if (exp2) {
      return exp2;
    }
  }
  let exp = detail.require ?? detail.node ?? detail.default;
  if (exp && typeof exp !== "string") {
    exp = findExportCondition(exp, type);
  }
  if (typeof exp === "string") {
    return exp;
  }
}
function findModules(source, conditions, target2, prefix, path, exports) {
  if (typeof exports === "string") {
    addModuleGlobs(source, target2, prefix, path, exports);
  } else if (Array.isArray(exports)) {
    for (const entry of exports) {
      findModules(source, conditions, target2, prefix, path, entry);
    }
  } else if (typeof exports === "object" && exports !== null) {
    let selectedCondition = false;
    for (const key in exports) {
      if (key.startsWith(".")) {
        findModules(source, conditions, target2, (0, import_node_path.join)(prefix, key), path, exports[key]);
      } else if (!selectedCondition && conditions.has(key)) {
        findModules(source, conditions, target2, prefix, path, exports[key]);
        selectedCondition = true;
      }
    }
  } else {
    throw new Error("Malformed exports field in package.json");
  }
}
function addModuleGlobs(source, target2, name, base, pattern) {
  let path = (0, import_node_path.join)(base, pattern);
  if (source) {
    path = path.replace(/\/dist\/(?:esm|cjs)\//, "/src/");
  }
  if (name.includes("*")) {
    if (!name.endsWith("/*")) {
      throw new Error(`Wildcard in module ${name} does not appear as final path segment`);
    }
    name = name.substring(0, name.length - 2);
    const paths = (0, import_glob.globSync)(source ? path.replace(/\.js$/, ".{ts,js,cjs,mjs}") : path);
    if (!paths.length) {
      throw new Error(`No match for module ${name} pattern ${pattern}`);
    }
    const [prefix, suffix] = path.split(/\*+/);
    const prefixLength = prefix === void 0 ? 0 : prefix.length;
    const suffixLength = suffix === void 0 ? 0 : suffix.length;
    for (const thisPath of paths) {
      const qualifier = thisPath.substring(prefixLength, thisPath.length - suffixLength);
      const thisName = (0, import_node_path.join)(name, qualifier);
      target2[thisName] = thisPath;
    }
  } else if (path.includes("*")) {
    throw new Error(`Wildcard in module path "${path}" but not in module "${name}"`);
  } else {
    name = name.replace(/\/(?:export|index)$/, "");
    let found = false;
    if ((0, import_file.isFile)(path)) {
      found = true;
    } else if (source && path.endsWith(".js")) {
      path = path.replace(/\.js$/, ".ts");
      if ((0, import_file.isFile)(path)) {
        found = true;
      }
    }
    if (!found) {
      throw new Error(`Module "${name}" path "${path}" not found`);
    }
    target2[name] = path;
  }
}
//# sourceMappingURL=package.js.map
