const babel = require("@babel/core");
const fs = require("fs");
const path = require("path");
const t = babel.types;

const dirs = fs
  .readdirSync(path.join(__dirname, "../src/components"))
  .filter((file) => file.startsWith("Dage"));

/**
 * @type {{[name: string]: string[]}}
 */
const mapper = {};
const extensions = [".ts", ".tsx"];

const findFile = (file) => {
  if (fs.existsSync(file)) {
    if (fs.statSync(file).isDirectory()) {
      return findFile(path.join(file, "index"));
    }

    return file;
  }

  for (const ext of extensions) {
    const f = `${file}${ext}`;
    if (fs.existsSync(f)) {
      return f;
    }
  }

  return null;
};

const walk = (context, entry) => {
  const base = path.basename(context);

  const addMapper = (name) => {
    mapper[base] = mapper[base] || [];
    mapper[base].push(name);
  };
  /**
   * @type {import('@babel/core').PluginObj}
   */
  const plugin = {
    visitor: {
      ExportNamedDeclaration(path) {
        const decl = path.node.declaration;

        if (t.isVariableDeclaration(decl)) {
          decl.declarations.forEach((i) => {
            if (t.isIdentifier(i.id)) {
              addMapper(i.id.name);
            }
          });
        } else if (
          (t.isFunctionDeclaration(decl) ||
            t.isTSInterfaceDeclaration(decl) ||
            t.isTSTypeAliasDeclaration(decl) ||
            t.isTSEnumDeclaration(decl) ||
            t.isClassDeclaration(decl)) &&
          t.isIdentifier(decl.id)
        ) {
          addMapper(decl.id.name);
        } else if (path.node.specifiers?.length) {
          path.node.specifiers.forEach((i) => {
            addMapper(i.exported.name);
          });
        }
      },
      ExportAllDeclaration(p) {
        // 递归
        if (p.node.source.value.startsWith("./")) {
          const relativeEntry = findFile(
            path.join(path.dirname(entry), p.node.source.value)
          );
          walk(context, relativeEntry);
        }
      },
    },
  };

  babel.transformFileSync(entry, {
    root: path.join(__dirname, "../"),
    configFile: false,
    babelrc: false,
    parserOpts: {
      plugins: [
        "decorators-legacy",
        "classProperties",
        "dynamicImport",
        "typescript",
        "jsx",
      ],
    },
    plugins: [plugin],
  });
};

dirs.forEach((dir) => {
  const context = path.join(__dirname, "../src/components", dir);
  const entry = findFile(path.join(context, "index"));

  if (entry == null) {
    return;
  }

  walk(context, entry);
});

fs.writeFileSync(
  path.join(__dirname, "../dist/metadata.json"),
  JSON.stringify(mapper, null, 2)
);
