import { createRequire } from 'module';
import pathParser from 'path';
import fse from 'fs-extra';
import { format as format$1 } from 'prettier';
import EventEmitter from 'events';
import { v4 } from 'uuid';
import dirTree from 'directory-tree';

createRequire(import.meta.url);

// src/utils/isPromise.ts
var isPromise = (result) => {
  return typeof result?.then === "function";
};
var formatOptions = {
  tabWidth: 2,
  printWidth: 160,
  parser: "typescript",
  singleQuote: true,
  semi: false,
  bracketSameLine: false,
  endOfLine: "auto"
};
var format = (text) => {
  return format$1(text, formatOptions);
};

// src/utils/write.ts
var write = async (data, path, options = { format: false }) => {
  const formattedData = options.format ? format(data) : data;
  try {
    await fse.stat(path);
    const oldContent = await fse.readFile(path, { encoding: "utf-8" });
    if (oldContent?.toString() === formattedData) {
      return;
    }
  } catch (_err) {
    return fse.outputFile(path, formattedData, { encoding: "utf-8" });
  }
  return fse.outputFile(path, formattedData, { encoding: "utf-8" });
};
var clean = async (path) => {
  return fse.remove(path);
};

// src/utils/cache.ts
function createPluginCache(cache) {
  return {
    delete(id) {
      return delete cache[id];
    },
    get(id) {
      const item = cache[id];
      if (!item)
        return;
      item[0] = 0;
      return item[1];
    },
    has(id) {
      const item = cache[id];
      if (!item)
        return false;
      item[0] = 0;
      return true;
    },
    set(id, value) {
      cache[id] = [0, value];
    }
  };
}
var getRelativePath = (root, file) => {
  if (!root || !file) {
    throw new Error("Root and file should be filled in when retrieving the relativePath");
  }
  const newPath = pathParser.relative(root, file).replace("../", "").replace(".ts", "").trimEnd();
  return `./${newPath}`;
};
var getPathMode = (path) => {
  if (!path) {
    return void 0;
  }
  return pathParser.extname(path) ? "file" : "directory";
};
var read = async (path, encoding = "utf8") => {
  try {
    return fse.readFile(path, encoding);
  } catch (err) {
    console.error(err);
    throw err;
  }
};

// src/plugin.ts
function createPlugin(factory) {
  return (options) => {
    const plugin = factory(options);
    if (Array.isArray(plugin)) {
      throw new Error("Not implemented");
    }
    if (!plugin.transform) {
      plugin.transform = function transform(code) {
        return code;
      };
    }
    return plugin;
  };
}
var name = "core";
var isEmittedFile = (result) => {
  return !!result.id;
};
var definePlugin = createPlugin((options) => {
  const { fileManager, resolveId, load } = options;
  const api = {
    get config() {
      return options.config;
    },
    fileManager,
    async addFile(file, options2) {
      if (isEmittedFile(file)) {
        const resolvedId = await resolveId({ fileName: file.id, directory: file.importer, options: file.options });
        const path = resolvedId || file.importer || file.id;
        return fileManager.add({
          path,
          fileName: file.name || file.id,
          source: file.source || ""
        });
      }
      if (options2?.root) ;
      return fileManager.addOrAppend(file);
    },
    resolveId,
    load,
    cache: createPluginCache(/* @__PURE__ */ Object.create(null))
  };
  return {
    name,
    api,
    resolveId(fileName, directory) {
      if (!directory) {
        return null;
      }
      return pathParser.resolve(directory, fileName);
    }
  };
});

// src/managers/fileManager/events.ts
var keys = {
  getFileKey: () => `file`,
  getStatusChangeKey: () => `status-change`,
  getStatusChangeByIdKey: (id) => `${id}-status-change`,
  getSuccessKey: () => `success`,
  getRemoveKey: (id) => `${id}remove`
};
var getFileManagerEvents = (emitter) => {
  return {
    emitFile: (id, file) => {
      emitter.emit(keys.getFileKey(), id, file);
    },
    emitStatusChange: (file) => {
      emitter.emit(keys.getStatusChangeKey(), file);
    },
    emitStatusChangeById: (id, status) => {
      emitter.emit(keys.getStatusChangeByIdKey(id), status);
    },
    emitSuccess: () => {
      emitter.emit(keys.getSuccessKey());
    },
    emitRemove: (id, file) => {
      emitter.emit(keys.getRemoveKey(id), file);
    },
    onAdd: (callback) => {
      emitter.on(keys.getFileKey(), callback);
      return () => emitter.removeListener(keys.getFileKey(), callback);
    },
    onStatusChange: (callback) => {
      emitter.on(keys.getStatusChangeKey(), callback);
      return () => emitter.removeListener(keys.getStatusChangeKey(), callback);
    },
    onStatusChangeById: (id, callback) => {
      emitter.on(keys.getStatusChangeByIdKey(id), callback);
      return () => emitter.removeListener(keys.getStatusChangeByIdKey(id), callback);
    },
    onSuccess: (callback) => {
      emitter.on(keys.getSuccessKey(), callback);
      return () => emitter.removeListener(keys.getSuccessKey(), callback);
    },
    onRemove: (id, callback) => {
      emitter.on(keys.getRemoveKey(id), callback);
      return () => emitter.removeListener(keys.getRemoveKey(id), callback);
    }
  };
};

// src/managers/fileManager/FileManager.ts
var FileManager = class {
  cache = /* @__PURE__ */ new Map();
  emitter = new EventEmitter();
  events = getFileManagerEvents(this.emitter);
  constructor() {
    this.events.onStatusChange(() => {
      if (this.getCountByStatus("removed") === this.cache.size) {
        this.events.emitSuccess();
      }
    });
  }
  getCache(id) {
    return this.cache.get(id);
  }
  getCacheByPath(path) {
    let cache;
    this.cache.forEach((item) => {
      if (item.file.path === path) {
        cache = item;
      }
    });
    return cache;
  }
  getCountByStatus(status) {
    let count = 0;
    this.cache.forEach((item) => {
      if (item.status === status) {
        count++;
      }
    });
    return count;
  }
  get files() {
    const files = [];
    this.cache.forEach((item) => {
      files.push(item.file);
    });
    return files;
  }
  add(file) {
    const cacheItem = { id: v4(), file, status: "new" };
    this.cache.set(cacheItem.id, cacheItem);
    this.events.emitFile(cacheItem.id, file);
    return new Promise((resolve) => {
      const unsubscribe = this.events.onRemove(cacheItem.id, (file2) => {
        resolve(file2);
        unsubscribe();
      });
    });
  }
  addOrAppend(file) {
    const previousCache = this.getCacheByPath(file.path);
    if (previousCache) {
      this.remove(previousCache.id);
      return this.add({
        ...file,
        source: `${previousCache.file.source}
${file.source}`
      });
    }
    return this.add(file);
  }
  setStatus(id, status) {
    const cacheItem = this.getCache(id);
    if (!cacheItem) {
      return;
    }
    cacheItem.status = status;
    this.cache.set(id, cacheItem);
    this.events.emitStatusChange(cacheItem.file);
    this.events.emitStatusChangeById(id, status);
  }
  get(id) {
    const cacheItem = this.getCache(id);
    return cacheItem?.file;
  }
  remove(id) {
    const cacheItem = this.getCache(id);
    if (!cacheItem) {
      return;
    }
    this.setStatus(id, "removed");
    this.events.emitRemove(id, cacheItem.file);
  }
  async write(...params) {
    return write(...params);
  }
  async read(...params) {
    return read(...params);
  }
};
var TreeNode = class {
  data;
  parent;
  children;
  constructor(data, parent) {
    this.data = data;
    this.parent = parent;
    return this;
  }
  addChild(data) {
    const child = new TreeNode(data, this);
    if (!this.children) {
      this.children = [];
    }
    this.children.push(child);
    return child;
  }
  find(data) {
    if (data === this.data) {
      return this;
    }
    if (this.children) {
      for (let i = 0, { length } = this.children, target = null; i < length; i++) {
        target = this.children[i].find(data);
        if (target) {
          return target;
        }
      }
    }
    return null;
  }
  leaves() {
    if (!this.children || this.children.length === 0) {
      return [this];
    }
    const leaves = [];
    if (this.children) {
      for (let i = 0, { length } = this.children; i < length; i++) {
        leaves.push.apply(leaves, this.children[i].leaves());
      }
    }
    return leaves;
  }
  root() {
    if (!this.parent) {
      return this;
    }
    return this.parent.root();
  }
  forEach(callback) {
    if (typeof callback !== "function") {
      throw new TypeError("forEach() callback must be a function");
    }
    callback(this);
    if (this.children) {
      for (let i = 0, { length } = this.children; i < length; i++) {
        this.children[i].forEach(callback);
      }
    }
    return this;
  }
  static build(path, options = {}) {
    const filteredTree = dirTree(path, { extensions: options?.extensions, exclude: options.exclude });
    if (!filteredTree) {
      return;
    }
    const treeNode = new TreeNode({ name: filteredTree.name, path: filteredTree.path, type: filteredTree.type });
    const recurse = (node, item) => {
      const subNode = node.addChild({ name: item.name, path: item.path, type: item.type });
      if (item.children?.length) {
        item.children?.forEach((child) => {
          recurse(subNode, child);
        });
      }
    };
    filteredTree.children?.forEach((child) => recurse(treeNode, child));
    return treeNode;
  }
};

// src/managers/pluginManager/PluginManager.ts
var hookNames = {
  validate: 1,
  buildStart: 1,
  resolveId: 1,
  load: 1,
  transform: 1,
  writeFile: 1,
  buildEnd: 1
};
var hooks = Object.keys(hookNames);
var PluginManager = class {
  plugins;
  fileManager;
  logger;
  config;
  core;
  constructor(config, options) {
    this.logger = options.logger;
    this.config = config;
    this.fileManager = new FileManager();
    this.core = definePlugin({
      config,
      fileManager: this.fileManager,
      load: this.load,
      resolveId: this.resolveId
    });
    this.plugins = [this.core, ...config.plugins || []];
  }
  resolveId = (params) => {
    if (params.pluginName) {
      return this.hookForPlugin(params.pluginName, "resolveId", [params.fileName, params.directory, params.options]);
    }
    return this.hookFirst("resolveId", [params.fileName, params.directory, params.options]);
  };
  load = async (id) => {
    return this.hookFirst("load", [id]);
  };
  hookForPlugin(pluginName, hookName, parameters, skipped) {
    let promise = Promise.resolve(null);
    for (const plugin of this.getSortedPlugins(hookName, pluginName)) {
      if (skipped && skipped.has(plugin))
        continue;
      promise = promise.then((result) => {
        if (result != null)
          return result;
        return this.run("hookFirst", hookName, parameters, plugin);
      });
    }
    return promise;
  }
  hookFirst(hookName, parameters, skipped) {
    let promise = Promise.resolve(null);
    for (const plugin of this.getSortedPlugins(hookName)) {
      if (skipped && skipped.has(plugin))
        continue;
      promise = promise.then((result) => {
        if (result != null)
          return result;
        return this.run("hookFirst", hookName, parameters, plugin);
      });
    }
    return promise;
  }
  async hookParallel(hookName, parameters) {
    const parallelPromises = [];
    for (const plugin of this.getSortedPlugins(hookName)) {
      if (plugin[hookName]?.sequential) {
        await Promise.all(parallelPromises);
        parallelPromises.length = 0;
        await this.run("hookParallel", hookName, parameters, plugin);
      } else {
        const promise = this.run("hookParallel", hookName, parameters, plugin);
        parallelPromises.push(promise);
      }
    }
    return Promise.all(parallelPromises);
  }
  hookReduceArg0(hookName, [argument0, ...rest], reduce) {
    let promise = Promise.resolve(argument0);
    for (const plugin of this.getSortedPlugins(hookName)) {
      promise = promise.then(
        (argument02) => this.run("hookReduceArg0", hookName, [argument02, ...rest], plugin).then(
          (result) => reduce.call(this.core.api, argument02, result, plugin)
        )
      );
    }
    return promise;
  }
  hookSeq(hookName, parameters) {
    let promise = Promise.resolve();
    for (const plugin of this.getSortedPlugins(hookName)) {
      promise = promise.then(() => this.run("hookSeq", hookName, parameters, plugin));
    }
    return promise.then(noReturn);
  }
  getSortedPlugins(hookName, pluginName) {
    const plugins = [...this.plugins];
    if (pluginName) {
      const pluginsByPluginName = plugins.filter((item) => item.name === pluginName && item[hookName]);
      if (pluginsByPluginName.length === 0) {
        if (this.config.logLevel === "warn" && this.logger?.spinner) {
          this.logger.spinner.info(`Plugin hook with ${hookName} not found for plugin ${pluginName}`);
        }
        return [this.core];
      }
      return pluginsByPluginName;
    }
    return plugins;
  }
  run(strategy, hookName, parameters, plugin) {
    const hook = plugin[hookName];
    return Promise.resolve().then(() => {
      if (typeof hook !== "function") {
        return hook;
      }
      if (this.config.logLevel === "info" && this.logger?.spinner) {
        this.logger.spinner.text = `[${strategy}] ${hookName}: Excecuting task for plugin ${plugin.name} 
`;
      }
      const hookResult = hook.apply(this.core.api, parameters);
      if (!hookResult?.then) {
        if (this.config.logLevel === "info" && this.logger?.spinner) {
          this.logger.spinner.succeed(`[${strategy}] ${hookName}: Excecuting task for plugin ${plugin.name} 
`);
        }
        return hookResult;
      }
      return Promise.resolve(hookResult).then((result) => {
        if (this.config.logLevel === "info" && this.logger?.spinner) {
          this.logger.spinner.succeed(`[${strategy}] ${hookName}: Excecuting task for plugin ${plugin.name} 
`);
        }
        return result;
      });
    }).catch((e) => this.catcher(e, plugin, hookName));
  }
  runSync(hookName, parameters, plugin) {
    const hook = plugin[hookName];
    try {
      return hook.apply(this.core.api, parameters);
    } catch (error) {
      return error;
    }
  }
  catcher(e, plugin, hookName) {
    const text = `${e.message} (plugin: ${plugin.name}, hook: ${hookName})
`;
    if (this.logger?.spinner) {
      this.logger.spinner.fail(text);
      throw e;
    }
  }
};
function noReturn() {
}

// src/managers/pluginManager/validate.ts
var ValidationPluginError = class extends Error {
};
var validatePlugins = (plugins, dependedPluginNames) => {
  let pluginNames = [];
  if (typeof dependedPluginNames === "string") {
    pluginNames = [dependedPluginNames];
  } else {
    pluginNames = dependedPluginNames;
  }
  pluginNames.forEach((pluginName) => {
    const exists = plugins.some((plugin) => plugin.name === pluginName);
    if (!exists) {
      throw new ValidationPluginError(`This plugin depends on the ${pluginName} plugin.`);
    }
  });
  return true;
};

// src/build.ts
async function transformReducer(_previousCode, result, _plugin) {
  if (result === null) {
    return null;
  }
  return result;
}
async function buildImplementation(options, done) {
  const { config, logger } = options;
  if (config.output.clean) {
    await clean(config.output.path);
  }
  const pluginManager = new PluginManager(config, { logger });
  const { plugins, fileManager } = pluginManager;
  try {
    await pluginManager.hookParallel("validate", [plugins]);
  } catch (e) {
    return;
  }
  fileManager.events.onSuccess(async () => {
    await pluginManager.hookParallel("buildEnd");
    setTimeout(() => {
      done({ fileManager });
    }, 1e3);
  });
  fileManager.events.onAdd(async (id, file) => {
    const { path } = file;
    let { source: code } = file;
    const loadedResult = await pluginManager.hookFirst("load", [path]);
    if (loadedResult) {
      code = loadedResult;
    }
    if (code) {
      const transformedCode = await pluginManager.hookReduceArg0("transform", [code, path], transformReducer);
      if (typeof config.input === "object") {
        await pluginManager.hookParallel("writeFile", [transformedCode, path]);
      }
      fileManager.setStatus(id, "success");
      fileManager.remove(id);
    }
  });
  await pluginManager.hookParallel("buildStart", [config]);
}
function build(options) {
  return new Promise((resolve, reject) => {
    try {
      buildImplementation(options, resolve);
    } catch (e) {
      reject(e);
    }
  });
}

// src/config.ts
var defineConfig = (options) => options;

// src/generators/Generator.ts
var Generator = class {
  _options = {};
  constructor(options = {}) {
    if (options) {
      this._options = {
        ...this._options,
        ...options
      };
    }
    return this;
  }
  get options() {
    return this._options;
  }
};

// src/generators/SchemaGenerator.ts
var SchemaGenerator = class extends Generator {
};

// src/index.ts
var src_default = build;

export { FileManager, Generator, PluginManager, SchemaGenerator, TreeNode, ValidationPluginError, build, clean, createPlugin, createPluginCache, src_default as default, defineConfig, format, getPathMode, getRelativePath, hooks, isPromise, read, validatePlugins, write };
//# sourceMappingURL=out.js.map
//# sourceMappingURL=index.mjs.map