'use strict';

const ast = require('./shared/babel-plugin-lingui-macro.b7bd8916.cjs');
const t = require('@babel/types');
const conf = require('@lingui/conf');
require('@lingui/message-utils/generateMessageId');

function _interopNamespaceCompat(e) {
  if (e && typeof e === 'object' && 'default' in e) return e;
  const n = Object.create(null);
  if (e) {
    for (const k in e) {
      n[k] = e[k];
    }
  }
  n.default = e;
  return n;
}

const t__namespace = /*#__PURE__*/_interopNamespaceCompat(t);

function cleanJSXElementLiteralChild(value) {
  const lines = value.split(/\r\n|\n|\r/);
  let lastNonEmptyLine = 0;
  for (let i = 0; i < lines.length; i++) {
    if (lines[i].match(/[^ \t]/)) {
      lastNonEmptyLine = i;
    }
  }
  let str = "";
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];
    const isFirstLine = i === 0;
    const isLastLine = i === lines.length - 1;
    const isLastNonEmptyLine = i === lastNonEmptyLine;
    let trimmedLine = line.replace(/\t/g, " ");
    if (!isFirstLine) {
      trimmedLine = trimmedLine.replace(/^[ ]+/, "");
    }
    if (!isLastLine) {
      trimmedLine = trimmedLine.replace(/[ ]+$/, "");
    }
    if (trimmedLine) {
      if (!isLastNonEmptyLine) {
        trimmedLine += " ";
      }
      str += trimmedLine;
    }
  }
  return str;
}

var __defProp$1 = Object.defineProperty;
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$1 = (obj, key, value) => {
  __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
const pluralRuleRe = /(_[\d\w]+|zero|one|two|few|many|other)/;
const jsx2icuExactChoice = (value) => value.replace(/_(\d+)/, "=$1").replace(/_(\w+)/, "$1");
function maybeNodeValue(node) {
  if (!node)
    return null;
  if (node.type === "StringLiteral")
    return { text: node.value, loc: node.loc };
  if (node.type === "JSXAttribute")
    return maybeNodeValue(node.value);
  if (node.type === "JSXExpressionContainer")
    return maybeNodeValue(node.expression);
  if (node.type === "TemplateLiteral" && node.expressions.length === 0)
    return { text: node.quasis[0].value.raw, loc: node.loc };
  return null;
}
class MacroJSX {
  constructor({ types }, opts) {
    __publicField$1(this, "types");
    __publicField$1(this, "ctx");
    __publicField$1(this, "replacePath", (path) => {
      if (!path.isJSXElement()) {
        return false;
      }
      const tokens = this.tokenizeNode(path, true, true);
      if (!tokens) {
        return false;
      }
      const { attributes, id, comment, context } = this.stripMacroAttributes(
        path
      );
      if (!tokens.length) {
        throw new Error("Incorrect usage of Trans");
      }
      const messageDescriptor = ast.createMessageDescriptorFromTokens(
        tokens,
        path.node.loc,
        this.ctx.stripNonEssentialProps,
        this.ctx.stripMessageProp,
        {
          id,
          context,
          comment
        }
      );
      attributes.push(this.types.jsxSpreadAttribute(messageDescriptor));
      const newNode = this.types.jsxElement(
        this.types.jsxOpeningElement(
          this.types.jsxIdentifier(this.ctx.transImportName),
          attributes,
          true
        ),
        null,
        [],
        true
      );
      newNode.loc = path.node.loc;
      return newNode;
    });
    __publicField$1(this, "attrName", (names, exclude = false) => {
      const namesRe = new RegExp("^(" + names.join("|") + ")$");
      return (attr) => {
        const name = attr.name.name;
        return exclude ? !namesRe.test(name) : namesRe.test(name);
      };
    });
    __publicField$1(this, "stripMacroAttributes", (path) => {
      const { attributes } = path.node.openingElement;
      const id = attributes.find(this.attrName([ast.MsgDescriptorPropKey.id]));
      const message = attributes.find(
        this.attrName([ast.MsgDescriptorPropKey.message])
      );
      const comment = attributes.find(
        this.attrName([ast.MsgDescriptorPropKey.comment])
      );
      const context = attributes.find(
        this.attrName([ast.MsgDescriptorPropKey.context])
      );
      let reserved = [
        ast.MsgDescriptorPropKey.id,
        ast.MsgDescriptorPropKey.message,
        ast.MsgDescriptorPropKey.comment,
        ast.MsgDescriptorPropKey.context
      ];
      if (this.isChoiceComponent(path)) {
        reserved = [
          ...reserved,
          "_\\w+",
          "_\\d+",
          "zero",
          "one",
          "two",
          "few",
          "many",
          "other",
          "value",
          "offset"
        ];
      }
      return {
        id: maybeNodeValue(id),
        message: maybeNodeValue(message),
        comment: maybeNodeValue(comment),
        context: maybeNodeValue(context),
        attributes: attributes.filter(this.attrName(reserved, true))
      };
    });
    __publicField$1(this, "tokenizeNode", (path, ignoreExpression = false, ignoreElement = false) => {
      if (this.isTransComponent(path)) {
        return this.tokenizeTrans(path);
      }
      const componentName = this.isChoiceComponent(path);
      if (componentName) {
        return [
          this.tokenizeChoiceComponent(
            path,
            componentName
          )
        ];
      }
      if (path.isJSXElement() && !ignoreElement) {
        return [this.tokenizeElement(path)];
      }
      if (!ignoreExpression) {
        return [this.tokenizeExpression(path)];
      }
    });
    __publicField$1(this, "tokenizeTrans", (path) => {
      return path.get("children").flatMap((child) => this.tokenizeChildren(child)).filter(Boolean);
    });
    __publicField$1(this, "tokenizeChildren", (path) => {
      if (path.isJSXExpressionContainer()) {
        const exp = path.get("expression");
        if (exp.isStringLiteral()) {
          return [this.tokenizeText(exp.node.value)];
        }
        if (exp.isTemplateLiteral()) {
          return this.tokenizeTemplateLiteral(exp);
        }
        if (exp.isConditionalExpression()) {
          return [this.tokenizeConditionalExpression(exp)];
        }
        if (exp.isJSXElement()) {
          return this.tokenizeNode(exp);
        }
        return [this.tokenizeExpression(exp)];
      } else if (path.isJSXElement()) {
        return this.tokenizeNode(path);
      } else if (path.isJSXSpreadChild()) {
        throw new Error(
          "Incorrect usage of Trans: Spread could not be used as Trans children"
        );
      } else if (path.isJSXText()) {
        return [this.tokenizeText(cleanJSXElementLiteralChild(path.node.value))];
      } else ;
    });
    __publicField$1(this, "tokenizeChoiceComponent", (path, componentName) => {
      const element = path.get("openingElement");
      const format = componentName.toLowerCase();
      const props = element.get("attributes").filter((attr) => {
        return this.attrName(
          [
            ast.MsgDescriptorPropKey.id,
            ast.MsgDescriptorPropKey.comment,
            ast.MsgDescriptorPropKey.message,
            ast.MsgDescriptorPropKey.context,
            "key",
            // we remove <Trans /> react props that are not useful for translation
            "render",
            "component",
            "components"
          ],
          true
        )(attr.node);
      });
      let token = {
        type: "arg",
        format,
        name: null,
        value: void 0,
        options: {
          offset: void 0
        }
      };
      for (const _attr of props) {
        if (_attr.isJSXSpreadAttribute()) {
          continue;
        }
        const attr = _attr;
        if (this.types.isJSXNamespacedName(attr.node.name)) {
          continue;
        }
        const name = attr.node.name.name;
        const value = attr.get("value");
        if (name === "value") {
          token = {
            ...token,
            ...this.tokenizeExpression(
              value.isLiteral() ? value : value.get("expression")
            )
          };
        } else if (format !== "select" && name === "offset") {
          token.options.offset = value.isStringLiteral() || value.isNumericLiteral() ? value.node.value : value.get(
            "expression"
          ).node.value;
        } else {
          let option;
          if (value.isStringLiteral()) {
            option = value.node.extra.raw.replace(
              /(["'])(.*)\1/,
              "$2"
            );
          } else {
            option = this.tokenizeChildren(value);
          }
          if (pluralRuleRe.test(name)) {
            token.options[jsx2icuExactChoice(name)] = option;
          } else {
            token.options[name] = option;
          }
        }
      }
      return token;
    });
    __publicField$1(this, "tokenizeElement", (path) => {
      const name = this.ctx.elementIndex();
      return {
        type: "element",
        name,
        value: {
          ...path.node,
          children: [],
          openingElement: {
            ...path.node.openingElement,
            selfClosing: true
          }
        },
        children: this.tokenizeTrans(path)
      };
    });
    __publicField$1(this, "tokenizeExpression", (path) => {
      return ast.tokenizeExpression(path.node, this.ctx);
    });
    __publicField$1(this, "tokenizeConditionalExpression", (exp) => {
      exp.traverse(
        {
          JSXElement: (el) => {
            if (this.isTransComponent(el) || this.isChoiceComponent(el)) {
              this.replacePath(el);
              el.skip();
            }
          }
        },
        exp.state
      );
      return this.tokenizeExpression(exp);
    });
    __publicField$1(this, "tokenizeText", (value) => {
      return {
        type: "text",
        value
      };
    });
    __publicField$1(this, "isLinguiComponent", (path, name) => {
      if (!path.isJSXElement()) {
        return false;
      }
      const config = path.context.state.get(
        "linguiConfig"
      );
      const identifier = path.get("openingElement").get("name");
      return config.macro.jsxPackage.some(
        (moduleSource) => identifier.referencesImport(moduleSource, name)
      );
    });
    __publicField$1(this, "isTransComponent", (path) => {
      return this.isLinguiComponent(path, ast.JsxMacroName.Trans);
    });
    __publicField$1(this, "isChoiceComponent", (path) => {
      if (this.isLinguiComponent(path, ast.JsxMacroName.Plural)) {
        return ast.JsxMacroName.Plural;
      }
      if (this.isLinguiComponent(path, ast.JsxMacroName.Select)) {
        return ast.JsxMacroName.Select;
      }
      if (this.isLinguiComponent(path, ast.JsxMacroName.SelectOrdinal)) {
        return ast.JsxMacroName.SelectOrdinal;
      }
    });
    this.types = types;
    this.ctx = {
      ...ast.createMacroJsContext(
        opts.isLinguiIdentifier,
        opts.stripNonEssentialProps,
        opts.stripMessageProp
      ),
      transImportName: opts.transImportName,
      elementIndex: ast.makeCounter()
    };
  }
  tokenizeTemplateLiteral(exp) {
    const expressions = exp.get("expressions");
    return exp.get("quasis").flatMap(({ node: text }, i) => {
      const value = text.value.cooked;
      let argTokens = [];
      const currExp = expressions[i];
      if (currExp) {
        argTokens = currExp.isCallExpression() ? this.tokenizeNode(currExp) : [this.tokenizeExpression(currExp)];
      }
      return [...value ? [this.tokenizeText(value)] : [], ...argTokens];
    });
  }
}

var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class MacroJs {
  constructor(opts) {
    // Identifier of i18n object
    __publicField(this, "i18nImportName");
    __publicField(this, "useLinguiImportName");
    __publicField(this, "needsUseLinguiImport", false);
    __publicField(this, "needsI18nImport", false);
    __publicField(this, "_ctx");
    __publicField(this, "replacePathWithMessage", (path, tokens, linguiInstance) => {
      return this.createI18nCall(
        ast.createMessageDescriptorFromTokens(
          tokens,
          path.node.loc,
          this._ctx.stripNonEssentialProps,
          this._ctx.stripMessageProp
        ),
        linguiInstance
      );
    });
    __publicField(this, "replacePath", (path) => {
      const ctx = this._ctx;
      if (
        //
        path.isCallExpression() && ast.isDefineMessage(path.get("callee").node, ctx)
      ) {
        return ast.processDescriptor(
          path.get("arguments")[0].node,
          ctx
        );
      }
      if (path.isTaggedTemplateExpression() && ast.isDefineMessage(path.get("tag").node, ctx)) {
        const tokens2 = ast.tokenizeTemplateLiteral(path.get("quasi").node, ctx);
        return ast.createMessageDescriptorFromTokens(
          tokens2,
          path.node.loc,
          ctx.stripNonEssentialProps,
          ctx.stripMessageProp
        );
      }
      if (path.isTaggedTemplateExpression()) {
        const tag = path.get("tag");
        if (tag.isCallExpression() && tag.get("arguments")[0]?.isExpression() && ast.isLinguiIdentifier(tag.get("callee").node, ast.JsMacroName.t, ctx)) {
          const i18nInstance = tag.get("arguments")[0].node;
          const tokens2 = ast.tokenizeNode(path.node, false, ctx);
          return this.replacePathWithMessage(path, tokens2, i18nInstance);
        }
      }
      if (path.isCallExpression()) {
        const callee = path.get("callee");
        if (callee.isCallExpression() && callee.get("arguments")[0]?.isExpression() && ast.isLinguiIdentifier(callee.get("callee").node, ast.JsMacroName.t, ctx)) {
          const i18nInstance = callee.node.arguments[0];
          return this.replaceTAsFunction(
            path.node,
            ctx,
            i18nInstance
          );
        }
      }
      if (path.isCallExpression() && ast.isLinguiIdentifier(path.get("callee").node, ast.JsMacroName.t, ctx)) {
        this.needsI18nImport = true;
        return this.replaceTAsFunction(path.node, ctx);
      }
      if (path.isCallExpression() && ast.isLinguiIdentifier(path.get("callee").node, ast.JsMacroName.useLingui, ctx)) {
        this.needsUseLinguiImport = true;
        return this.processUseLingui(path, ctx);
      }
      const tokens = ast.tokenizeNode(path.node, true, ctx);
      if (tokens) {
        this.needsI18nImport = true;
        return this.replacePathWithMessage(path, tokens);
      }
      return false;
    });
    /**
     * macro `t` is called with MessageDescriptor, after that
     * we create a new node to append it to i18n._
     */
    __publicField(this, "replaceTAsFunction", (node, ctx, linguiInstance) => {
      let arg = node.arguments[0];
      if (t__namespace.isObjectExpression(arg)) {
        arg = ast.processDescriptor(arg, ctx);
      }
      return this.createI18nCall(arg, linguiInstance);
    });
    this.i18nImportName = opts.i18nImportName;
    this.useLinguiImportName = opts.useLinguiImportName;
    this._ctx = ast.createMacroJsContext(
      opts.isLinguiIdentifier,
      opts.stripNonEssentialProps,
      opts.stripMessageProp
    );
  }
  /**
   * Receives reference to `useLingui()` call
   *
   * Finds every usage of { t } destructured from the call
   * and process each reference as usual `t` macro.
   *
   * const { t } = useLingui()
   * t`Message`
   *
   * ↓ ↓ ↓ ↓ ↓ ↓
   *
   * const { _: _t } = useLingui()
   * _t({id: <hash>, message: "Message"})
   */
  processUseLingui(path, ctx) {
    if (!path.parentPath.isVariableDeclarator()) {
      throw new Error(
        `\`useLingui\` macro must be used in variable declaration.

 Example:

 const { t } = useLingui()
      `
      );
    }
    const varDec = path.parentPath.node;
    if (!t__namespace.isObjectPattern(varDec.id)) {
      throw new Error(
        `You have to destructure \`t\` when using the \`useLingui\` macro, i.e:
 const { t } = useLingui()
 or
 const { t: _ } = useLingui()
 `
      );
    }
    const _property = t__namespace.isObjectPattern(varDec.id) ? varDec.id.properties.find(
      (property) => t__namespace.isObjectProperty(property) && t__namespace.isIdentifier(property.key) && t__namespace.isIdentifier(property.value) && property.key.name == "t"
    ) : null;
    const newNode = t__namespace.callExpression(t__namespace.identifier(this.useLinguiImportName), []);
    if (!_property) {
      return newNode;
    }
    const uniqTIdentifier = path.scope.generateUidIdentifier("t");
    path.scope.getBinding(_property.value.name)?.referencePaths.forEach((refPath) => {
      const currentPath = refPath.parentPath;
      const _ctx = ast.createMacroJsContext(
        ctx.isLinguiIdentifier,
        ctx.stripNonEssentialProps,
        ctx.stripMessageProp
      );
      if (currentPath.isTaggedTemplateExpression()) {
        const tokens = ast.tokenizeTemplateLiteral(currentPath.node, _ctx);
        const descriptor = ast.createMessageDescriptorFromTokens(
          tokens,
          currentPath.node.loc,
          _ctx.stripNonEssentialProps,
          _ctx.stripMessageProp
        );
        const callExpr = t__namespace.callExpression(
          t__namespace.identifier(uniqTIdentifier.name),
          [descriptor]
        );
        return currentPath.replaceWith(callExpr);
      }
      if (currentPath.isCallExpression() && currentPath.get("arguments")[0]?.isObjectExpression()) {
        const descriptor = ast.processDescriptor(
          currentPath.get("arguments")[0].node,
          _ctx
        );
        const callExpr = t__namespace.callExpression(
          t__namespace.identifier(uniqTIdentifier.name),
          [descriptor]
        );
        return currentPath.replaceWith(callExpr);
      }
      refPath.replaceWith(t__namespace.identifier(uniqTIdentifier.name));
    });
    _property.key.name = "_";
    _property.value.name = uniqTIdentifier.name;
    return t__namespace.callExpression(t__namespace.identifier(this.useLinguiImportName), []);
  }
  createI18nCall(messageDescriptor, linguiInstance) {
    return t__namespace.callExpression(
      t__namespace.memberExpression(
        linguiInstance ?? t__namespace.identifier(this.i18nImportName),
        t__namespace.identifier("_")
      ),
      messageDescriptor ? [messageDescriptor] : []
    );
  }
}

let config;
function getConfig(_config) {
  if (_config) {
    config = _config;
  }
  if (!config) {
    config = conf.getConfig();
  }
  return config;
}
function reportUnsupportedSyntax(path, e) {
  const codeFrameError = path.buildCodeFrameError(
    `Unsupported macro usage. Please check the examples at https://lingui.dev/ref/macro#examples-of-js-macros.
 If you think this is a bug, fill in an issue at https://github.com/lingui/js-lingui/issues

 Error: ${e.message}`
  );
  codeFrameError.stack = codeFrameError.message + "\n" + e.stack;
  throw codeFrameError;
}
function shouldStripMessageProp(opts) {
  if (typeof opts.stripMessageField === "boolean") {
    return opts.stripMessageField;
  }
  return process.env.NODE_ENV === "production" && !opts.extract;
}
const getIdentifierPath = (path, node) => {
  let foundPath;
  path.traverse(
    {
      Identifier: (path2) => {
        if (path2.node === node) {
          foundPath = path2;
          path2.stop();
        }
      }
    },
    path.state
  );
  return foundPath;
};
const LinguiSymbolToImportMap = {
  Trans: "jsxPackage",
  useLingui: "jsxPackage",
  i18n: "corePackage"
};
function linguiPlugin({
  types: t
}) {
  function addImport(macroImports, state, name) {
    const [path] = macroImports[LinguiSymbolToImportMap[name]];
    const config2 = state.get("linguiConfig");
    if (!state.get("has_import_" + name)) {
      state.set("has_import_" + name, true);
      const [moduleSource, importName] = config2.runtimeConfigModule[name];
      const importDecl = t.importDeclaration(
        [
          t.importSpecifier(
            getSymbolIdentifier(state, name),
            t.identifier(importName)
          )
        ],
        t.stringLiteral(moduleSource)
      );
      importDecl.loc = path.node.loc;
      const [newPath] = path.insertAfter(importDecl);
      path.parentPath.scope.registerDeclaration(newPath);
    }
    return path.parentPath.scope.getBinding(
      getSymbolIdentifier(state, name).name
    );
  }
  function getMacroImports(path) {
    const corePackage = new Set(
      path.get("body").filter(
        (statement) => statement.isImportDeclaration() && config.macro.corePackage.includes(
          statement.get("source").node.value
        )
      )
    );
    const jsxPackage = new Set(
      path.get("body").filter(
        (statement) => statement.isImportDeclaration() && config.macro.jsxPackage.includes(statement.get("source").node.value)
      )
    );
    return {
      corePackage,
      jsxPackage,
      all: /* @__PURE__ */ new Set([...corePackage, ...jsxPackage])
    };
  }
  function getSymbolIdentifier(state, name) {
    return state.get("linguiIdentifiers")[name];
  }
  function isLinguiIdentifier(path, node, macro) {
    let identPath = getIdentifierPath(path, node);
    if (macro === ast.JsMacroName.useLingui) {
      if (config.macro.jsxPackage.some(
        (moduleSource) => identPath.referencesImport(moduleSource, ast.JsMacroName.useLingui)
      )) {
        return true;
      }
    } else {
      identPath = identPath || getIdentifierPath(path.getFunctionParent(), node);
      if (config.macro.corePackage.some(
        (moduleSource) => identPath.referencesImport(moduleSource, macro)
      )) {
        return true;
      }
    }
    return false;
  }
  return {
    name: "lingui-macro-plugin",
    visitor: {
      Program: {
        enter(path, state) {
          state.set(
            "linguiConfig",
            getConfig(state.opts.linguiConfig)
          );
          const macroImports = getMacroImports(path);
          if (!macroImports.all.size) {
            return;
          }
          state.set("linguiIdentifiers", {
            i18n: path.scope.generateUidIdentifier("i18n"),
            Trans: path.scope.generateUidIdentifier("Trans"),
            useLingui: path.scope.generateUidIdentifier("useLingui")
          });
          path.traverse(
            {
              JSXElement(path2, state2) {
                const macro = new MacroJSX(
                  { types: t },
                  {
                    transImportName: getSymbolIdentifier(state2, "Trans").name,
                    stripNonEssentialProps: process.env.NODE_ENV == "production" && !state2.opts.extract,
                    stripMessageProp: shouldStripMessageProp(
                      state2.opts
                    ),
                    isLinguiIdentifier: (node, macro2) => isLinguiIdentifier(path2, node, macro2)
                  }
                );
                let newNode;
                try {
                  newNode = macro.replacePath(path2);
                } catch (e) {
                  reportUnsupportedSyntax(path2, e);
                }
                if (newNode) {
                  const [newPath] = path2.replaceWith(newNode);
                  addImport(macroImports, state2, "Trans").reference(newPath);
                }
              },
              "CallExpression|TaggedTemplateExpression"(path2, state2) {
                const macro = new MacroJs({
                  stripNonEssentialProps: process.env.NODE_ENV == "production" && !state2.opts.extract,
                  stripMessageProp: shouldStripMessageProp(
                    state2.opts
                  ),
                  i18nImportName: getSymbolIdentifier(state2, "i18n").name,
                  useLinguiImportName: getSymbolIdentifier(state2, "useLingui").name,
                  isLinguiIdentifier: (node, macro2) => isLinguiIdentifier(path2, node, macro2)
                });
                let newNode;
                try {
                  newNode = macro.replacePath(path2);
                } catch (e) {
                  reportUnsupportedSyntax(path2, e);
                }
                if (newNode) {
                  const [newPath] = path2.replaceWith(newNode);
                  if (macro.needsUseLinguiImport) {
                    addImport(macroImports, state2, "useLingui").reference(
                      newPath
                    );
                    path2.scope.crawl();
                  }
                  if (macro.needsI18nImport) {
                    addImport(macroImports, state2, "i18n").reference(newPath);
                  }
                }
              }
            },
            state
          );
        },
        exit(path, state) {
          const macroImports = getMacroImports(path);
          macroImports.all.forEach((path2) => path2.remove());
        }
      }
    }
  };
}

module.exports = linguiPlugin;
