/**
 * @license
 * Copyright 2022-2026 Greg Lauckhart <greg@lauckhart.com>
 * SPDX-License-Identifier: Apache-2.0
 */
import { existsSync } from "node:fs";
import { join } from "node:path";
import {
  createSolutionBuilder,
  createSolutionBuilderHost,
  ExitStatus,
  sys
} from "typescript";
import { BuildError, InternalBuildError } from "../error.js";
import { TypescriptContext } from "./context.js";
const BUILD_DIAGNOSTICS = false;
async function createSolutionBuilderContext(workspace, graph) {
  let diagnostics;
  const host = createSolutionBuilderHost();
  host.reportDiagnostic = (diagnostic) => (diagnostics ??= []).push(diagnostic);
  let context;
  function currentContext() {
    if (context === void 0) {
      throw new Error("Build context accessed with no context");
    }
    return context;
  }
  instrumentHostWithInterceptors(host, currentContext, graph);
  const builder = createSolutionBuilder(host, [workspace.resolve("tsconfig.json")], {});
  TypescriptContext.diagnose(diagnostics);
  return {
    async build(pkg, path, refreshCallback, _emit) {
      context = {
        refreshCallback
      };
      if (path === "src" && pkg.isLibrary && pkg.supportsEsm && pkg.supportsCjs) {
        const esmDeclIdentifier = new RegExp(
          `^(${pkg.resolve("dist").replace(/[\\/]/g, "[\\/]")}[\\/](?:.*))esm([\\/].*(?:\\.d\\.ts|\\.d\\.ts\\.map))$`
        );
        context.fileWrite = (writeFile, filename, contents, writeBom) => {
          const match = filename.match(esmDeclIdentifier);
          if (!match) {
            return;
          }
          writeFile(`${match[1]}cjs${match[2]}`, contents, writeBom);
        };
      }
      try {
        const status = builder.build(pkg.resolve(join(path, "tsconfig.json")));
        TypescriptContext.diagnose(diagnostics);
        switch (status) {
          case void 0:
          case ExitStatus.Success:
            break;
          case ExitStatus.InvalidProject_OutputsSkipped:
            throw new BuildError(
              `Typescript does not think ${path} is part of the project, this is probably a build tooling error`
            );
          case ExitStatus.ProjectReferenceCycle_OutputsSkipped:
            throw new BuildError(
              `Typescript identified a cyclical project reference in ${path}, please check package dependencies`
            );
          default:
            throw new InternalBuildError(`Build error ${ExitStatus[status]} without diagnostics`);
        }
      } finally {
        context = void 0;
      }
    }
  };
}
function instrumentHostWithInterceptors(host, currentContext, graph) {
  const moduleIdentifier = graph ? new RegExp(`^(${graph.nodes.map((node) => node.pkg.path).join("|")})[\\/]dist[\\/](?:esm|cjs)/(.*).d.ts$`) : void 0;
  const writeFile = host.writeFile ?? sys.writeFile;
  host.writeFile = (path, data, writeBom) => {
    const context = currentContext();
    context.refreshCallback();
    writeFile(path, data, writeBom);
    context.fileWrite?.(writeFile, path, data, writeBom);
  };
  let resolveModuleNameLiterals;
  const resolveModuleNameLiteralsInterceptor = (...args) => {
    currentContext().refreshCallback();
    const result = resolveModuleNameLiterals(...args);
    if (!BUILD_DIAGNOSTICS) {
      return result;
    }
    for (const entry of result) {
      const filename = entry.resolvedModule?.resolvedFileName;
      const match = moduleIdentifier && filename?.match(moduleIdentifier);
      if (match) {
        const sourceFilename = `${match[1]}/src/${match[2]}.ts`;
        if (existsSync(sourceFilename)) {
          console.warn(".d.ts reference", sourceFilename);
        }
      }
    }
    return result;
  };
  const createProgram = host.createProgram;
  host.createProgram = (...args) => {
    const compilerHost = args[2];
    if (compilerHost && compilerHost.resolveModuleNameLiterals !== resolveModuleNameLiteralsInterceptor) {
      resolveModuleNameLiterals = compilerHost.resolveModuleNameLiterals;
      compilerHost.resolveModuleNameLiterals = resolveModuleNameLiteralsInterceptor;
    }
    const result = createProgram(...args);
    const getCompilerOptions = result.getCompilerOptions;
    result.getCompilerOptions = (...args2) => {
      currentContext().refreshCallback();
      return getCompilerOptions(...args2);
    };
    return result;
  };
}
export {
  createSolutionBuilderContext
};
//# sourceMappingURL=solution-builder.js.map
