import MagicString from 'magic-string';
import { c as cleanUrl } from './chunk-utils.js';
import { walk } from 'estree-walker';
import { createFilter } from 'vite';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path/posix';
import { a as ManualMockedModule, M as MockerRegistry } from './chunk-registry.js';
import { fileURLToPath } from 'node:url';
import { existsSync, readFileSync } from 'node:fs';
import { findMockRedirect } from './redirect.js';
import { i as isAbsolute, j as join$1, r as resolve } from './chunk-pathe.ff20891b.js';
import 'node:module';

const isNodeInPatternWeakSet = /* @__PURE__ */ new WeakSet();
function setIsNodeInPattern(node) {
  return isNodeInPatternWeakSet.add(node);
}
function isNodeInPattern(node) {
  return isNodeInPatternWeakSet.has(node);
}
function esmWalker(root, { onIdentifier, onImportMeta, onDynamicImport, onCallExpression }) {
  const parentStack = [];
  const varKindStack = [];
  const scopeMap = /* @__PURE__ */ new WeakMap();
  const identifiers = [];
  const setScope = (node, name) => {
    let scopeIds = scopeMap.get(node);
    if (scopeIds && scopeIds.has(name)) {
      return;
    }
    if (!scopeIds) {
      scopeIds = /* @__PURE__ */ new Set();
      scopeMap.set(node, scopeIds);
    }
    scopeIds.add(name);
  };
  function isInScope(name, parents) {
    return parents.some((node) => {
      var _a;
      return node && ((_a = scopeMap.get(node)) == null ? void 0 : _a.has(name));
    });
  }
  function handlePattern(p, parentScope) {
    if (p.type === "Identifier") {
      setScope(parentScope, p.name);
    } else if (p.type === "RestElement") {
      handlePattern(p.argument, parentScope);
    } else if (p.type === "ObjectPattern") {
      p.properties.forEach((property) => {
        if (property.type === "RestElement") {
          setScope(parentScope, property.argument.name);
        } else {
          handlePattern(property.value, parentScope);
        }
      });
    } else if (p.type === "ArrayPattern") {
      p.elements.forEach((element) => {
        if (element) {
          handlePattern(element, parentScope);
        }
      });
    } else if (p.type === "AssignmentPattern") {
      handlePattern(p.left, parentScope);
    } else {
      setScope(parentScope, p.name);
    }
  }
  walk(root, {
    enter(node, parent) {
      if (node.type === "ImportDeclaration") {
        return this.skip();
      }
      if (parent && !(parent.type === "IfStatement" && node === parent.alternate)) {
        parentStack.unshift(parent);
      }
      if (node.type === "VariableDeclaration") {
        varKindStack.unshift(node.kind);
      }
      if (node.type === "CallExpression") {
        onCallExpression == null ? void 0 : onCallExpression(node);
      }
      if (node.type === "MetaProperty" && node.meta.name === "import") {
        onImportMeta == null ? void 0 : onImportMeta(node);
      } else if (node.type === "ImportExpression") {
        onDynamicImport == null ? void 0 : onDynamicImport(node);
      }
      if (node.type === "Identifier") {
        if (!isInScope(node.name, parentStack) && isRefIdentifier(node, parent, parentStack)) {
          identifiers.push([node, parentStack.slice(0)]);
        }
      } else if (isFunctionNode(node)) {
        if (node.type === "FunctionDeclaration") {
          const parentScope = findParentScope(parentStack);
          if (parentScope) {
            setScope(parentScope, node.id.name);
          }
        }
        node.params.forEach((p) => {
          if (p.type === "ObjectPattern" || p.type === "ArrayPattern") {
            handlePattern(p, node);
            return;
          }
          walk(p.type === "AssignmentPattern" ? p.left : p, {
            enter(child, parent2) {
              if ((parent2 == null ? void 0 : parent2.type) === "AssignmentPattern" && (parent2 == null ? void 0 : parent2.right) === child) {
                return this.skip();
              }
              if (child.type !== "Identifier") {
                return;
              }
              if (isStaticPropertyKey(child, parent2)) {
                return;
              }
              if ((parent2 == null ? void 0 : parent2.type) === "TemplateLiteral" && (parent2 == null ? void 0 : parent2.expressions.includes(child)) || (parent2 == null ? void 0 : parent2.type) === "CallExpression" && (parent2 == null ? void 0 : parent2.callee) === child) {
                return;
              }
              setScope(node, child.name);
            }
          });
        });
      } else if (node.type === "Property" && parent.type === "ObjectPattern") {
        setIsNodeInPattern(node);
      } else if (node.type === "VariableDeclarator") {
        const parentFunction = findParentScope(
          parentStack,
          varKindStack[0] === "var"
        );
        if (parentFunction) {
          handlePattern(node.id, parentFunction);
        }
      } else if (node.type === "CatchClause" && node.param) {
        handlePattern(node.param, node);
      }
    },
    leave(node, parent) {
      if (parent && !(parent.type === "IfStatement" && node === parent.alternate)) {
        parentStack.shift();
      }
      if (node.type === "VariableDeclaration") {
        varKindStack.shift();
      }
    }
  });
  identifiers.forEach(([node, stack]) => {
    if (!isInScope(node.name, stack)) {
      const parent = stack[0];
      const grandparent = stack[1];
      const hasBindingShortcut = isStaticProperty(parent) && parent.shorthand && (!isNodeInPattern(parent) || isInDestructuringAssignment(parent, parentStack));
      const classDeclaration = parent.type === "PropertyDefinition" && (grandparent == null ? void 0 : grandparent.type) === "ClassBody" || parent.type === "ClassDeclaration" && node === parent.superClass;
      const classExpression = parent.type === "ClassExpression" && node === parent.id;
      onIdentifier == null ? void 0 : onIdentifier(
        node,
        {
          hasBindingShortcut,
          classDeclaration,
          classExpression
        },
        stack
      );
    }
  });
}
function isRefIdentifier(id, parent, parentStack) {
  if (parent.type === "CatchClause" || (parent.type === "VariableDeclarator" || parent.type === "ClassDeclaration") && parent.id === id) {
    return false;
  }
  if (isFunctionNode(parent)) {
    if (parent.id === id) {
      return false;
    }
    if (parent.params.includes(id)) {
      return false;
    }
  }
  if (parent.type === "MethodDefinition" && !parent.computed) {
    return false;
  }
  if (isStaticPropertyKey(id, parent)) {
    return false;
  }
  if (isNodeInPattern(parent) && parent.value === id) {
    return false;
  }
  if (parent.type === "ArrayPattern" && !isInDestructuringAssignment(parent, parentStack)) {
    return false;
  }
  if (parent.type === "MemberExpression" && parent.property === id && !parent.computed) {
    return false;
  }
  if (parent.type === "ExportSpecifier") {
    return false;
  }
  if (id.name === "arguments") {
    return false;
  }
  return true;
}
function isStaticProperty(node) {
  return node && node.type === "Property" && !node.computed;
}
function isStaticPropertyKey(node, parent) {
  return isStaticProperty(parent) && parent.key === node;
}
const functionNodeTypeRE = /Function(?:Expression|Declaration)$|Method$/;
function isFunctionNode(node) {
  return functionNodeTypeRE.test(node.type);
}
const blockNodeTypeRE = /^BlockStatement$|^For(?:In|Of)?Statement$/;
function isBlock(node) {
  return blockNodeTypeRE.test(node.type);
}
function findParentScope(parentStack, isVar = false) {
  return parentStack.find(isVar ? isFunctionNode : isBlock);
}
function isInDestructuringAssignment(parent, parentStack) {
  if (parent && (parent.type === "Property" || parent.type === "ArrayPattern")) {
    return parentStack.some((i) => i.type === "AssignmentExpression");
  }
  return false;
}
function getArbitraryModuleIdentifier(node) {
  return node.type === "Identifier" ? node.name : node.raw;
}

function automockPlugin(options = {}) {
  return {
    name: "vitest:automock",
    enforce: "post",
    transform(code, id) {
      if (id.includes("mock=automock") || id.includes("mock=autospy")) {
        const mockType = id.includes("mock=automock") ? "automock" : "autospy";
        const ms = automockModule(code, mockType, this.parse, options);
        return {
          code: ms.toString(),
          map: ms.generateMap({ hires: "boundary", source: cleanUrl(id) })
        };
      }
    }
  };
}
function automockModule(code, mockType, parse, options = {}) {
  const globalThisAccessor = options.globalThisAccessor || '"__vitest_mocker__"';
  const ast = parse(code);
  const m = new MagicString(code);
  const allSpecifiers = [];
  let importIndex = 0;
  for (const _node of ast.body) {
    if (_node.type === "ExportAllDeclaration") {
      throw new Error(
        `automocking files with \`export *\` is not supported in browser mode because it cannot be statically analysed`
      );
    }
    if (_node.type === "ExportNamedDeclaration") {
      let traversePattern2 = function(expression) {
        if (expression.type === "Identifier") {
          allSpecifiers.push({ name: expression.name });
        } else if (expression.type === "ArrayPattern") {
          expression.elements.forEach((element) => {
            if (!element) {
              return;
            }
            traversePattern2(element);
          });
        } else if (expression.type === "ObjectPattern") {
          expression.properties.forEach((property) => {
            if (property.type === "RestElement") {
              traversePattern2(property);
            } else if (property.type === "Property") {
              traversePattern2(property.value);
            } else ;
          });
        } else if (expression.type === "RestElement") {
          traversePattern2(expression.argument);
        } else if (expression.type === "AssignmentPattern") {
          throw new Error(
            `AssignmentPattern is not supported. Please open a new bug report.`
          );
        } else if (expression.type === "MemberExpression") {
          throw new Error(
            `MemberExpression is not supported. Please open a new bug report.`
          );
        } else ;
      };
      const node = _node;
      const declaration = node.declaration;
      if (declaration) {
        if (declaration.type === "FunctionDeclaration") {
          allSpecifiers.push({ name: declaration.id.name });
        } else if (declaration.type === "VariableDeclaration") {
          declaration.declarations.forEach((declaration2) => {
            traversePattern2(declaration2.id);
          });
        } else if (declaration.type === "ClassDeclaration") {
          allSpecifiers.push({ name: declaration.id.name });
        } else ;
        m.remove(node.start, declaration.start);
      }
      const specifiers = node.specifiers || [];
      const source = node.source;
      if (!source && specifiers.length) {
        specifiers.forEach((specifier) => {
          allSpecifiers.push({
            alias: getArbitraryModuleIdentifier(specifier.exported),
            name: getArbitraryModuleIdentifier(specifier.local)
          });
        });
        m.remove(node.start, node.end);
      } else if (source && specifiers.length) {
        const importNames = [];
        specifiers.forEach((specifier) => {
          const importedName = `__vitest_imported_${importIndex++}__`;
          importNames.push([getArbitraryModuleIdentifier(specifier.local), importedName]);
          allSpecifiers.push({
            name: importedName,
            alias: getArbitraryModuleIdentifier(specifier.exported)
          });
        });
        const importString = `import { ${importNames.map(([name, alias]) => `${name} as ${alias}`).join(", ")} } from '${source.value}'`;
        m.overwrite(node.start, node.end, importString);
      }
    }
    if (_node.type === "ExportDefaultDeclaration") {
      const node = _node;
      const declaration = node.declaration;
      allSpecifiers.push({ name: "__vitest_default", alias: "default" });
      m.overwrite(node.start, declaration.start, `const __vitest_default = `);
    }
  }
  const moduleObject = `
const __vitest_current_es_module__ = {
  __esModule: true,
  ${allSpecifiers.map(({ name }) => `["${name}"]: ${name},`).join("\n  ")}
}
const __vitest_mocked_module__ = globalThis[${globalThisAccessor}].mockObject(__vitest_current_es_module__, "${mockType}")
`;
  const assigning = allSpecifiers.map(({ name }, index) => {
    return `const __vitest_mocked_${index}__ = __vitest_mocked_module__["${name}"]`;
  }).join("\n");
  const redeclarations = allSpecifiers.map(({ name, alias }, index) => {
    return `  __vitest_mocked_${index}__ as ${alias || name},`;
  }).join("\n");
  const specifiersExports = `
export {
${redeclarations}
}
`;
  m.append(moduleObject + assigning + specifiersExports);
  return m;
}

const regexDynamicImport = /import\s*\(/;
function dynamicImportPlugin(options = {}) {
  return {
    name: "vitest:browser:esm-injector",
    enforce: "post",
    transform(source, id) {
      if (!regexDynamicImport.test(source)) {
        return;
      }
      if (options.filter && !options.filter(id)) {
        return;
      }
      return injectDynamicImport(source, id, this.parse, options);
    }
  };
}
function injectDynamicImport(code, id, parse, options = {}) {
  const s = new MagicString(code);
  let ast;
  try {
    ast = parse(code);
  } catch (err) {
    console.error(`Cannot parse ${id}:
${err.message}`);
    return;
  }
  esmWalker(ast, {
    // TODO: make env updatable
    onImportMeta() {
    },
    onDynamicImport(node) {
      const globalThisAccessor = options.globalThisAccessor || '"__vitest_mocker__"';
      const replaceString = `globalThis[${globalThisAccessor}].wrapDynamicImport(() => import(`;
      const importSubstring = code.substring(node.start, node.end);
      const hasIgnore = importSubstring.includes("/* @vite-ignore */");
      s.overwrite(
        node.start,
        node.source.start,
        replaceString + (hasIgnore ? "/* @vite-ignore */ " : "")
      );
      s.overwrite(node.end - 1, node.end, "))");
    }
  });
  return {
    ast,
    code: s.toString(),
    map: s.generateMap({ hires: "boundary", source: id })
  };
}

// AST walker module for ESTree compatible trees


function makeTest(test) {
  if (typeof test === "string")
    { return function (type) { return type === test; } }
  else if (!test)
    { return function () { return true; } }
  else
    { return test }
}

var Found = function Found(node, state) { this.node = node; this.state = state; };

// Find the innermost node of a given type that contains the given
// position. Interface similar to findNodeAt.
function findNodeAround(node, pos, test, baseVisitor, state) {
  test = makeTest(test);
  if (!baseVisitor) { baseVisitor = base; }
  try {
    (function c(node, st, override) {
      var type = override || node.type;
      if (node.start > pos || node.end < pos) { return }
      baseVisitor[type](node, st, c);
      if (test(type, node)) { throw new Found(node, st) }
    })(node, state);
  } catch (e) {
    if (e instanceof Found) { return e }
    throw e
  }
}

function skipThrough(node, st, c) { c(node, st); }
function ignore(_node, _st, _c) {}

// Node walkers.

var base = {};

base.Program = base.BlockStatement = base.StaticBlock = function (node, st, c) {
  for (var i = 0, list = node.body; i < list.length; i += 1)
    {
    var stmt = list[i];

    c(stmt, st, "Statement");
  }
};
base.Statement = skipThrough;
base.EmptyStatement = ignore;
base.ExpressionStatement = base.ParenthesizedExpression = base.ChainExpression =
  function (node, st, c) { return c(node.expression, st, "Expression"); };
base.IfStatement = function (node, st, c) {
  c(node.test, st, "Expression");
  c(node.consequent, st, "Statement");
  if (node.alternate) { c(node.alternate, st, "Statement"); }
};
base.LabeledStatement = function (node, st, c) { return c(node.body, st, "Statement"); };
base.BreakStatement = base.ContinueStatement = ignore;
base.WithStatement = function (node, st, c) {
  c(node.object, st, "Expression");
  c(node.body, st, "Statement");
};
base.SwitchStatement = function (node, st, c) {
  c(node.discriminant, st, "Expression");
  for (var i = 0, list = node.cases; i < list.length; i += 1) {
    var cs = list[i];

    c(cs, st);
  }
};
base.SwitchCase = function (node, st, c) {
  if (node.test) { c(node.test, st, "Expression"); }
  for (var i = 0, list = node.consequent; i < list.length; i += 1)
    {
    var cons = list[i];

    c(cons, st, "Statement");
  }
};
base.ReturnStatement = base.YieldExpression = base.AwaitExpression = function (node, st, c) {
  if (node.argument) { c(node.argument, st, "Expression"); }
};
base.ThrowStatement = base.SpreadElement =
  function (node, st, c) { return c(node.argument, st, "Expression"); };
base.TryStatement = function (node, st, c) {
  c(node.block, st, "Statement");
  if (node.handler) { c(node.handler, st); }
  if (node.finalizer) { c(node.finalizer, st, "Statement"); }
};
base.CatchClause = function (node, st, c) {
  if (node.param) { c(node.param, st, "Pattern"); }
  c(node.body, st, "Statement");
};
base.WhileStatement = base.DoWhileStatement = function (node, st, c) {
  c(node.test, st, "Expression");
  c(node.body, st, "Statement");
};
base.ForStatement = function (node, st, c) {
  if (node.init) { c(node.init, st, "ForInit"); }
  if (node.test) { c(node.test, st, "Expression"); }
  if (node.update) { c(node.update, st, "Expression"); }
  c(node.body, st, "Statement");
};
base.ForInStatement = base.ForOfStatement = function (node, st, c) {
  c(node.left, st, "ForInit");
  c(node.right, st, "Expression");
  c(node.body, st, "Statement");
};
base.ForInit = function (node, st, c) {
  if (node.type === "VariableDeclaration") { c(node, st); }
  else { c(node, st, "Expression"); }
};
base.DebuggerStatement = ignore;

base.FunctionDeclaration = function (node, st, c) { return c(node, st, "Function"); };
base.VariableDeclaration = function (node, st, c) {
  for (var i = 0, list = node.declarations; i < list.length; i += 1)
    {
    var decl = list[i];

    c(decl, st);
  }
};
base.VariableDeclarator = function (node, st, c) {
  c(node.id, st, "Pattern");
  if (node.init) { c(node.init, st, "Expression"); }
};

base.Function = function (node, st, c) {
  if (node.id) { c(node.id, st, "Pattern"); }
  for (var i = 0, list = node.params; i < list.length; i += 1)
    {
    var param = list[i];

    c(param, st, "Pattern");
  }
  c(node.body, st, node.expression ? "Expression" : "Statement");
};

base.Pattern = function (node, st, c) {
  if (node.type === "Identifier")
    { c(node, st, "VariablePattern"); }
  else if (node.type === "MemberExpression")
    { c(node, st, "MemberPattern"); }
  else
    { c(node, st); }
};
base.VariablePattern = ignore;
base.MemberPattern = skipThrough;
base.RestElement = function (node, st, c) { return c(node.argument, st, "Pattern"); };
base.ArrayPattern = function (node, st, c) {
  for (var i = 0, list = node.elements; i < list.length; i += 1) {
    var elt = list[i];

    if (elt) { c(elt, st, "Pattern"); }
  }
};
base.ObjectPattern = function (node, st, c) {
  for (var i = 0, list = node.properties; i < list.length; i += 1) {
    var prop = list[i];

    if (prop.type === "Property") {
      if (prop.computed) { c(prop.key, st, "Expression"); }
      c(prop.value, st, "Pattern");
    } else if (prop.type === "RestElement") {
      c(prop.argument, st, "Pattern");
    }
  }
};

base.Expression = skipThrough;
base.ThisExpression = base.Super = base.MetaProperty = ignore;
base.ArrayExpression = function (node, st, c) {
  for (var i = 0, list = node.elements; i < list.length; i += 1) {
    var elt = list[i];

    if (elt) { c(elt, st, "Expression"); }
  }
};
base.ObjectExpression = function (node, st, c) {
  for (var i = 0, list = node.properties; i < list.length; i += 1)
    {
    var prop = list[i];

    c(prop, st);
  }
};
base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
base.SequenceExpression = function (node, st, c) {
  for (var i = 0, list = node.expressions; i < list.length; i += 1)
    {
    var expr = list[i];

    c(expr, st, "Expression");
  }
};
base.TemplateLiteral = function (node, st, c) {
  for (var i = 0, list = node.quasis; i < list.length; i += 1)
    {
    var quasi = list[i];

    c(quasi, st);
  }

  for (var i$1 = 0, list$1 = node.expressions; i$1 < list$1.length; i$1 += 1)
    {
    var expr = list$1[i$1];

    c(expr, st, "Expression");
  }
};
base.TemplateElement = ignore;
base.UnaryExpression = base.UpdateExpression = function (node, st, c) {
  c(node.argument, st, "Expression");
};
base.BinaryExpression = base.LogicalExpression = function (node, st, c) {
  c(node.left, st, "Expression");
  c(node.right, st, "Expression");
};
base.AssignmentExpression = base.AssignmentPattern = function (node, st, c) {
  c(node.left, st, "Pattern");
  c(node.right, st, "Expression");
};
base.ConditionalExpression = function (node, st, c) {
  c(node.test, st, "Expression");
  c(node.consequent, st, "Expression");
  c(node.alternate, st, "Expression");
};
base.NewExpression = base.CallExpression = function (node, st, c) {
  c(node.callee, st, "Expression");
  if (node.arguments)
    { for (var i = 0, list = node.arguments; i < list.length; i += 1)
      {
        var arg = list[i];

        c(arg, st, "Expression");
      } }
};
base.MemberExpression = function (node, st, c) {
  c(node.object, st, "Expression");
  if (node.computed) { c(node.property, st, "Expression"); }
};
base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) {
  if (node.declaration)
    { c(node.declaration, st, node.type === "ExportNamedDeclaration" || node.declaration.id ? "Statement" : "Expression"); }
  if (node.source) { c(node.source, st, "Expression"); }
};
base.ExportAllDeclaration = function (node, st, c) {
  if (node.exported)
    { c(node.exported, st); }
  c(node.source, st, "Expression");
};
base.ImportDeclaration = function (node, st, c) {
  for (var i = 0, list = node.specifiers; i < list.length; i += 1)
    {
    var spec = list[i];

    c(spec, st);
  }
  c(node.source, st, "Expression");
};
base.ImportExpression = function (node, st, c) {
  c(node.source, st, "Expression");
};
base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.PrivateIdentifier = base.Literal = ignore;

base.TaggedTemplateExpression = function (node, st, c) {
  c(node.tag, st, "Expression");
  c(node.quasi, st, "Expression");
};
base.ClassDeclaration = base.ClassExpression = function (node, st, c) { return c(node, st, "Class"); };
base.Class = function (node, st, c) {
  if (node.id) { c(node.id, st, "Pattern"); }
  if (node.superClass) { c(node.superClass, st, "Expression"); }
  c(node.body, st);
};
base.ClassBody = function (node, st, c) {
  for (var i = 0, list = node.body; i < list.length; i += 1)
    {
    var elt = list[i];

    c(elt, st);
  }
};
base.MethodDefinition = base.PropertyDefinition = base.Property = function (node, st, c) {
  if (node.computed) { c(node.key, st, "Expression"); }
  if (node.value) { c(node.value, st, "Expression"); }
};

function hoistMocksPlugin(options = {}) {
  const filter = options.filter || createFilter(options.include, options.exclude);
  const {
    hoistableMockMethodNames = ["mock", "unmock"],
    dynamicImportMockMethodNames = ["mock", "unmock", "doMock", "doUnmock"],
    hoistedMethodNames = ["hoisted"],
    utilsObjectNames = ["vi", "vitest"]
  } = options;
  const methods = /* @__PURE__ */ new Set([
    ...hoistableMockMethodNames,
    ...hoistedMethodNames,
    ...dynamicImportMockMethodNames
  ]);
  const regexpHoistable2 = new RegExp(
    `\\b(?:${utilsObjectNames.join("|")})\\s*.\\s*(?:${Array.from(methods).join("|")})\\(`
  );
  return {
    name: "vitest:mocks",
    enforce: "post",
    transform(code, id) {
      if (!filter(id)) {
        return;
      }
      return hoistMocks(code, id, this.parse, {
        regexpHoistable: regexpHoistable2,
        hoistableMockMethodNames,
        hoistedMethodNames,
        utilsObjectNames,
        dynamicImportMockMethodNames,
        ...options
      });
    }
  };
}
const API_NOT_FOUND_ERROR = `There are some problems in resolving the mocks API.
You may encounter this issue when importing the mocks API from another module other than 'vitest'.
To fix this issue you can either:
- import the mocks API directly from 'vitest'
- enable the 'globals' options`;
function API_NOT_FOUND_CHECK(names) {
  return `
if (${names.map((name) => `typeof globalThis["${name}"] === "undefined"`).join(" && ")}) { throw new Error(${JSON.stringify(API_NOT_FOUND_ERROR)}) }
`;
}
function isIdentifier(node) {
  return node.type === "Identifier";
}
function getBetterEnd(code, node) {
  let end = node.end;
  if (code[node.end] === ";") {
    end += 1;
  }
  if (code[node.end + 1] === "\n") {
    end += 1;
  }
  return end;
}
const regexpHoistable = /\b(?:vi|vitest)\s*\.\s*(?:mock|unmock|hoisted|doMock|doUnmock)\(/;
const hashbangRE = /^#!.*\n/;
function hoistMocks(code, id, parse, options = {}) {
  var _a;
  const needHoisting = (options.regexpHoistable || regexpHoistable).test(code);
  if (!needHoisting) {
    return;
  }
  const s = new MagicString(code);
  let ast;
  try {
    ast = parse(code);
  } catch (err) {
    console.error(`Cannot parse ${id}:
${err.message}.`);
    return;
  }
  const {
    hoistableMockMethodNames = ["mock", "unmock"],
    dynamicImportMockMethodNames = ["mock", "unmock", "doMock", "doUnmock"],
    hoistedMethodNames = ["hoisted"],
    utilsObjectNames = ["vi", "vitest"],
    hoistedModules = ["vitest"]
  } = options;
  const hoistIndex = ((_a = code.match(hashbangRE)) == null ? void 0 : _a[0].length) ?? 0;
  let hoistedModuleImported = false;
  let uid = 0;
  const idToImportMap = /* @__PURE__ */ new Map();
  const transformImportDeclaration = (node) => {
    const source = node.source.value;
    const importId = `__vi_import_${uid++}__`;
    const hasSpecifiers = node.specifiers.length > 0;
    const code2 = hasSpecifiers ? `const ${importId} = await import('${source}')
` : `await import('${source}')
`;
    return {
      code: code2,
      id: importId
    };
  };
  function defineImport(node) {
    if (hoistedModules.includes(node.source.value)) {
      hoistedModuleImported = true;
      return;
    }
    const declaration = transformImportDeclaration(node);
    if (!declaration) {
      return null;
    }
    s.appendLeft(hoistIndex, declaration.code);
    return declaration.id;
  }
  for (const node of ast.body) {
    if (node.type === "ImportDeclaration") {
      const importId = defineImport(node);
      if (!importId) {
        continue;
      }
      s.remove(node.start, getBetterEnd(code, node));
      for (const spec of node.specifiers) {
        if (spec.type === "ImportSpecifier") {
          idToImportMap.set(
            spec.local.name,
            `${importId}.${getArbitraryModuleIdentifier(spec.imported)}`
          );
        } else if (spec.type === "ImportDefaultSpecifier") {
          idToImportMap.set(spec.local.name, `${importId}.default`);
        } else {
          idToImportMap.set(spec.local.name, importId);
        }
      }
    }
  }
  const declaredConst = /* @__PURE__ */ new Set();
  const hoistedNodes = [];
  function createSyntaxError(node, message) {
    const _error = new SyntaxError(message);
    Error.captureStackTrace(_error, createSyntaxError);
    const serializedError = {
      name: "SyntaxError",
      message: _error.message,
      stack: _error.stack
    };
    if (options.codeFrameGenerator) {
      serializedError.frame = options.codeFrameGenerator(node, id, code);
    }
    return serializedError;
  }
  function assertNotDefaultExport(node, error) {
    var _a2;
    const defaultExport = (_a2 = findNodeAround(
      ast,
      node.start,
      "ExportDefaultDeclaration"
    )) == null ? void 0 : _a2.node;
    if ((defaultExport == null ? void 0 : defaultExport.declaration) === node || (defaultExport == null ? void 0 : defaultExport.declaration.type) === "AwaitExpression" && defaultExport.declaration.argument === node) {
      throw createSyntaxError(defaultExport, error);
    }
  }
  function assertNotNamedExport(node, error) {
    var _a2;
    const nodeExported = (_a2 = findNodeAround(
      ast,
      node.start,
      "ExportNamedDeclaration"
    )) == null ? void 0 : _a2.node;
    if ((nodeExported == null ? void 0 : nodeExported.declaration) === node) {
      throw createSyntaxError(nodeExported, error);
    }
  }
  function getVariableDeclaration(node) {
    var _a2, _b;
    const declarationNode = (_a2 = findNodeAround(
      ast,
      node.start,
      "VariableDeclaration"
    )) == null ? void 0 : _a2.node;
    const init = (_b = declarationNode == null ? void 0 : declarationNode.declarations[0]) == null ? void 0 : _b.init;
    if (init && (init === node || init.type === "AwaitExpression" && init.argument === node)) {
      return declarationNode;
    }
  }
  esmWalker(ast, {
    onIdentifier(id2, info, parentStack) {
      const binding = idToImportMap.get(id2.name);
      if (!binding) {
        return;
      }
      if (info.hasBindingShortcut) {
        s.appendLeft(id2.end, `: ${binding}`);
      } else if (info.classDeclaration) {
        if (!declaredConst.has(id2.name)) {
          declaredConst.add(id2.name);
          const topNode = parentStack[parentStack.length - 2];
          s.prependRight(topNode.start, `const ${id2.name} = ${binding};
`);
        }
      } else if (
        // don't transform class name identifier
        !info.classExpression
      ) {
        s.update(id2.start, id2.end, binding);
      }
    },
    onCallExpression(node) {
      var _a2;
      if (node.callee.type === "MemberExpression" && isIdentifier(node.callee.object) && utilsObjectNames.includes(node.callee.object.name) && isIdentifier(node.callee.property)) {
        const methodName = node.callee.property.name;
        if (hoistableMockMethodNames.includes(methodName)) {
          const method = `${node.callee.object.name}.${methodName}`;
          assertNotDefaultExport(
            node,
            `Cannot export the result of "${method}". Remove export declaration because "${method}" doesn't return anything.`
          );
          const declarationNode = getVariableDeclaration(node);
          if (declarationNode) {
            assertNotNamedExport(
              declarationNode,
              `Cannot export the result of "${method}". Remove export declaration because "${method}" doesn't return anything.`
            );
          }
          hoistedNodes.push(node);
        } else if (dynamicImportMockMethodNames.includes(methodName)) {
          const moduleInfo = node.arguments[0];
          let source = null;
          if (moduleInfo.type === "ImportExpression") {
            source = moduleInfo.source;
          }
          if (moduleInfo.type === "AwaitExpression" && moduleInfo.argument.type === "ImportExpression") {
            source = moduleInfo.argument.source;
          }
          if (source) {
            s.overwrite(
              moduleInfo.start,
              moduleInfo.end,
              s.slice(source.start, source.end)
            );
          }
        }
        if (hoistedMethodNames.includes(methodName)) {
          assertNotDefaultExport(
            node,
            "Cannot export hoisted variable. You can control hoisting behavior by placing the import from this file first."
          );
          const declarationNode = getVariableDeclaration(node);
          if (declarationNode) {
            assertNotNamedExport(
              declarationNode,
              "Cannot export hoisted variable. You can control hoisting behavior by placing the import from this file first."
            );
            hoistedNodes.push(declarationNode);
          } else {
            const awaitedExpression = (_a2 = findNodeAround(
              ast,
              node.start,
              "AwaitExpression"
            )) == null ? void 0 : _a2.node;
            hoistedNodes.push(
              (awaitedExpression == null ? void 0 : awaitedExpression.argument) === node ? awaitedExpression : node
            );
          }
        }
      }
    }
  });
  function getNodeName(node) {
    const callee = node.callee || {};
    if (callee.type === "MemberExpression" && isIdentifier(callee.property) && isIdentifier(callee.object)) {
      return `${callee.object.name}.${callee.property.name}()`;
    }
    return '"hoisted method"';
  }
  function getNodeCall(node) {
    if (node.type === "CallExpression") {
      return node;
    }
    if (node.type === "VariableDeclaration") {
      const { declarations } = node;
      const init = declarations[0].init;
      if (init) {
        return getNodeCall(init);
      }
    }
    if (node.type === "AwaitExpression") {
      const { argument } = node;
      if (argument.type === "CallExpression") {
        return getNodeCall(argument);
      }
    }
    return node;
  }
  function createError(outsideNode, insideNode) {
    const outsideCall = getNodeCall(outsideNode);
    const insideCall = getNodeCall(insideNode);
    throw createSyntaxError(
      insideCall,
      `Cannot call ${getNodeName(insideCall)} inside ${getNodeName(
        outsideCall
      )}: both methods are hoisted to the top of the file and not actually called inside each other.`
    );
  }
  function rewriteMockDynamicImport(nodeCode, moduleInfo, expressionStart, expressionEnd, mockStart) {
    const source = moduleInfo.source;
    const importPath = s.slice(source.start, source.end);
    const nodeCodeStart = expressionStart - mockStart;
    const nodeCodeEnd = expressionEnd - mockStart;
    return nodeCode.slice(0, nodeCodeStart) + importPath + nodeCode.slice(nodeCodeEnd);
  }
  for (let i = 0; i < hoistedNodes.length; i++) {
    const node = hoistedNodes[i];
    for (let j = i + 1; j < hoistedNodes.length; j++) {
      const otherNode = hoistedNodes[j];
      if (node.start >= otherNode.start && node.end <= otherNode.end) {
        throw createError(otherNode, node);
      }
      if (otherNode.start >= node.start && otherNode.end <= node.end) {
        throw createError(node, otherNode);
      }
    }
  }
  const hoistedCode = hoistedNodes.map((node) => {
    const end = getBetterEnd(code, node);
    let nodeCode = s.slice(node.start, end);
    if (node.type === "CallExpression" && node.callee.type === "MemberExpression" && dynamicImportMockMethodNames.includes(node.callee.property.name)) {
      const moduleInfo = node.arguments[0];
      if (moduleInfo.type === "ImportExpression") {
        nodeCode = rewriteMockDynamicImport(
          nodeCode,
          moduleInfo,
          moduleInfo.start,
          moduleInfo.end,
          node.start
        );
      }
      if (moduleInfo.type === "AwaitExpression" && moduleInfo.argument.type === "ImportExpression") {
        nodeCode = rewriteMockDynamicImport(
          nodeCode,
          moduleInfo.argument,
          moduleInfo.start,
          moduleInfo.end,
          node.start
        );
      }
    }
    s.remove(node.start, end);
    return `${nodeCode}${nodeCode.endsWith("\n") ? "" : "\n"}`;
  }).join("");
  if (hoistedCode || hoistedModuleImported) {
    s.prepend(
      (!hoistedModuleImported && hoistedCode ? API_NOT_FOUND_CHECK(utilsObjectNames) : "") + hoistedCode
    );
  }
  return {
    ast,
    code: s.toString(),
    map: s.generateMap({ hires: "boundary", source: id })
  };
}

function interceptorPlugin(options) {
  const registry = new MockerRegistry();
  return {
    name: "vitest:mocks:interceptor",
    enforce: "pre",
    async load(id) {
      const mock = registry.get(id);
      if (!mock) {
        return;
      }
      if (mock.type === "manual") {
        const exports = Object.keys(await mock.resolve());
        const accessor = options.globalThisAccessor || '"__vitest_mocker__"';
        const serverUrl = mock.serverUrl;
        const module = `const module = globalThis[${accessor}].getFactoryModule("${serverUrl}");`;
        const keys = exports.map((name) => {
          if (name === "default") {
            return `export default module["default"];`;
          }
          return `export const ${name} = module["${name}"];`;
        }).join("\n");
        return `${module}
${keys}`;
      }
      if (mock.type === "redirect") {
        return readFile(mock.redirect, "utf-8");
      }
    },
    transform: {
      order: "post",
      handler(code, id) {
        const mock = registry.get(id);
        if (!mock) {
          return;
        }
        if (mock.type === "automock" || mock.type === "autospy") {
          const m = automockModule(code, mock.type, this.parse, {
            globalThisAccessor: options.globalThisAccessor
          });
          return {
            code: m.toString(),
            map: m.generateMap({ hires: "boundary", source: cleanUrl(id) })
          };
        }
      }
    },
    configureServer(server) {
      server.ws.on("vitest:interceptor:register", (event) => {
        const serverUrl = event.url;
        event.url = join(server.config.root, event.url);
        if (event.type === "manual") {
          const module = ManualMockedModule.fromJSON(event, async () => {
            const keys = await getFactoryExports(serverUrl);
            return Object.fromEntries(keys.map((key) => [key, null]));
          });
          Object.assign(module, {
            serverUrl
          });
          registry.add(module);
        } else {
          if (event.type === "redirect") {
            const redirectUrl = new URL(event.redirect);
            event.redirect = join(server.config.root, redirectUrl.pathname);
          }
          registry.register(event);
        }
        server.ws.send("vitest:interceptor:register:result");
      });
      server.ws.on("vitest:interceptor:delete", (id) => {
        registry.delete(id);
        server.ws.send("vitest:interceptor:register:delete");
      });
      server.ws.on("vitest:interceptor:invalidate", () => {
        registry.clear();
        server.ws.send("vitest:interceptor:register:invalidate");
      });
      function getFactoryExports(url) {
        server.ws.send("vitest:interceptor:resolve", url);
        let timeout;
        return new Promise((resolve, reject) => {
          timeout = setTimeout(() => {
            reject(new Error(`Timeout while waiting for factory exports of ${url}`));
          }, 1e4);
          server.ws.on("vitest:interceptor:resolved", ({ url: resolvedUrl, keys }) => {
            if (resolvedUrl === url) {
              clearTimeout(timeout);
              resolve(keys);
            }
          });
        });
      }
    }
  };
}

const VALID_ID_PREFIX = "/@id/";
class ServerMockResolver {
  constructor(server, options = {}) {
    this.server = server;
    this.options = options;
  }
  async resolveMock(rawId, importer, options) {
    var _a;
    const { id, fsPath, external } = await this.resolveMockId(rawId, importer);
    if (options.mock === "factory") {
      const manifest = getViteDepsManifest(this.server.config);
      const needsInterop = ((_a = manifest == null ? void 0 : manifest[fsPath]) == null ? void 0 : _a.needsInterop) ?? false;
      return { mockType: "manual", resolvedId: id, needsInterop };
    }
    if (options.mock === "spy") {
      return { mockType: "autospy", resolvedId: id };
    }
    const redirectUrl = findMockRedirect(this.server.config.root, fsPath, external);
    return {
      mockType: redirectUrl === null ? "automock" : "redirect",
      redirectUrl,
      resolvedId: id
    };
  }
  invalidate(ids) {
    ids.forEach((id) => {
      const moduleGraph = this.server.moduleGraph;
      const module = moduleGraph.getModuleById(id);
      if (module) {
        moduleGraph.invalidateModule(module, /* @__PURE__ */ new Set(), Date.now(), true);
      }
    });
  }
  async resolveId(id, importer) {
    const resolved = await this.server.pluginContainer.resolveId(
      id,
      importer,
      {
        ssr: false
      }
    );
    if (!resolved) {
      return null;
    }
    const isOptimized = resolved.id.startsWith(withTrailingSlash(this.server.config.cacheDir));
    let url;
    const root = this.server.config.root;
    if (resolved.id.startsWith(withTrailingSlash(root))) {
      url = resolved.id.slice(root.length);
    } else if (resolved.id !== "/@react-refresh" && isAbsolute(resolved.id) && existsSync(cleanUrl(resolved.id))) {
      url = join$1("/@fs/", resolved.id);
    } else {
      url = resolved.id;
    }
    if (url[0] !== "." && url[0] !== "/") {
      url = id.startsWith(VALID_ID_PREFIX) ? id : VALID_ID_PREFIX + id.replace("\0", "__x00__");
    }
    return {
      id: resolved.id,
      url,
      optimized: isOptimized
    };
  }
  async resolveMockId(rawId, importer) {
    if (!importer.startsWith(this.server.config.root)) {
      importer = join$1(this.server.config.root, importer);
    }
    const resolved = await this.server.pluginContainer.resolveId(
      rawId,
      importer,
      {
        ssr: false
      }
    );
    return this.resolveModule(rawId, resolved);
  }
  resolveModule(rawId, resolved) {
    const id = (resolved == null ? void 0 : resolved.id) || rawId;
    const external = !isAbsolute(id) || isModuleDirectory(this.options, id) ? rawId : null;
    return {
      id,
      fsPath: cleanUrl(id),
      external
    };
  }
}
function isModuleDirectory(config, path) {
  const moduleDirectories = config.moduleDirectories || [
    "/node_modules/"
  ];
  return moduleDirectories.some((dir) => path.includes(dir));
}
const metadata = /* @__PURE__ */ new WeakMap();
function getViteDepsManifest(config) {
  if (metadata.has(config)) {
    return metadata.get(config);
  }
  const cacheDirPath = getDepsCacheDir(config);
  const metadataPath = resolve(cacheDirPath, "_metadata.json");
  if (!existsSync(metadataPath)) {
    return null;
  }
  const { optimized } = JSON.parse(readFileSync(metadataPath, "utf-8"));
  const newManifest = {};
  for (const name in optimized) {
    const dep = optimized[name];
    const file = resolve(cacheDirPath, dep.file);
    newManifest[file] = {
      hash: dep.fileHash,
      needsInterop: dep.needsInterop
    };
  }
  metadata.set(config, newManifest);
  return newManifest;
}
function getDepsCacheDir(config) {
  return resolve(config.cacheDir, "deps");
}
function withTrailingSlash(path) {
  if (path[path.length - 1] !== "/") {
    return `${path}/`;
  }
  return path;
}

function mockerPlugin(options = {}) {
  let server;
  const registerPath = resolve(fileURLToPath(new URL("./register.js", import.meta.url)));
  return [
    {
      name: "vitest:mocker:ws-rpc",
      config(_, { command }) {
        if (command !== "serve") {
          return;
        }
        return {
          server: {
            // don't pre-transform request because they might be mocked at runtime
            preTransformRequests: false
          },
          optimizeDeps: {
            exclude: ["@vitest/mocker/register", "@vitest/mocker/browser"]
          }
        };
      },
      configureServer(server_) {
        server = server_;
        const mockResolver = new ServerMockResolver(server);
        server.ws.on("vitest:mocks:resolveId", async ({ id, importer }) => {
          const resolved = await mockResolver.resolveId(id, importer);
          server.ws.send("vitest:mocks:resolvedId:result", resolved);
        });
        server.ws.on("vitest:mocks:resolveMock", async ({ id, importer, options: options2 }) => {
          const resolved = await mockResolver.resolveMock(id, importer, options2);
          server.ws.send("vitest:mocks:resolveMock:result", resolved);
        });
        server.ws.on("vitest:mocks:invalidate", async ({ ids }) => {
          mockResolver.invalidate(ids);
          server.ws.send("vitest:mocks:invalidate:result");
        });
      },
      async load(id) {
        if (id !== registerPath) {
          return;
        }
        if (!server) {
          return "export {}";
        }
        const content = await readFile(registerPath, "utf-8");
        const result = content.replace(
          /__VITEST_GLOBAL_THIS_ACCESSOR__/g,
          options.globalThisAccessor ?? '"__vitest_mocker__"'
        ).replace(
          "__VITEST_MOCKER_ROOT__",
          JSON.stringify(server.config.root)
        );
        return result;
      }
    },
    hoistMocksPlugin(options.hoistMocks),
    interceptorPlugin(options),
    automockPlugin(options),
    dynamicImportPlugin(options)
  ];
}

export { ServerMockResolver, automockModule, automockPlugin, dynamicImportPlugin, findMockRedirect, hoistMocks, hoistMocksPlugin, interceptorPlugin, mockerPlugin };
