#!/usr/bin/env node

// src/bin/package/compile-tsup.ts
import chalk6 from "chalk";

// src/lib/crossSpawn.ts
import crossSpawn from "cross-spawn";
function spawnSync(command, args, options) {
  return crossSpawn.sync(command, args, options);
}

// src/actions/package/compile/packageCompileEsbuild.ts
import fs2 from "node:fs";
import path3 from "node:path";
import { cwd as cwd3 } from "node:process";
import chalk5 from "chalk";
import { build } from "esbuild";

// src/actions/package/compile/inputs.ts
import chalk from "chalk";
import { glob } from "glob";
var getAllInputs = (srcDir, isVerbose = false) => {
  return glob.sync(`${srcDir}/**/*.{ts,tsx,mts,cts}`, { posix: true }).map((file) => {
    const result = file.slice(Math.max(0, srcDir.length + 1));
    if (isVerbose) {
      console.log(chalk.gray(`getAllInputs: ${JSON.stringify(result, null, 2)}`));
    }
    return result;
  });
};

// src/actions/package/compile/buildEntries.ts
var bareSpecOrStoryRe = /(?:^|\/)(?:spec|stories)\.(?:ts|tsx|mts|cts)$/;
var isSpecOrStory = (entry) => {
  return entry.includes(".spec.") || entry.includes(".stories.") || entry.startsWith("spec/") || entry.includes("/spec/") || entry.startsWith("stories/") || entry.includes("/stories/") || bareSpecOrStoryRe.test(entry);
};
var isDeclaration = (entry) => /\.d\.(?:ts|mts|cts)$/.test(entry);
var buildEntries = (srcDir, entryMode = "single", options, shouldExcludeSpecAndStories = true, isVerbose = false) => {
  let entries;
  switch (entryMode) {
    case "platform": {
      entries = ["index-node.ts", "index-browser.ts"];
      break;
    }
    case "all": {
      entries = (shouldExcludeSpecAndStories ? getAllInputs(srcDir).filter((entry) => !isSpecOrStory(entry)) : getAllInputs(srcDir)).filter((entry) => !isDeclaration(entry));
      break;
    }
    case "custom": {
      entries = [];
      break;
    }
    default: {
      entries = ["index.ts"];
      break;
    }
  }
  if (typeof options !== "boolean" && Array.isArray(options?.entry)) {
    entries.push(...options.entry);
  }
  if (isVerbose) console.log(`buildEntries [${entryMode}] ${entries.length}`);
  return entries;
};

// src/actions/package/compile/deepMerge.ts
function deepMerge(target, source) {
  for (const [key, sourceVal] of Object.entries(source)) {
    if (typeof sourceVal === "object" && sourceVal !== null && !Array.isArray(sourceVal)) {
      if (!Object.hasOwn(target, key) || typeof target[key] !== "object") {
        target[key] = {};
      }
      deepMerge(target[key], sourceVal);
    } else {
      target[key] = sourceVal;
    }
  }
  return target;
}
function deepMergeObjects(objects) {
  const result = {};
  for (const obj of objects) {
    deepMerge(result, obj);
  }
  return result;
}

// src/actions/package/compile/entryOutputName.ts
import path from "node:path";
var platformSourceSegments = /* @__PURE__ */ new Set(["browser", "neutral", "node"]);
var toPosixPath = (value) => value.split(path.sep).join("/");
var normalizeEntryPath = (entry) => toPosixPath(entry).replace(/^\.?\//u, "");
var customEntryOutputName = (entry) => {
  const parsed = path.posix.parse(normalizeEntryPath(entry));
  const withoutExtension = path.posix.join(parsed.dir, parsed.name);
  const segments = withoutExtension.split("/");
  return segments.length > 1 && platformSourceSegments.has(segments[0]) ? segments.slice(1).join("/") : withoutExtension;
};

// src/actions/package/compile/resolveCompilePlatforms.ts
import { cwd } from "node:process";
import chalk2 from "chalk";
var resolvePlatformRecord = (value) => {
  if (value === void 0 || value === true) return { src: {} };
  if (value === false) return {};
  const { esbuildOptions: _legacyEsbuildOptions, ...entries } = value;
  return entries;
};
var classifyPlatform = (value) => {
  if (value === void 0) {
    return {
      deprecatedEmptyObject: false,
      entries: {},
      state: "unset"
    };
  }
  if (typeof value === "boolean") {
    return {
      deprecatedEmptyObject: false,
      entries: value ? { src: {} } : {},
      state: value ? "build" : "off"
    };
  }
  const isDeprecatedEmptyObject = Object.keys(value).length === 0;
  const entries = resolvePlatformRecord(value);
  return {
    deprecatedEmptyObject: isDeprecatedEmptyObject,
    entries,
    state: Object.keys(entries).length > 0 ? "build" : "off"
  };
};
var resolveCompilePlatforms = (compile) => {
  const node = classifyPlatform(compile?.node);
  const browser = classifyPlatform(compile?.browser);
  const neutral = classifyPlatform(compile?.neutral);
  const isAnyBuild = node.state === "build" || browser.state === "build" || neutral.state === "build";
  const neutralEntries = neutral.state === "build" ? neutral.entries : !isAnyBuild && neutral.state === "unset" ? { src: {} } : {};
  const deprecatedEmptyObjectTargets = [
    ...node.deprecatedEmptyObject ? ["node"] : [],
    ...browser.deprecatedEmptyObject ? ["browser"] : [],
    ...neutral.deprecatedEmptyObject ? ["neutral"] : []
  ];
  return {
    browser: browser.state === "build" ? browser.entries : {},
    deprecatedEmptyObjectTargets,
    neutral: neutralEntries,
    node: node.state === "build" ? node.entries : {}
  };
};
var deprecatedEmptyObjectWarned = /* @__PURE__ */ new Set();
var warnDeprecatedEmptyObjectTargets = (targets) => {
  for (const target of targets) {
    const key = `${cwd()}:${target}`;
    if (deprecatedEmptyObjectWarned.has(key)) continue;
    deprecatedEmptyObjectWarned.add(key);
    console.warn(chalk2.yellow(
      `[xy] Deprecated: compile.${target} = {} is treated as "do not build". Set it to \`false\` or remove the field.`
    ));
  }
};

// src/actions/package/compile/runTsc.ts
import { cwd as cwd2 } from "node:process";
import chalk3 from "chalk";
var runTsc = (configPath, isVerbose = false, extraArgs = []) => {
  const args = ["-p", configPath, ...extraArgs];
  if (isVerbose) {
    console.log(chalk3.gray(`tsc ${args.join(" ")}`));
  }
  const result = spawnSync("tsc", args, {
    cwd: cwd2(),
    stdio: "inherit"
  });
  return result.status ?? (result.error === void 0 ? 0 : 1);
};

// src/actions/package/compile/validateFullPackage.ts
import fs from "node:fs";
import path2 from "node:path";
import chalk4 from "chalk";
import ts, { formatDiagnosticsWithColorAndContext, sys } from "typescript";
var toPosixPath2 = (value) => value.split(path2.sep).join("/");
var relativePath = (from, to) => {
  const result = toPosixPath2(path2.relative(from, to));
  return result.startsWith(".") ? result : `./${result}`;
};
var formatTsConfigDiagnostics = (diagnostics, pkg) => {
  return formatDiagnosticsWithColorAndContext(diagnostics, {
    getCanonicalFileName: (fileName) => fileName,
    getCurrentDirectory: () => pkg,
    getNewLine: () => sys.newLine
  });
};
var isInPackageSubdirectory = (pkg, fileName, directoryName) => {
  const relative = toPosixPath2(path2.relative(path2.resolve(pkg, directoryName), fileName));
  return relative !== "" && !relative.startsWith("../") && relative !== "..";
};
var createFullValidationConfig = (pkg, tsconfigPath) => {
  const configFile = ts.readConfigFile(tsconfigPath, (fileName) => ts.sys.readFile(fileName));
  if (configFile.error) {
    return { configPath: tsconfigPath, diagnostics: [configFile.error] };
  }
  const parsed = ts.parseJsonConfigFileContent(
    configFile.config,
    ts.sys,
    pkg,
    void 0,
    tsconfigPath
  );
  const buildDir = path2.resolve(pkg, "build");
  fs.mkdirSync(buildDir, { recursive: true });
  const configPath = path2.resolve(buildDir, "tsconfig.package-validate.json");
  const files = parsed.fileNames.filter((fileName) => !isInPackageSubdirectory(pkg, fileName, "packages")).map((fileName) => relativePath(buildDir, fileName));
  if (files.length === 0) {
    const emptyFile = path2.resolve(buildDir, "tsconfig.package-validate-empty.d.ts");
    fs.writeFileSync(emptyFile, "");
    files.push(relativePath(buildDir, emptyFile));
  }
  fs.writeFileSync(
    configPath,
    `${JSON.stringify({
      compilerOptions: {
        noEmit: true,
        rootDir: relativePath(buildDir, pkg)
      },
      extends: relativePath(buildDir, tsconfigPath),
      files,
      include: []
    }, null, 2)}
`
  );
  return { configPath, diagnostics: parsed.errors };
};
var validateFullPackage = (pkg, isVerbose = false) => {
  const tsconfigPath = path2.resolve(pkg, "tsconfig.json");
  if (!fs.existsSync(tsconfigPath)) {
    console.warn(chalk4.yellow("Compile:Validate skipped \u2014 tsconfig.json not found"));
    return 0;
  }
  if (isVerbose) {
    console.log(chalk4.cyan("Compile:Validate [tsconfig.json]"));
  }
  const { configPath, diagnostics } = createFullValidationConfig(pkg, tsconfigPath);
  if (diagnostics.length > 0) {
    console.error(formatTsConfigDiagnostics(diagnostics, pkg));
    return 1;
  }
  const result = runTsc(configPath, isVerbose);
  if (result !== 0) {
    console.error(chalk4.red(`Compile:Validate failed (tsc exit code ${result})`));
  }
  return result;
};

// src/actions/package/compile/packageCompileEsbuild.ts
var isBuildFormat = (value) => {
  return typeof value === "string" && ["cjs", "esm", "iife"].includes(value);
};
var legacyIgnoredOptions = /* @__PURE__ */ new Set([
  "clean",
  "cjsInterop",
  "dts",
  "entry",
  "outDir",
  "silent",
  "skipNodeModulesBundle"
]);
var supportedEsbuildOptions = /* @__PURE__ */ new Set([
  "absWorkingDir",
  "alias",
  "allowOverwrite",
  "assetNames",
  "banner",
  "bundle",
  "charset",
  "chunkNames",
  "color",
  "conditions",
  "define",
  "drop",
  "dropLabels",
  "entryNames",
  "entryPoints",
  "external",
  "footer",
  "format",
  "globalName",
  "ignoreAnnotations",
  "inject",
  "jsx",
  "jsxDev",
  "jsxFactory",
  "jsxFragment",
  "jsxImportSource",
  "jsxSideEffects",
  "keepNames",
  "legalComments",
  "lineLimit",
  "loader",
  "logLevel",
  "logLimit",
  "logOverride",
  "mainFields",
  "mangleCache",
  "mangleProps",
  "mangleQuoted",
  "metafile",
  "minify",
  "minifyIdentifiers",
  "minifySyntax",
  "minifyWhitespace",
  "nodePaths",
  "outExtension",
  "outbase",
  "outdir",
  "outfile",
  "packages",
  "platform",
  "plugins",
  "preserveSymlinks",
  "publicPath",
  "pure",
  "reserveProps",
  "resolveExtensions",
  "sourcemap",
  "sourceRoot",
  "sourcesContent",
  "splitting",
  "stdin",
  "supported",
  "target",
  "treeShaking",
  "tsconfig",
  "tsconfigRaw",
  "watch",
  "write"
]);
var relativePath2 = (from, to) => {
  const result = toPosixPath(path3.relative(from, to));
  return result.startsWith(".") ? result : `./${result}`;
};
var safeFilePart = (value) => value.replaceAll(/[^\dA-Za-z-]+/g, "-").replaceAll(/(?:^-|-$)/g, "") || "src";
var createEsbuildEntryPoints = (srcDir, entries, entryMode = "single") => {
  if (entryMode !== "custom") {
    return entries.map((entry) => `${srcDir}/${normalizeEntryPath(entry)}`);
  }
  const entryPoints = {};
  for (const entry of entries) {
    const outputName = customEntryOutputName(entry);
    if (entryPoints[outputName] !== void 0) {
      throw new Error(`Duplicate custom entry output "${outputName}" from "${entry}"`);
    }
    entryPoints[outputName] = `${srcDir}/${normalizeEntryPath(entry)}`;
  }
  return entryPoints;
};
var createTypescriptConfig = ({
  hasDeclaration = false,
  outDir,
  pkg,
  srcDir
}) => {
  const buildDir = path3.resolve(pkg, "build");
  const sourceRoot = path3.resolve(pkg, srcDir);
  return {
    compilerOptions: {
      ...hasDeclaration ? {
        declaration: true,
        declarationMap: true,
        emitDeclarationOnly: true,
        noEmit: false,
        outDir: outDir === void 0 ? void 0 : relativePath2(buildDir, path3.resolve(pkg, outDir))
      } : { noEmit: true },
      rootDir: relativePath2(buildDir, sourceRoot)
    },
    exclude: [
      `${relativePath2(buildDir, sourceRoot)}/**/*.spec.*`,
      `${relativePath2(buildDir, sourceRoot)}/**/*.stories.*`,
      `${relativePath2(buildDir, sourceRoot)}/**/*.example.*`,
      `${relativePath2(buildDir, sourceRoot)}/**/spec/**`,
      `${relativePath2(buildDir, sourceRoot)}/**/stories/**`
    ],
    extends: relativePath2(buildDir, path3.resolve(pkg, "tsconfig.json")),
    include: [`${relativePath2(buildDir, sourceRoot)}/**/*`]
  };
};
var writeTypescriptConfig = (pkg, platform, srcDir, hasDeclaration, outDir) => {
  const buildDir = path3.resolve(pkg, "build");
  fs2.mkdirSync(buildDir, { recursive: true });
  const configPath = path3.resolve(buildDir, `tsconfig.package-${hasDeclaration ? "dts" : "check"}-${platform}-${safeFilePart(srcDir)}.json`);
  fs2.writeFileSync(
    configPath,
    `${JSON.stringify(createTypescriptConfig({
      hasDeclaration,
      outDir,
      pkg,
      srcDir
    }), null, 2)}
`
  );
  return configPath;
};
function copyDeclarationFiles(fromDir, toDir) {
  if (path3.resolve(fromDir) === path3.resolve(toDir) || !fs2.existsSync(fromDir)) return;
  const copyFrom = (dir) => {
    const entries = fs2.readdirSync(dir, { withFileTypes: true });
    for (const entry of entries) {
      const sourcePath = path3.join(dir, entry.name);
      const relative = path3.relative(fromDir, sourcePath);
      const targetPath = path3.join(toDir, relative);
      if (entry.isDirectory()) {
        copyFrom(sourcePath);
      } else if (entry.isFile() && (entry.name.endsWith(".d.ts") || entry.name.endsWith(".d.ts.map"))) {
        fs2.mkdirSync(path3.dirname(targetPath), { recursive: true });
        fs2.copyFileSync(sourcePath, targetPath);
      }
    }
  };
  copyFrom(fromDir);
}
var normalizeOptions = (options = {}, isVerbose = false) => {
  const normalized = {};
  const ignored = [];
  for (const [key, value] of Object.entries(options)) {
    if (legacyIgnoredOptions.has(key)) {
      ignored.push(key);
      continue;
    }
    if (key === "format" && Array.isArray(value)) {
      if (isBuildFormat(value[0])) {
        normalized.format = value[0];
      }
      continue;
    }
    if (key === "outExtension" && typeof value === "function") {
      ignored.push(key);
      continue;
    }
    if (supportedEsbuildOptions.has(key)) {
      normalized[key] = value;
      continue;
    }
    ignored.push(key);
  }
  if (isVerbose && ignored.length > 0) {
    console.warn(chalk5.yellow(`[xy] Ignoring unsupported compile options for esbuild: ${[...new Set(ignored)].toSorted((a, b) => a.localeCompare(b)).join(", ")}`));
  }
  return normalized;
};
var esbuildOptions = (options = [], isVerbose = false) => {
  const standardLoaders = {
    ".gif": "copy",
    ".html": "copy",
    ".jpg": "copy",
    ".json": "json",
    ".png": "copy",
    ".svg": "text",
    ".webp": "copy"
  };
  const standardOptions = {
    bundle: true,
    format: "esm",
    loader: standardLoaders,
    outExtension: { ".js": ".mjs" },
    packages: "external",
    sourcemap: true,
    target: "esnext"
  };
  return deepMergeObjects([standardOptions, ...options.map((option) => normalizeOptions(option, isVerbose))]);
};
var resolvePlatformEsbuildOptions = (value) => {
  if (typeof value !== "object" || value === null) return {};
  const entry = value.esbuildOptions;
  return entry ?? {};
};
var getRootOutDir = (options) => {
  return options.outDir ?? options.outdir ?? "dist";
};
var compileFolder = async (srcDir, entries, options, platform, outDir, shouldCheckTypes, emitDeclarations, entryMode, isVerbose) => {
  const pkg = cwd3();
  if (isVerbose ?? false) {
    console.log(chalk5.gray(`compileFolder [${srcDir}, ${outDir}]`));
  }
  if (!fs2.existsSync(path3.resolve(pkg, srcDir))) {
    if (isVerbose ?? false) {
      console.warn(chalk5.yellow(`No source directory found in ${srcDir} to compile`));
    }
    return 0;
  }
  if (entries.length === 0) {
    console.warn(chalk5.yellow(`No entries found in ${srcDir} to compile`));
    return 0;
  }
  if (shouldCheckTypes) {
    const checkConfig = writeTypescriptConfig(pkg, platform, srcDir, false);
    const checkResult = runTsc(checkConfig, isVerbose);
    if (checkResult !== 0) {
      console.error(chalk5.red(`Compile:Validation had ${checkResult} errors`));
      return checkResult;
    }
  }
  const declarationResult = await emitDeclarations();
  if (declarationResult !== 0) {
    console.error(chalk5.red(`Compile:Declaration emit had ${declarationResult} errors`));
    return declarationResult;
  }
  const buildOptions = {
    ...options,
    entryPoints: createEsbuildEntryPoints(srcDir, entries, entryMode),
    outbase: srcDir,
    outdir: outDir,
    platform,
    tsconfig: "tsconfig.json"
  };
  if (isVerbose ?? false) {
    console.log(chalk5.cyan(`ESBUILD:build:start [${srcDir}]`));
    const pluginNames = buildOptions.plugins?.map((plugin) => plugin.name);
    const loggedOptions = { ...buildOptions, plugins: pluginNames };
    console.log(chalk5.gray(`ESBUILD:build:options [${JSON.stringify(loggedOptions, null, 2)}]`));
  }
  await build(buildOptions);
  if (isVerbose ?? false) {
    console.log(chalk5.cyan(`ESBUILD:build:stop [${srcDir}]`));
  }
  return 0;
};
function createDeclarationEmitter(isVerbose = false) {
  const declarationPromises = /* @__PURE__ */ new Map();
  return async (srcDir, rootOutDir, platform, outDir) => {
    const key = `${srcDir}\0${rootOutDir}`;
    let promise = declarationPromises.get(key);
    if (!promise) {
      promise = (async () => {
        const dtsConfig = writeTypescriptConfig(cwd3(), platform, srcDir, true, outDir);
        return { outDir, result: runTsc(dtsConfig, isVerbose) };
      })();
      declarationPromises.set(key, promise);
    }
    const emitted = await promise;
    if (emitted.result !== 0) return emitted.result;
    copyDeclarationFiles(emitted.outDir, outDir);
    return 0;
  };
}
function resolveCompileMode(config, mode) {
  const compile = config?.compile;
  const isVerbose = config?.verbose ?? false;
  const shouldEmit = !(mode.validateOnly ?? false);
  const shouldRunFullValidation = !(mode.emitOnly ?? false) && (compile?.validate ?? true);
  return {
    compile,
    shouldEmit,
    shouldRunFolderChecks: !shouldRunFullValidation && !(mode.emitOnly ?? false),
    shouldRunFullValidation,
    isVerbose
  };
}
async function compilePlatformEntries({
  shouldCheckTypes,
  compile,
  emitDeclarations,
  entries,
  legacyTsupOptions,
  platform,
  platformEsbuild,
  isVerbose
}) {
  const results = await Promise.all(
    Object.entries(entries).map(async ([srcDir, options]) => {
      const rootOutDir = getRootOutDir(options);
      const outDir = `${rootOutDir}/${platform}`;
      const entry = buildEntries(srcDir, compile?.entryMode, options, true, isVerbose);
      return compileFolder(
        srcDir,
        entry,
        esbuildOptions([
          platformEsbuild,
          legacyTsupOptions ?? {},
          compile?.esbuild?.options ?? {},
          options,
          { outdir: outDir }
        ], isVerbose),
        platform,
        outDir,
        shouldCheckTypes,
        () => emitDeclarations(srcDir, rootOutDir, platform, outDir),
        compile?.entryMode,
        isVerbose
      );
    })
  );
  return results.reduce((prev, value) => prev + value, 0);
}
var packageCompileEsbuild = async (config, mode = {}) => {
  const {
    compile,
    shouldEmit,
    shouldRunFolderChecks,
    shouldRunFullValidation,
    isVerbose
  } = resolveCompileMode(config, mode);
  if (isVerbose) {
    console.log("Compiling with esbuild and tsc");
  }
  if (shouldRunFullValidation) {
    const validateResult = validateFullPackage(cwd3(), isVerbose);
    if (validateResult !== 0) {
      return validateResult;
    }
  }
  if (!shouldEmit) return 0;
  const platforms = resolveCompilePlatforms(compile);
  warnDeprecatedEmptyObjectTargets(platforms.deprecatedEmptyObjectTargets);
  const compileForNode = platforms.node;
  const compileForBrowser = platforms.browser;
  const compileForNeutral = platforms.neutral;
  const nodeEsbuild = resolvePlatformEsbuildOptions(compile?.node);
  const browserEsbuild = resolvePlatformEsbuildOptions(compile?.browser);
  const neutralEsbuild = resolvePlatformEsbuildOptions(compile?.neutral);
  const legacyTsupOptions = compile?.tsup?.options;
  const emitDeclarations = createDeclarationEmitter(isVerbose);
  const [nodeErrors, browserErrors, neutralErrors] = await Promise.all([
    compilePlatformEntries({
      shouldCheckTypes: shouldRunFolderChecks,
      compile,
      emitDeclarations,
      entries: compileForNode,
      legacyTsupOptions,
      platform: "node",
      platformEsbuild: nodeEsbuild,
      isVerbose
    }),
    compilePlatformEntries({
      shouldCheckTypes: shouldRunFolderChecks,
      compile,
      emitDeclarations,
      entries: compileForBrowser,
      legacyTsupOptions,
      platform: "browser",
      platformEsbuild: browserEsbuild,
      isVerbose
    }),
    compilePlatformEntries({
      shouldCheckTypes: shouldRunFolderChecks,
      compile,
      emitDeclarations,
      entries: compileForNeutral,
      legacyTsupOptions,
      platform: "neutral",
      platformEsbuild: neutralEsbuild,
      isVerbose
    })
  ]);
  return nodeErrors + browserErrors + neutralErrors;
};

// src/bin/package/compile-tsup.ts
try {
  process.exitCode = await packageCompileEsbuild();
} catch (reason) {
  console.error(chalk6.red(reason));
  process.exitCode = 1;
}
//# sourceMappingURL=compile-tsup.mjs.map
