"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
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 __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
var __runInitializers = (array, flags, self, value) => {
  for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
  return value;
};
var __decorateElement = (array, flags, name, decorators, target, extra) => {
  var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
  var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
  var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
  var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
    return __privateGet(this, extra);
  }, set [name](x) {
    return __privateSet(this, extra, x);
  } }, name));
  k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
  for (var i = decorators.length - 1; i >= 0; i--) {
    ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
    if (k) {
      ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
      if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
      if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
    }
    it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
    if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
    else if (typeof it !== "object" || it === null) __typeError("Object expected");
    else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
  }
  return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
};
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);

// src/core/program/index.ts
var index_exports = {};
__export(index_exports, {
  BaseProgram: () => BaseProgram,
  getHooks: () => getHooks,
  withConfigDefaults: () => withConfigDefaults,
  withConfigScope: () => withConfigScope
});
module.exports = __toCommonJS(index_exports);
var import_node_path2 = require("node:path");
var import_lodash2 = require("lodash");
var import_config = require("@diplodoc/cli/lib/config");
var import_logger = require("@diplodoc/cli/lib/logger");
var import_utils2 = require("@diplodoc/cli/lib/utils");

// src/core/program/hooks.ts
var import_tapable = require("tapable");
var import_utils = require("@diplodoc/cli/lib/utils");
function hooks(name) {
  return {
    Command: new import_tapable.SyncHook(
      ["command", "options"],
      `${name}.Command`
    ),
    /**
     * Async series hook which runs when current program config
     * was completely resolved but not merged with args.<br/>
     * **Config can't be modified here.**
     *
     * @usage Best place to validate that config doesn't store any secret data.
     *
     * @prop config - completely built config specified for current program.
     * @prop args - program call args.
     */
    RawConfig: new import_tapable.AsyncSeriesHook(
      ["config", "args"],
      `${name}.Config`
    ),
    /**
     * Async series **waterfall** hook which runs when current program config
     * was completely resolved and merged with args.<br/>
     * **Config can be modified here.**
     *
     * @usage Best place to normalize final config data.<br/>
     *
     * @prop config - completely built config specified for current program.
     * @prop args - previously merged to config program call args.
     */
    Config: new import_tapable.AsyncSeriesWaterfallHook(
      ["config", "args"],
      `${name}.Config`
    ),
    /**
     * Async series hook which runs before start of any Run type.<br/>
     *
     * @usage Best place to subscribe on Run services hooks.
     *
     * @prop run - [Run](../run) constructed context.
     */
    BeforeAnyRun: new import_tapable.AsyncSeriesHook(["run"], `${name}.BeforeAnyRun`),
    /**
     * Async series hook which runs after all entries processing.<br/>
     *
     * @usage Best place to emit additional assets on output fs.
     *
     * @prop run - [Run](../run) constructed context.
     */
    AfterAnyRun: new import_tapable.AsyncSeriesHook(["run"], `${name}.AfterAnyRun`),
    /**
     * Async series **waterfall** hook which runs when current program fails with error.<br/>
     * If someone from handlers will return `undefined`, then error will be ignored.
     *
     * @usage Best place to handle uncaught program errors.<br/>
     *
     * @prop error - thrown error or undefined.
     */
    Error: new import_tapable.AsyncSeriesWaterfallHook(["error"], `${name}.Error`)
  };
}
var [getHooksInternal, withHooks] = (0, import_utils.generateHooksAccess)("Base", hooks);
function getHooks(holder) {
  return getHooksInternal(holder);
}

// src/core/program/decorators.ts
var import_lodash = require("lodash");
var configDefaults = /* @__PURE__ */ Symbol("defaultConfig");
function getConfigDefaults(target) {
  return target[configDefaults] || {};
}
function withConfigDefaults(config) {
  return function(Class, { kind }) {
    if (kind !== "class") {
      throw new TypeError(`Decorator 'withConfigDefaults' is not applicable to '${kind}'.`);
    }
    return class extends Class {
      get [configDefaults]() {
        const moreDefaults = super[configDefaults] || {};
        return (0, import_lodash.merge)({}, moreDefaults, config());
      }
    };
  };
}
var configScope = /* @__PURE__ */ Symbol("configScope");
function getConfigScope(target) {
  return target[configScope] || {};
}
function withConfigScope(scope, options) {
  return function(Class, { kind }) {
    if (kind !== "class") {
      throw new TypeError(`Decorator 'withConfigScope' is not applicable to '${kind}'.`);
    }
    return class extends Class {
      get [configScope]() {
        return options?.strict ? { strictScope: scope } : { scope };
      }
    };
  };
}

// src/core/program/utils.ts
var import_node_path = require("node:path");
function isRelative(path) {
  return /^\.{1,2}\//.test(path || "");
}
function requireExtension(name) {
  if (isRelative(name) || (0, import_node_path.isAbsolute)(name)) {
    return require(name);
  }
  const local = (name2) => {
    if (name2.startsWith("@")) {
      return void 0;
    }
    return "@diplodoc/cli/" + name2;
  };
  const scoped = (name2, fixed = false) => {
    if (name2.startsWith("@")) {
      return void 0;
    }
    return "@diplodoc/" + name2 + (fixed ? "-extension" : "");
  };
  const precise = (name2, fixed = false) => {
    return name2 + (fixed ? "-extension" : "");
  };
  const variants = [
    local(name),
    scoped(name),
    scoped(name, true),
    precise(name),
    precise(name, true)
  ].filter(Boolean);
  while (variants.length) {
    const variant = variants.shift();
    try {
      return require(variant);
    } catch {
    }
  }
  throw new Error(`Unable to resolve '${name}' extension`);
}
function isProgram(module2) {
  return Boolean(module2 && typeof module2.init === "function");
}

// src/core/program/index.ts
var YFM_CONFIG_FILENAME = ".yfm";
var _BaseProgram_decorators, _init;
_BaseProgram_decorators = [withHooks, withConfigDefaults(() => ({
  strict: false,
  quiet: false,
  originAsInput: false,
  copyOnWrite: true,
  workerMaxOldSpace: 0
}))];
var BaseProgram = class {
  constructor(config, behavior = {}) {
    this.name = "Base";
    this.command = void 0;
    this.config = void 0;
    this.logger = new import_logger.Logger();
    this.options = void 0;
    this.modules = [];
    this.extensions = [];
    this._report = { code: 0 };
    this.parent = null;
    this.behavior = {};
    this.behavior = behavior;
    if (config) {
      const defaults = getConfigDefaults(this);
      this.config = (0, import_config.withConfigUtils)(process.cwd(), {
        ...defaults,
        ...config
      });
    }
  }
  static is(program) {
    return program instanceof this;
  }
  get report() {
    return this.parent ? this.parent.report : this._report;
  }
  async init(args, parent) {
    this.logger.setup(args);
    const config = parent?.config || await this.resolveConfig(args);
    const extensions = await this.resolveExtensions(config.extensions);
    this["config"] = parent?.config || await this.hookConfig(config, args);
    this.logger.setup(this["config"]);
    this.modules.push(...extensions);
    this.options.forEach((option) => {
      this.command.addOption(option);
    });
    this.command.action(() => this._action());
    for (const module2 of this.modules) {
      if (isProgram(module2)) {
        await module2.init(args, this);
      } else {
        module2.apply(this);
      }
    }
    this.apply(parent);
  }
  apply(program) {
    const isDefault = this.behavior.isDefaultCommand;
    this["parent"] = program;
    if (program) {
      getHooks(program).Command.tap(
        this.name,
        (0, import_lodash2.once)((command) => {
          command.addCommand(this.command, { isDefault });
          getHooks(this).Command.call(this.command, this.options);
        })
      );
      this.logger.pipe(program.logger);
    } else {
      getHooks(this).Command.call(this.command, this.options);
    }
  }
  async parse(args) {
    return this.command.parseAsync(args);
  }
  async action(_args) {
    throw new Error("Should be implemented");
  }
  args(args) {
    const options = this.options.map((option) => option.attributeName());
    return {
      ...this.parent?.args(args),
      ...(0, import_lodash2.pick)(args, options)
    };
  }
  addModule(module2) {
    this.modules.push(module2);
  }
  async resolveConfig(args) {
    const defaults = getConfigDefaults(this);
    const { scope, strictScope } = getConfigScope(this);
    const configPath = (0, import_node_path2.isAbsolute)(args.config) || isRelative(args.config) ? (0, import_node_path2.resolve)(args.config) : (0, import_node_path2.resolve)(args.input, args.config);
    const filter = strictScope && (0, import_config.strictScope)(strictScope) || scope && (0, import_config.scope)(scope);
    const config = await (0, import_config.resolveConfig)(configPath, {
      filter: filter || void 0,
      defaults,
      fallback: args.config === YFM_CONFIG_FILENAME ? defaults : null
    });
    config.extensions = this.normalizeExtensions(config, args);
    return config;
  }
  async hookConfig(config, args) {
    await getHooks(this).RawConfig.promise(config, args);
    (0, import_lodash2.merge)(config, (0, import_lodash2.omit)(this.args(args), ["extensions"]));
    return getHooks(this).Config.promise(config, args);
  }
  async _action() {
    const args = this.command.optsWithGlobals();
    const config = await this.resolveConfig(args);
    this["config"] = await this.hookConfig(config, args);
    try {
      await this.action(args);
    } catch (error) {
      error = await getHooks(this).Error.promise(error);
      if (!error) {
        return;
      }
      if ((0, import_utils2.own)(error, "hook")) {
        const { service, hook, name } = error.hook;
        console.error(
          `Intercept error for ${service}.${hook} hook from ${name} extension.`
        );
      }
      const message = (0, import_utils2.errorMessage)(error);
      if (message && !error?.logged) {
        console.error(message);
      }
      this.report.code = error ? 1 : 0;
    }
  }
  normalizeExtensions(config, args) {
    const argsExtensions = (args.extensions || []).map((ext) => {
      const name = isRelative(ext) ? (0, import_node_path2.resolve)(ext) : ext;
      const options = {};
      return { name, options };
    });
    const configExtensions = (config.extensions || []).map((ext) => {
      const extPath = typeof ext === "string" ? ext : ext.name;
      const name = isRelative(extPath) ? config.resolve(extPath) : extPath;
      const options = typeof ext === "string" ? {} : (0, import_lodash2.omit)(ext, "path");
      return { name, options };
    });
    return [...argsExtensions, ...configExtensions];
  }
  async resolveExtensions(extensions) {
    const initialize = async ({ name, options }) => {
      try {
        const ExtensionModule = requireExtension(name);
        const Extension = ExtensionModule.Extension || ExtensionModule.default;
        return new Extension(options);
      } catch (error) {
        this.logger.error(
          `Failed to load extension from ${name}: ${error instanceof Error ? error.message : String(error)}`
        );
        throw error;
      }
    };
    return Promise.all(extensions.map(initialize));
  }
};
_init = __decoratorStart(null);
BaseProgram = __decorateElement(_init, 0, "BaseProgram", _BaseProgram_decorators, BaseProgram);
__runInitializers(_init, 1, BaseProgram);
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  BaseProgram,
  getHooks,
  withConfigDefaults,
  withConfigScope
});
//# sourceMappingURL=index.js.map
