UNPKG

storybook

Version:

Storybook: Develop, document, and test UI components in isolation

1,983 lines • 89.8 kB
import CJS_COMPAT_NODE_URL_qjinduibsyq from 'node:url';
import CJS_COMPAT_NODE_PATH_qjinduibsyq from 'node:path';
import CJS_COMPAT_NODE_MODULE_qjinduibsyq from "node:module";

var __filename = CJS_COMPAT_NODE_URL_qjinduibsyq.fileURLToPath(import.meta.url);
var __dirname = CJS_COMPAT_NODE_PATH_qjinduibsyq.dirname(__filename);
var require = CJS_COMPAT_NODE_MODULE_qjinduibsyq.createRequire(import.meta.url);

// ------------------------------------------------------------
// end of CJS compatibility banner, injected by Storybook's esbuild configuration
// ------------------------------------------------------------
import {
  findVarInitialization,
  formatCsf,
  importedName,
  isCanonicalCsf2BindCall,
  isCsfFactoryCall,
  isTypeSpecifier,
  keyOf,
  loadCsf,
  pathForNode,
  resolveArgValue,
  resolveArgsRecord,
  resolveBindingMembers,
  resolveIdentifierInit,
  resolveObjectMembers,
  sourceOf
} from "./chunk-PG6QDVJT.js";
import {
  require_dist
} from "./chunk-FE5AV6EP.js";
import {
  __toESM
} from "./chunk-4US4PNS3.js";

// src/csf-tools/ConfigFile.ts
var import_ts_dedent = __toESM(require_dist(), 1);
import { readFile, writeFile } from "node:fs/promises";
import {
  babelParse,
  generate,
  recast,
  types as t,
  traverse
} from "storybook/internal/babel";
import { logger } from "storybook/internal/node-logger";
var getCsfParsingErrorMessage = ({
  expectedType,
  foundType,
  node
}) => import_ts_dedent.dedent`
      CSF Parsing error: Expected '${expectedType}' but found '${foundType}' instead in '${node?.type}'.
    `, propKey = (p) => t.isIdentifier(p.key) ? p.key.name : t.isStringLiteral(p.key) ? p.key.value : null, _getPath = (path2, node) => {
  if (path2.length === 0)
    return node;
  if (t.isObjectExpression(node)) {
    let [first, ...rest] = path2, field = node.properties.find((p) => propKey(p) === first);
    if (field)
      return _getPath(rest, field.value);
  }
}, _getPathProperties = (path2, node) => {
  if (path2.length === 0) {
    if (t.isObjectExpression(node))
      return node.properties;
    throw new Error("Expected object expression");
  }
  if (t.isObjectExpression(node)) {
    let [first, ...rest] = path2, field = node.properties.find((p) => propKey(p) === first);
    if (field)
      return rest.length === 0 ? node.properties : _getPathProperties(rest, field.value);
  }
}, _findVarDeclarator = (identifier, program) => {
  let declarator = null, declarations = null;
  return program.body.find((node) => (t.isVariableDeclaration(node) ? declarations = node.declarations : t.isExportNamedDeclaration(node) && t.isVariableDeclaration(node.declaration) && (declarations = node.declaration.declarations), declarations && declarations.find((decl) => t.isVariableDeclarator(decl) && t.isIdentifier(decl.id) && decl.id.name === identifier ? (declarator = decl, !0) : !1))), declarator;
}, _findVarInitialization = (identifier, program) => _findVarDeclarator(identifier, program)?.init, _makeObjectExpression = (path2, value) => {
  if (path2.length === 0)
    return value;
  let [first, ...rest] = path2, innerExpression = _makeObjectExpression(rest, value);
  return t.objectExpression([t.objectProperty(t.identifier(first), innerExpression)]);
}, _updateExportNode = (path2, expr, existing) => {
  let [first, ...rest] = path2, existingField = existing.properties.find(
    (p) => propKey(p) === first
  );
  existingField ? t.isObjectExpression(existingField.value) && rest.length > 0 ? _updateExportNode(rest, expr, existingField.value) : existingField.value = _makeObjectExpression(rest, expr) : existing.properties.push(
    t.objectProperty(t.identifier(first), _makeObjectExpression(rest, expr))
  );
}, ConfigFile = class {
  constructor(ast, code, fileName) {
    this._exports = {};
    // FIXME: this is a hack. this is only used in the case where the user is
    // modifying a named export that's a scalar. The _exports map is not suitable
    // for that. But rather than refactor the whole thing, we just use this as a stopgap.
    this._exportDecls = {};
    this.hasDefaultExport = !1;
    /** Unwraps TS assertions/satisfies from a node, to get the underlying node. */
    this._unwrap = (node) => t.isTSAsExpression(node) || t.isTSSatisfiesExpression(node) ? this._unwrap(node.expression) : node;
    /**
     * Resolve a declaration node by unwrapping TS assertions/satisfies and following identifiers to
     * resolve the correct node in case it's an identifier.
     */
    this._resolveDeclaration = (node, parent = this._ast.program) => {
      let decl = this._unwrap(node);
      if (t.isIdentifier(decl) && t.isProgram(parent)) {
        let initialization = _findVarInitialization(decl.name, parent);
        return initialization ? this._unwrap(initialization) : decl;
      }
      return decl;
    };
    this._ast = ast, this._code = code, this.fileName = fileName;
  }
  _parseExportsObject(exportsObject) {
    this._exportsObject = exportsObject, exportsObject.properties.forEach((p) => {
      let exportName = propKey(p);
      if (exportName) {
        let exportVal = this._resolveDeclaration(p.value);
        this._exports[exportName] = exportVal;
      }
    });
  }
  parse() {
    let self = this;
    return traverse(this._ast, {
      ExportDefaultDeclaration: {
        enter({ node, parent }) {
          self.hasDefaultExport = !0;
          let decl = self._resolveDeclaration(node.declaration, parent);
          for (; t.isCallExpression(decl); )
            if (t.isObjectExpression(decl.arguments[0])) {
              decl = decl.arguments[0];
              break;
            } else if (t.isMemberExpression(decl.callee) && t.isCallExpression(decl.callee.object))
              decl = decl.callee.object;
            else
              break;
          t.isObjectExpression(decl) ? self._parseExportsObject(decl) : logger.warn(
            getCsfParsingErrorMessage({
              expectedType: "ObjectExpression",
              foundType: decl?.type,
              node: decl || node.declaration
            })
          );
        }
      },
      ExportNamedDeclaration: {
        enter({ node, parent }) {
          if (t.isVariableDeclaration(node.declaration))
            node.declaration.declarations.forEach((decl) => {
              if (t.isVariableDeclarator(decl) && t.isIdentifier(decl.id)) {
                let { name: exportName } = decl.id, exportVal = self._resolveDeclaration(decl.init, parent);
                self._exports[exportName] = exportVal, self._exportDecls[exportName] = decl;
              }
            });
          else if (t.isFunctionDeclaration(node.declaration)) {
            let decl = node.declaration;
            if (t.isIdentifier(decl.id)) {
              let { name: exportName } = decl.id;
              self._exportDecls[exportName] = decl;
            }
          } else node.specifiers ? node.specifiers.forEach((spec) => {
            if (t.isExportSpecifier(spec) && t.isIdentifier(spec.local) && t.isIdentifier(spec.exported)) {
              let { name: localName } = spec.local, { name: exportName } = spec.exported, decl = _findVarDeclarator(localName, parent);
              decl && (self._exports[exportName] = self._resolveDeclaration(decl.init, parent), self._exportDecls[exportName] = decl);
            }
          }) : logger.warn(
            getCsfParsingErrorMessage({
              expectedType: "VariableDeclaration",
              foundType: node.declaration?.type,
              node: node.declaration
            })
          );
        }
      },
      ExpressionStatement: {
        enter({ node, parent }) {
          if (t.isAssignmentExpression(node.expression) && node.expression.operator === "=") {
            let { left, right } = node.expression;
            if (t.isMemberExpression(left) && t.isIdentifier(left.object) && left.object.name === "module" && t.isIdentifier(left.property) && left.property.name === "exports") {
              let exportObject = right;
              exportObject = self._resolveDeclaration(exportObject, parent), t.isObjectExpression(exportObject) ? (self._exportsObject = exportObject, exportObject.properties.forEach((p) => {
                let exportName = propKey(p);
                if (exportName) {
                  let exportVal = self._resolveDeclaration(p.value, parent);
                  self._exports[exportName] = exportVal;
                }
              })) : logger.warn(
                getCsfParsingErrorMessage({
                  expectedType: "ObjectExpression",
                  foundType: exportObject?.type,
                  node: exportObject
                })
              );
            }
          }
        }
      },
      CallExpression: {
        enter: ({ node }) => {
          t.isIdentifier(node.callee) && node.callee.name === "definePreview" && node.arguments.length === 1 && t.isObjectExpression(node.arguments[0]) && self._parseExportsObject(node.arguments[0]);
        }
      }
    }), self;
  }
  getFieldNode(path2) {
    let [root, ...rest] = path2, exported = this._exports[root];
    if (exported)
      return _getPath(rest, exported);
  }
  getFieldProperties(path2) {
    let [root, ...rest] = path2, exported = this._exports[root];
    if (exported)
      return _getPathProperties(rest, exported);
  }
  getFieldValue(path2) {
    let node = this.getFieldNode(path2);
    if (node) {
      let { code } = generate(node, {});
      return (0, eval)(`(() => (${code}))()`);
    }
  }
  getSafeFieldValue(path2) {
    try {
      return this.getFieldValue(path2);
    } catch {
    }
  }
  setFieldNode(path2, expr) {
    let [first, ...rest] = path2, exportNode = this._exports[first];
    if (this._exportsObject) {
      let existingProp = this._exportsObject.properties.find((p) => propKey(p) === first);
      if (existingProp && t.isIdentifier(existingProp.value)) {
        let varDecl2 = _findVarDeclarator(existingProp.value.name, this._ast.program);
        if (varDecl2 && t.isObjectExpression(varDecl2.init)) {
          _updateExportNode(rest, expr, varDecl2.init);
          return;
        }
      }
      _updateExportNode(path2, expr, this._exportsObject), this._exports[path2[0]] = expr;
      return;
    }
    if (exportNode && t.isObjectExpression(exportNode) && rest.length > 0) {
      _updateExportNode(rest, expr, exportNode);
      return;
    }
    let varDecl = _findVarDeclarator(first, this._ast.program);
    if (varDecl && t.isObjectExpression(varDecl.init)) {
      _updateExportNode(rest, expr, varDecl.init);
      return;
    }
    if (exportNode && rest.length === 0 && this._exportDecls[path2[0]]) {
      let decl = this._exportDecls[path2[0]];
      t.isVariableDeclarator(decl) && (decl.init = _makeObjectExpression([], expr));
    } else {
      if (this.hasDefaultExport)
        throw new Error(
          `Could not set the "${path2.join(
            "."
          )}" field as the default export is not an object in this file.`
        );
      {
        let exportObj = _makeObjectExpression(rest, expr), newExport = t.exportNamedDeclaration(
          t.variableDeclaration("const", [t.variableDeclarator(t.identifier(first), exportObj)])
        );
        this._exports[first] = exportObj, this._ast.program.body.push(newExport);
      }
    }
  }
  /**
   * @example
   *
   * ```ts
   * // 1. { framework: 'framework-name' }
   * // 2. { framework: { name: 'framework-name', options: {} }
   * getNameFromPath(['framework']); // => 'framework-name'
   * ```
   *
   * @returns The name of a node in a given path, supporting the following formats:
   */
  getNameFromPath(path2) {
    let node = this.getFieldNode(path2);
    if (node)
      return this._getPresetValue(node, "name");
  }
  /**
   * Returns an array of names of a node in a given path, supporting the following formats:
   *
   * @example
   *
   * ```ts
   * const config = {
   *   addons: ['first-addon', { name: 'second-addon', options: {} }],
   * };
   * // => ['first-addon', 'second-addon']
   * getNamesFromPath(['addons']);
   * ```
   */
  getNamesFromPath(path2) {
    let node = this.getFieldNode(path2);
    if (!node)
      return;
    let pathNames = [];
    return t.isArrayExpression(node) && node.elements.forEach((element) => {
      pathNames.push(this._getPresetValue(element, "name"));
    }), pathNames;
  }
  _getPnpWrappedValue(node) {
    if (t.isCallExpression(node)) {
      let arg = node.arguments[0];
      if (t.isStringLiteral(arg))
        return arg.value;
    }
  }
  /**
   * Given a node and a fallback property, returns a **non-evaluated** string value of the node.
   *
   * 1. `{ node: 'value' }`
   * 2. `{ node: { fallbackProperty: 'value' } }`
   */
  _getPresetValue(node, fallbackProperty) {
    let value;
    if (t.isStringLiteral(node) ? value = node.value : t.isObjectExpression(node) ? node.properties.forEach((prop) => {
      t.isObjectProperty(prop) && t.isIdentifier(prop.key) && prop.key.name === fallbackProperty && (t.isStringLiteral(prop.value) ? value = prop.value.value : value = this._getPnpWrappedValue(prop.value)), t.isObjectProperty(prop) && t.isStringLiteral(prop.key) && prop.key.value === "name" && t.isStringLiteral(prop.value) && (value = prop.value.value);
    }) : t.isCallExpression(node) && (value = this._getPnpWrappedValue(node)), !value)
      throw new Error(
        `The given node must be a string literal or an object expression with a "${fallbackProperty}" property that is a string literal.`
      );
    return value;
  }
  removeField(path2) {
    let removeProperty = (properties2, prop) => {
      let index = properties2.findIndex(
        (p) => t.isIdentifier(p.key) && p.key.name === prop || t.isStringLiteral(p.key) && p.key.value === prop
      );
      index >= 0 && properties2.splice(index, 1);
    };
    if (path2.length === 1) {
      let removedRootProperty = !1;
      if (this._ast.program.body.forEach((node) => {
        if (t.isExportNamedDeclaration(node) && t.isVariableDeclaration(node.declaration)) {
          let decl = node.declaration.declarations[0];
          t.isIdentifier(decl.id) && decl.id.name === path2[0] && (this._ast.program.body.splice(this._ast.program.body.indexOf(node), 1), removedRootProperty = !0);
        }
        if (t.isExportDefaultDeclaration(node)) {
          let resolved = this._resolveDeclaration(node.declaration);
          if (t.isObjectExpression(resolved)) {
            let properties2 = resolved.properties;
            removeProperty(properties2, path2[0]), removedRootProperty = !0;
          }
        }
        if (t.isExpressionStatement(node) && t.isAssignmentExpression(node.expression) && t.isMemberExpression(node.expression.left) && t.isIdentifier(node.expression.left.object) && node.expression.left.object.name === "module" && t.isIdentifier(node.expression.left.property) && node.expression.left.property.name === "exports" && t.isObjectExpression(node.expression.right)) {
          let properties2 = node.expression.right.properties;
          removeProperty(properties2, path2[0]), removedRootProperty = !0;
        }
      }), removedRootProperty)
        return;
    }
    let properties = this.getFieldProperties(path2);
    if (properties) {
      let lastPath = path2.at(-1);
      removeProperty(properties, lastPath);
    }
  }
  appendValueToArray(path2, value) {
    let node = this.valueToNode(value);
    node && this.appendNodeToArray(path2, node);
  }
  appendNodeToArray(path2, node) {
    let current = this.getFieldNode(path2);
    if (!current)
      this.setFieldNode(path2, t.arrayExpression([node]));
    else if (t.isArrayExpression(current))
      current.elements.push(node);
    else
      throw new Error(`Expected array at '${path2.join(".")}', got '${current.type}'`);
  }
  /**
   * Specialized helper to remove addons or other array entries that can either be strings or
   * objects with a name property.
   */
  removeEntryFromArray(path2, value) {
    let current = this.getFieldNode(path2);
    if (current)
      if (t.isArrayExpression(current)) {
        let index = current.elements.findIndex((element) => t.isStringLiteral(element) ? element.value === value : t.isObjectExpression(element) ? this._getPresetValue(element, "name") === value : this._getPnpWrappedValue(element) === value);
        if (index >= 0)
          current.elements.splice(index, 1);
        else
          throw new Error(`Could not find '${value}' in array at '${path2.join(".")}'`);
      } else
        throw new Error(`Expected array at '${path2.join(".")}', got '${current.type}'`);
  }
  _inferQuotes() {
    if (!this._quotes) {
      let occurrences = (this._ast.tokens || []).slice(0, 500).reduce(
        (acc, token) => (token.type.label === "string" && (acc[this._code[token.start]] += 1), acc),
        { "'": 0, '"': 0 }
      );
      this._quotes = occurrences["'"] > occurrences['"'] ? "single" : "double";
    }
    return this._quotes;
  }
  valueToNode(value) {
    let quotes = this._inferQuotes(), valueNode;
    if (quotes === "single") {
      let { code } = generate(t.valueToNode(value), { jsescOption: { quotes } }), program = babelParse(`const __x = ${code}`);
      traverse(program, {
        VariableDeclaration: {
          enter({ node }) {
            node.declarations.length === 1 && t.isVariableDeclarator(node.declarations[0]) && t.isIdentifier(node.declarations[0].id) && node.declarations[0].id.name === "__x" && (valueNode = node.declarations[0].init);
          }
        }
      });
    } else
      valueNode = t.valueToNode(value);
    return valueNode;
  }
  setFieldValue(path2, value) {
    let valueNode = this.valueToNode(value);
    if (!valueNode)
      throw new Error(`Unexpected value ${JSON.stringify(value)}`);
    this.setFieldNode(path2, valueNode);
  }
  getBodyDeclarations() {
    return this._ast.program.body;
  }
  setBodyDeclaration(declaration) {
    this._ast.program.body.push(declaration);
  }
  /**
   * Import specifiers for a specific require import
   *
   * @example
   *
   * ```ts
   * // const { foo } = require('bar');
   * setRequireImport(['foo'], 'bar');
   *
   * // const foo = require('bar');
   * setRequireImport('foo', 'bar');
   * ```
   *
   * @param importSpecifiers - The import specifiers to set. If a string is passed in, a default
   *   import will be set. Otherwise, an array of named imports will be set
   * @param fromImport - The module to import from
   */
  setRequireImport(importSpecifier, fromImport) {
    let requireDeclaration = this._ast.program.body.find((node) => {
      let hasDeclaration = t.isVariableDeclaration(node) && node.declarations.length === 1 && t.isVariableDeclarator(node.declarations[0]) && t.isCallExpression(node.declarations[0].init) && t.isIdentifier(node.declarations[0].init.callee) && node.declarations[0].init.callee.name === "require" && t.isStringLiteral(node.declarations[0].init.arguments[0]) && (node.declarations[0].init.arguments[0].value === fromImport || node.declarations[0].init.arguments[0].value === fromImport.split("node:")[1]);
      return hasDeclaration && (fromImport = node.declarations[0].init.arguments[0].value), hasDeclaration;
    }), hasRequireSpecifier = (name) => t.isObjectPattern(requireDeclaration?.declarations[0].id) && requireDeclaration?.declarations[0].id.properties.find(
      (specifier) => t.isObjectProperty(specifier) && t.isIdentifier(specifier.key) && specifier.key.name === name
    ), hasDefaultRequireSpecifier = (declaration, name) => declaration.declarations.length === 1 && t.isVariableDeclarator(declaration.declarations[0]) && t.isIdentifier(declaration.declarations[0].id) && declaration.declarations[0].id.name === name;
    if (typeof importSpecifier == "string") {
      let addDefaultRequireSpecifier = () => {
        this._ast.program.body.unshift(
          t.variableDeclaration("const", [
            t.variableDeclarator(
              t.identifier(importSpecifier),
              t.callExpression(t.identifier("require"), [t.stringLiteral(fromImport)])
            )
          ])
        );
      };
      requireDeclaration && hasDefaultRequireSpecifier(requireDeclaration, importSpecifier) || addDefaultRequireSpecifier();
    } else requireDeclaration ? importSpecifier.forEach((specifier) => {
      hasRequireSpecifier(specifier) || requireDeclaration.declarations[0].id.properties.push(
        t.objectProperty(t.identifier(specifier), t.identifier(specifier), void 0, !0)
      );
    }) : this._ast.program.body.unshift(
      t.variableDeclaration("const", [
        t.variableDeclarator(
          t.objectPattern(
            importSpecifier.map(
              (specifier) => t.objectProperty(t.identifier(specifier), t.identifier(specifier), void 0, !0)
            )
          ),
          t.callExpression(t.identifier("require"), [t.stringLiteral(fromImport)])
        )
      ])
    );
  }
  /**
   * Set import specifiers for a given import statement.
   *
   * Does not support setting type imports (yet)
   *
   * @example
   *
   * ```ts
   * // import { foo } from 'bar';
   * setImport(['foo'], 'bar');
   *
   * // import foo from 'bar';
   * setImport('foo', 'bar');
   *
   * // import * as foo from 'bar';
   * setImport({ namespace: 'foo' }, 'bar');
   *
   * // import 'bar';
   * setImport(null, 'bar');
   * ```
   *
   * @param importSpecifiers - The import specifiers to set. If a string is passed in, a default
   *   import will be set. Otherwise, an array of named imports will be set
   * @param fromImport - The module to import from
   */
  setImport(importSpecifier, fromImport) {
    let importDeclaration = this._ast.program.body.find((node) => {
      let hasDeclaration = t.isImportDeclaration(node) && (node.source.value === fromImport || node.source.value === fromImport.split("node:")[1]);
      return hasDeclaration && (fromImport = node.source.value), hasDeclaration;
    }), getNewImportSpecifier = (specifier) => t.importSpecifier(t.identifier(specifier), t.identifier(specifier)), hasImportSpecifier = (declaration, name) => declaration.specifiers.find(
      (specifier) => t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported) && specifier.imported.name === name
    ), hasNamespaceImportSpecifier = (declaration, name) => declaration.specifiers.find(
      (specifier) => t.isImportNamespaceSpecifier(specifier) && t.isIdentifier(specifier.local) && specifier.local.name === name
    );
    importSpecifier === null ? importDeclaration || this._ast.program.body.unshift(t.importDeclaration([], t.stringLiteral(fromImport))) : typeof importSpecifier == "string" ? importDeclaration ? ((declaration, name) => declaration.specifiers.find(
      (specifier) => t.isImportDefaultSpecifier(specifier) && t.isIdentifier(specifier.local) && specifier.local.name === name
    ))(importDeclaration, importSpecifier) || importDeclaration.specifiers.push(
      t.importDefaultSpecifier(t.identifier(importSpecifier))
    ) : this._ast.program.body.unshift(
      t.importDeclaration(
        [t.importDefaultSpecifier(t.identifier(importSpecifier))],
        t.stringLiteral(fromImport)
      )
    ) : Array.isArray(importSpecifier) ? importDeclaration ? importSpecifier.forEach((specifier) => {
      hasImportSpecifier(importDeclaration, specifier) || importDeclaration.specifiers.push(getNewImportSpecifier(specifier));
    }) : this._ast.program.body.unshift(
      t.importDeclaration(
        importSpecifier.map(getNewImportSpecifier),
        t.stringLiteral(fromImport)
      )
    ) : importSpecifier.namespace && (importDeclaration ? hasNamespaceImportSpecifier(importDeclaration, importSpecifier.namespace) || importDeclaration.specifiers.push(
      t.importNamespaceSpecifier(t.identifier(importSpecifier.namespace))
    ) : this._ast.program.body.unshift(
      t.importDeclaration(
        [t.importNamespaceSpecifier(t.identifier(importSpecifier.namespace))],
        t.stringLiteral(fromImport)
      )
    ));
  }
  _removeRequireImport(importSpecifier, fromImport) {
    let requireDeclarationIndex = this._ast.program.body.findIndex((node) => t.isVariableDeclaration(node) && node.declarations.length === 1 && t.isVariableDeclarator(node.declarations[0]) && t.isCallExpression(node.declarations[0].init) && t.isIdentifier(node.declarations[0].init.callee) && node.declarations[0].init.callee.name === "require" && t.isStringLiteral(node.declarations[0].init.arguments[0]) && (node.declarations[0].init.arguments[0].value === fromImport || node.declarations[0].init.arguments[0].value === fromImport.split("node:")[1]));
    if (requireDeclarationIndex === -1)
      return;
    let declarator = this._ast.program.body[requireDeclarationIndex].declarations[0];
    if (importSpecifier !== null) {
      if (typeof importSpecifier == "string") {
        t.isIdentifier(declarator.id) && declarator.id.name === importSpecifier && this._ast.program.body.splice(requireDeclarationIndex, 1);
        return;
      }
      if (!(typeof importSpecifier == "object" && "namespace" in importSpecifier) && Array.isArray(importSpecifier) && t.isObjectPattern(declarator.id)) {
        let objectPattern = declarator.id;
        importSpecifier.forEach((specifier) => {
          let index = objectPattern.properties.findIndex(
            (prop) => t.isObjectProperty(prop) && t.isIdentifier(prop.key) && prop.key.name === specifier
          );
          index !== -1 && objectPattern.properties.splice(index, 1);
        }), objectPattern.properties.length === 0 && this._ast.program.body.splice(requireDeclarationIndex, 1);
      }
    }
  }
  _removeImport(importSpecifier, fromImport) {
    let importDeclarationIndex = this._ast.program.body.findIndex(
      (node) => t.isImportDeclaration(node) && (node.source.value === fromImport || node.source.value === fromImport.split("node:")[1])
    );
    if (importDeclarationIndex === -1)
      return;
    let importDeclaration = this._ast.program.body[importDeclarationIndex];
    if (importSpecifier === null) {
      importDeclaration.specifiers.length === 0 && this._ast.program.body.splice(importDeclarationIndex, 1);
      return;
    }
    if (typeof importSpecifier == "object" && "namespace" in importSpecifier) {
      let index = importDeclaration.specifiers.findIndex(
        (specifier) => t.isImportNamespaceSpecifier(specifier) && t.isIdentifier(specifier.local) && specifier.local.name === importSpecifier.namespace
      );
      index !== -1 && importDeclaration.specifiers.splice(index, 1);
    }
    if (typeof importSpecifier == "string") {
      let index = importDeclaration.specifiers.findIndex(
        (specifier) => t.isImportDefaultSpecifier(specifier) && t.isIdentifier(specifier.local) && specifier.local.name === importSpecifier
      );
      index !== -1 && importDeclaration.specifiers.splice(index, 1);
    }
    Array.isArray(importSpecifier) && importSpecifier.forEach((specifier) => {
      let index = importDeclaration.specifiers.findIndex(
        (current) => t.isImportSpecifier(current) && t.isIdentifier(current.imported) && current.imported.name === specifier
      );
      index !== -1 && importDeclaration.specifiers.splice(index, 1);
    }), importDeclaration.specifiers.length === 0 && this._ast.program.body.splice(importDeclarationIndex, 1);
  }
  /**
   * Remove import specifiers for a given import statement.
   *
   * Does not support removing type imports (yet)
   *
   * @example
   *
   * ```ts
   * // import { foo } from 'bar';
   * setImport(['foo'], 'bar');
   *
   * // import foo from 'bar';
   * setImport('foo', 'bar');
   *
   * // import * as foo from 'bar';
   * setImport({ namespace: 'foo' }, 'bar');
   *
   * // import 'bar';
   * setImport(null, 'bar');
   * ```
   *
   * @param importSpecifiers - The import specifiers to remove. If a string is passed in, will only
   *   remove the default import. Otherwise, named imports matching the array will be removed.
   * @param fromImport - The module to import from
   */
  removeImport(importSpecifier, fromImport) {
    this._removeRequireImport(importSpecifier, fromImport), this._removeImport(importSpecifier, fromImport);
  }
}, loadConfig = (code, fileName) => {
  let ast = babelParse(code);
  return new ConfigFile(ast, code, fileName);
}, formatConfig = (config) => printConfig(config).code, printConfig = (config, options = {}) => recast.print(config._ast, options), readConfig = async (fileName) => {
  let code = (await readFile(fileName, "utf-8")).toString();
  return loadConfig(code, fileName).parse();
}, writeConfig = async (config, fileName) => {
  let fname = fileName || config.fileName;
  if (!fname)
    throw new Error("Please specify a fileName for writeConfig");
  await writeFile(fname, formatConfig(config));
}, isCsfFactoryPreview = (previewConfig) => !!previewConfig._ast.program.body.find((node) => t.isImportDeclaration(node) && node.source.value.includes("storybook") && node.specifiers.some((specifier) => t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported) && specifier.imported.name === "definePreview"));

// src/csf-tools/getStorySortParameter.ts
var import_ts_dedent2 = __toESM(require_dist(), 1);
import { babelParse as babelParse2, generate as generate2, types as t2, traverse as traverse2 } from "storybook/internal/babel";
import { logger as logger2 } from "storybook/internal/node-logger";
var getValue = (obj, key) => {
  let value;
  return obj.properties.forEach((p) => {
    t2.isIdentifier(p.key) && p.key.name === key && (value = p.value);
  }), value;
}, parseValue = (value) => {
  let expr = stripTSModifiers(value);
  if (t2.isArrayExpression(expr))
    return expr.elements.map((o) => parseValue(o));
  if (t2.isObjectExpression(expr))
    return expr.properties.reduce((acc, p) => (t2.isIdentifier(p.key) && (acc[p.key.name] = parseValue(p.value)), acc), {});
  if (t2.isLiteral(expr))
    return expr.value;
  if (t2.isIdentifier(expr))
    return unsupported(expr.name, !0);
  throw new Error(`Unknown node type ${expr.type}`);
}, unsupported = (unexpectedVar, isError) => {
  let message = import_ts_dedent2.dedent`
    Unexpected '${unexpectedVar}'. Parameter 'options.storySort' should be defined inline e.g.:

    export default {
      parameters: {
        options: {
          storySort: <array | object | function>
        },
      },
    };
  `;
  if (isError)
    throw new Error(message);
  logger2.log(message);
}, stripTSModifiers = (expr) => t2.isTSAsExpression(expr) || t2.isTSSatisfiesExpression(expr) ? expr.expression : expr, parseParameters = (params) => {
  let paramsObject = stripTSModifiers(params);
  if (t2.isObjectExpression(paramsObject)) {
    let options = getValue(paramsObject, "options");
    if (options) {
      if (t2.isObjectExpression(options))
        return getValue(options, "storySort");
      unsupported("options", !0);
    }
  }
}, parseDefault = (defaultExpr, program) => {
  let defaultObj = stripTSModifiers(defaultExpr);
  if (t2.isObjectExpression(defaultObj)) {
    let params = getValue(defaultObj, "parameters");
    if (t2.isIdentifier(params) && (params = findVarInitialization(params.name, program)), params)
      return parseParameters(params);
  } else
    unsupported("default", !0);
}, getStorySortParameter = (previewCode) => {
  if (!previewCode.includes("storySort"))
    return;
  let storySort, ast = babelParse2(previewCode);
  if (traverse2(ast, {
    ExportNamedDeclaration: {
      enter({ node }) {
        t2.isVariableDeclaration(node.declaration) ? node.declaration.declarations.forEach((decl) => {
          if (t2.isVariableDeclarator(decl) && t2.isIdentifier(decl.id)) {
            let { name: exportName } = decl.id;
            if (exportName === "parameters" && decl.init) {
              let paramsObject = stripTSModifiers(decl.init);
              storySort = parseParameters(paramsObject);
            }
          }
        }) : node.specifiers.forEach((spec) => {
          t2.isIdentifier(spec.exported) && spec.exported.name === "parameters" && unsupported("parameters", !1);
        });
      }
    },
    ExportDefaultDeclaration: {
      enter({ node }) {
        let defaultObj = node.declaration;
        t2.isIdentifier(defaultObj) && (defaultObj = findVarInitialization(defaultObj.name, ast.program)), defaultObj = stripTSModifiers(defaultObj), t2.isCallExpression(defaultObj) && t2.isObjectExpression(defaultObj.arguments?.[0]) ? storySort = parseDefault(defaultObj.arguments[0], ast.program) : t2.isObjectExpression(defaultObj) ? storySort = parseDefault(defaultObj, ast.program) : unsupported("default", !1);
      }
    }
  }), !!storySort) {
    if (t2.isArrowFunctionExpression(storySort)) {
      let { code: sortCode } = generate2(storySort, {});
      return (0, eval)(sortCode);
    }
    if (t2.isFunctionExpression(storySort)) {
      let { code: sortCode } = generate2(storySort, {}), functionName = storySort.id?.name, wrapper = `(a, b) => {
      ${sortCode};
      return ${functionName}(a, b)
    }`;
      return (0, eval)(wrapper);
    }
    return t2.isLiteral(storySort) || t2.isArrayExpression(storySort) || t2.isObjectExpression(storySort) ? parseValue(storySort) : unsupported("storySort", !0);
  }
};

// ../../node_modules/comment-parser/es6/primitives.js
var Markers;
(function(Markers2) {
  Markers2.start = "/**", Markers2.nostart = "/***", Markers2.delim = "*", Markers2.end = "*/";
})(Markers = Markers || (Markers = {}));

// ../../node_modules/comment-parser/es6/util.js
function isSpace(source) {
  return /^\s+$/.test(source);
}
function splitCR(source) {
  let matches = source.match(/\r+$/);
  return matches == null ? ["", source] : [source.slice(-matches[0].length), source.slice(0, -matches[0].length)];
}
function splitSpace(source) {
  let matches = source.match(/^\s+/);
  return matches == null ? ["", source] : [source.slice(0, matches[0].length), source.slice(matches[0].length)];
}
function splitLines(source) {
  return source.split(/\n/);
}
function seedSpec(spec = {}) {
  return Object.assign({ tag: "", name: "", type: "", optional: !1, description: "", problems: [], source: [] }, spec);
}
function seedTokens(tokens = {}) {
  return Object.assign({ start: "", delimiter: "", postDelimiter: "", tag: "", postTag: "", name: "", postName: "", type: "", postType: "", description: "", end: "", lineEnd: "" }, tokens);
}

// ../../node_modules/comment-parser/es6/parser/block-parser.js
var reTag = /^@\S+/;
function getParser({ fence = "```" } = {}) {
  let fencer = getFencer(fence), toggleFence = (source, isFenced) => fencer(source) ? !isFenced : isFenced;
  return function(source) {
    let sections = [[]], isFenced = !1;
    for (let line of source)
      reTag.test(line.tokens.description) && !isFenced ? sections.push([line]) : sections[sections.length - 1].push(line), isFenced = toggleFence(line.tokens.description, isFenced);
    return sections;
  };
}
function getFencer(fence) {
  return typeof fence == "string" ? (source) => source.split(fence).length % 2 === 0 : fence;
}

// ../../node_modules/comment-parser/es6/parser/source-parser.js
function getParser2({ startLine = 0, markers = Markers } = {}) {
  let block = null, num = startLine;
  return function(source) {
    let rest = source, tokens = seedTokens();
    if ([tokens.lineEnd, rest] = splitCR(rest), [tokens.start, rest] = splitSpace(rest), block === null && rest.startsWith(markers.start) && !rest.startsWith(markers.nostart) && (block = [], tokens.delimiter = rest.slice(0, markers.start.length), rest = rest.slice(markers.start.length), [tokens.postDelimiter, rest] = splitSpace(rest)), block === null)
      return num++, null;
    let isClosed = rest.trimRight().endsWith(markers.end);
    if (tokens.delimiter === "" && rest.startsWith(markers.delim) && !rest.startsWith(markers.end) && (tokens.delimiter = markers.delim, rest = rest.slice(markers.delim.length), [tokens.postDelimiter, rest] = splitSpace(rest)), isClosed) {
      let trimmed = rest.trimRight();
      tokens.end = rest.slice(trimmed.length - markers.end.length), rest = trimmed.slice(0, -markers.end.length);
    }
    if (tokens.description = rest, block.push({ number: num, source, tokens }), num++, isClosed) {
      let result = block.slice();
      return block = null, result;
    }
    return null;
  };
}

// ../../node_modules/comment-parser/es6/parser/spec-parser.js
function getParser3({ tokenizers }) {
  return function(source) {
    var _a;
    let spec = seedSpec({ source });
    for (let tokenize2 of tokenizers)
      if (spec = tokenize2(spec), !((_a = spec.problems[spec.problems.length - 1]) === null || _a === void 0) && _a.critical)
        break;
    return spec;
  };
}

// ../../node_modules/comment-parser/es6/parser/tokenizers/tag.js
function tagTokenizer() {
  return (spec) => {
    let { tokens } = spec.source[0], match = tokens.description.match(/\s*(@(\S+))(\s*)/);
    return match === null ? (spec.problems.push({
      code: "spec:tag:prefix",
      message: 'tag should start with "@" symbol',
      line: spec.source[0].number,
      critical: !0
    }), spec) : (tokens.tag = match[1], tokens.postTag = match[3], tokens.description = tokens.description.slice(match[0].length), spec.tag = match[2], spec);
  };
}

// ../../node_modules/comment-parser/es6/parser/tokenizers/type.js
function typeTokenizer(spacing = "compact") {
  let join2 = getJoiner(spacing);
  return (spec) => {
    let curlies = 0, lines = [];
    for (let [i, { tokens }] of spec.source.entries()) {
      let type = "";
      if (i === 0 && tokens.description[0] !== "{")
        return spec;
      for (let ch of tokens.description)
        if (ch === "{" && curlies++, ch === "}" && curlies--, type += ch, curlies === 0)
          break;
      if (lines.push([tokens, type]), curlies === 0)
        break;
    }
    if (curlies !== 0)
      return spec.problems.push({
        code: "spec:type:unpaired-curlies",
        message: "unpaired curlies",
        line: spec.source[0].number,
        critical: !0
      }), spec;
    let parts = [], offset = lines[0][0].postDelimiter.length;
    for (let [i, [tokens, type]] of lines.entries())
      tokens.type = type, i > 0 && (tokens.type = tokens.postDelimiter.slice(offset) + type, tokens.postDelimiter = tokens.postDelimiter.slice(0, offset)), [tokens.postType, tokens.description] = splitSpace(tokens.description.slice(type.length)), parts.push(tokens.type);
    return parts[0] = parts[0].slice(1), parts[parts.length - 1] = parts[parts.length - 1].slice(0, -1), spec.type = join2(parts), spec;
  };
}
var trim = (x) => x.trim();
function getJoiner(spacing) {
  return spacing === "compact" ? (t8) => t8.map(trim).join("") : spacing === "preserve" ? (t8) => t8.join(`
`) : spacing;
}

// ../../node_modules/comment-parser/es6/parser/tokenizers/name.js
var isQuoted = (s) => s && s.startsWith('"') && s.endsWith('"');
function nameTokenizer() {
  let typeEnd = (num, { tokens }, i) => tokens.type === "" ? num : i;
  return (spec) => {
    let { tokens } = spec.source[spec.source.reduce(typeEnd, 0)], source = tokens.description.trimLeft(), quotedGroups = source.split('"');
    if (quotedGroups.length > 1 && quotedGroups[0] === "" && quotedGroups.length % 2 === 1)
      return spec.name = quotedGroups[1], tokens.name = `"${quotedGroups[1]}"`, [tokens.postName, tokens.description] = splitSpace(source.slice(tokens.name.length)), spec;
    let brackets = 0, name = "", optional = !1, defaultValue;
    for (let ch of source) {
      if (brackets === 0 && isSpace(ch))
        break;
      ch === "[" && brackets++, ch === "]" && brackets--, name += ch;
    }
    if (brackets !== 0)
      return spec.problems.push({
        code: "spec:name:unpaired-brackets",
        message: "unpaired brackets",
        line: spec.source[0].number,
        critical: !0
      }), spec;
    let nameToken = name;
    if (name[0] === "[" && name[name.length - 1] === "]") {
      optional = !0, name = name.slice(1, -1);
      let parts = name.split("=");
      if (name = parts[0].trim(), parts[1] !== void 0 && (defaultValue = parts.slice(1).join("=").trim()), name === "")
        return spec.problems.push({
          code: "spec:name:empty-name",
          message: "empty name",
          line: spec.source[0].number,
          critical: !0
        }), spec;
      if (defaultValue === "")
        return spec.problems.push({
          code: "spec:name:empty-default",
          message: "empty default value",
          line: spec.source[0].number,
          critical: !0
        }), spec;
      if (!isQuoted(defaultValue) && /=(?!>)/.test(defaultValue))
        return spec.problems.push({
          code: "spec:name:invalid-default",
          message: "invalid default value syntax",
          line: spec.source[0].number,
          critical: !0
        }), spec;
    }
    return spec.optional = optional, spec.name = name, tokens.name = nameToken, defaultValue !== void 0 && (spec.default = defaultValue), [tokens.postName, tokens.description] = splitSpace(source.slice(tokens.name.length)), spec;
  };
}

// ../../node_modules/comment-parser/es6/parser/tokenizers/description.js
function descriptionTokenizer(spacing = "compact", markers = Markers) {
  let join2 = getJoiner2(spacing);
  return (spec) => (spec.description = join2(spec.source, markers), spec);
}
function getJoiner2(spacing) {
  return spacing === "compact" ? compactJoiner : spacing === "preserve" ? preserveJoiner : spacing;
}
function compactJoiner(lines, markers = Markers) {
  return lines.map(({ tokens: { description } }) => description.trim()).filter((description) => description !== "").join(" ");
}
var lineNo = (num, { tokens }, i) => tokens.type === "" ? num : i, getDescription = ({ tokens }) => (tokens.delimiter === "" ? tokens.start : tokens.postDelimiter.slice(1)) + tokens.description;
function preserveJoiner(lines, markers = Markers) {
  if (lines.length === 0)
    return "";
  lines[0].tokens.description === "" && lines[0].tokens.delimiter === markers.start && (lines = lines.slice(1));
  let lastLine = lines[lines.length - 1];
  return lastLine !== void 0 && lastLine.tokens.description === "" && lastLine.tokens.end.endsWith(markers.end) && (lines = lines.slice(0, -1)), lines = lines.slice(lines.reduce(lineNo, 0)), lines.map(getDescription).join(`
`);
}

// ../../node_modules/comment-parser/es6/parser/index.js
function getParser4({ startLine = 0, fence = "```", spacing = "compact", markers = Markers, tokenizers = [
  tagTokenizer(),
  typeTokenizer(spacing),
  nameTokenizer(),
  descriptionTokenizer(spacing)
] } = {}) {
  if (startLine < 0 || startLine % 1 > 0)
    throw new Error("Invalid startLine");
  let parseSource = getParser2({ startLine, markers }), parseBlock = getParser({ fence }), parseSpec = getParser3({ tokenizers }), joinDescription = getJoiner2(spacing);
  return function(source) {
    let blocks = [];
    for (let line of splitLines(source)) {
      let lines = parseSource(line);
      if (lines === null)
        continue;
      let sections = parseBlock(lines), specs = sections.slice(1).map(parseSpec);
      blocks.push({
        description: joinDescription(sections[0], markers),
        tags: specs,
        source: lines,
        problems: specs.reduce((acc, spec) => acc.concat(spec.problems), [])
      });
    }
    return blocks;
  };
}

// ../../node_modules/comment-parser/es6/stringifier/index.js
function join(tokens) {
  return tokens.start + tokens.delimiter + tokens.postDelimiter + tokens.tag + tokens.postTag + tokens.type + tokens.postType + tokens.name + tokens.postName + tokens.description + tokens.end + tokens.lineEnd;
}
function getStringifier() {
  return (block) => block.source.map(({ tokens }) => join(tokens)).join(`
`);
}

// ../../node_modules/comment-parser/es6/stringifier/inspect.js
var zeroWidth = {
  line: 0,
  start: 0,
  delimiter: 0,
  postDelimiter: 0,
  tag: 0,
  postTag: 0,
  name: 0,
  postName: 0,
  type: 0,
  postType: 0,
  description: 0,
  end: 0,
  lineEnd: 0
};
var fields = Object.keys(zeroWidth);

// ../../node_modules/comment-parser/es6/index.js
function parse(source, options = {}) {
  return getParser4(options)(source);
}
var stringify = getStringifier();

// src/csf-tools/jsdoc.ts
function groupByTag(specs) {
  let groups = /* @__PURE__ */ new Map();
  for (let spec of specs) {
    let group = groups.get(spec.tag);
    group ? group.push(spec) : groups.set(spec.tag, [spec]);
  }
  return groups;
}
function hasTags(tags) {
  return !!tags && Object.keys(tags).length > 0;
}
function mergeTags(docgenJsDocTags, extractedTags) {
  return hasTags(docgenJsDocTags) ? { ...extractedTags ?? {}, ...docgenJsDocTags } : extractedTags ?? {};
}
function extractJSDocInfo(jsdocComment) {
  let jsDoc = ["/**", ...jsdocComment.split(`
`).map((line) => ` * ${line}`), " */"].join(`
`), description = parse(jsDoc, { spacing: "preserve" })[0].description, parsed = parse(jsDoc, { spacing: "compact" });
  return {
    description,
    tags: Object.fromEntries(
      Array.from(groupByTag(parsed[0].tags), ([tag, specs]) => [
        tag,
        specs.map(
          (spec) => ((spec.type ? `{${spec.type}} ` : "") + `${spec.name} ${spec.description}`).trim()
        )
      ])
    )
  };
}
function extractComponentDescription(metaJsDoc, docgenDescription, docgenJsDocTags) {
  let jsdocComment = metaJsDoc || docgenDescription, extracted = jsdocComment ? extractJSDocInfo(jsdocComment) : void 0, tags = mergeTags(docgenJsDocTags, extracted?.tags), description = extracted?.description;
  return {
    description: ((tags?.describe?.[0] || tags?.desc?.[0]) ?? description)?.trim(),
    summary: tags.summary?.[0],
    jsDocTags: tags
  };
}

// src/csf-tools/enrichCsf.ts
import { generate as generate3, types as t3 } from "storybook/internal/babel";
var enrichCsfStory = (csf, csfSource, key, options) => {
  let storyExport = csfSource.getStoryExport(key), isCsfFactory = t3.isCallExpression(storyExport) && t3.isMemberExpression(storyExport.callee) && t3.isIdentifier(storyExport.callee.object) && storyExport.callee.object.name === "meta", source = !options?.disableSource && extractSource(storyExport), description = !options?.disableDescription && extractDescription(csfSource._storyStatements[key]), parameters = [], baseStoryObject = isCsfFactory ? t3.memberExpression(t3.identifier(key), t3.identifier("input")) : t3.identifier(key), originalParameters = t3.memberExpression(baseStoryObject, t3.identifier("parameters"));
  parameters.push(t3.spreadElement(originalParameters));
  let optionalDocs = t3.optionalMemberExpression(
    originalParameters,
    t3.identifier("docs"),
    !1,
    !0
  ), extraDocsParameters = [];
  if (source) {
    let optionalSource = t3.optionalMemberExpression(
      optionalDocs,
      t3.identifier("source"),
      !1,
      !0
    );
    extraDocsParameters.push(
      t3.objectProperty(
        t3.identifier("source"),
        t3.objectExpression([
          t3.objectProperty(t3.identifier("originalSource"), t3.stringLiteral(source)),
          t3.spreadElement(optionalSource)
        ])
      )
    );
  }
  if (description) {
    let optionalDescription = t3.optionalMemberExpression(
      optionalDocs,
      t3.identifier("description"),
      !1,
      !0
    );
    extraDocsParameters.push(
      t3.objectProperty(
        t3.identifier("description"),
        t3.objectExpression([
          t3.objectProperty(t3.identifier("story"), t3.stringLiteral(description)),
          t3.spreadElement(optionalDescription)
        ])
      )
    );
  }
  if (extraDocsParameters.length > 0) {
    parameters.push(
      t3.objectProperty(
        t3.identifier("docs"),
        t3.objectExpression([t3.spreadElement(optionalDocs), ...extraDocsParameters])
      )
    );
    let addParameter = t3.expressionStatement(
      t3.assignmentExpression("=", originalParameters, t3.objectExpression(parameters))
    );
    csf._ast.program.body.push(addParameter);
  }
}, addComponentDescription = (node, path2, value) => {
  if (!path2.length) {
    node.properties.find(
      (p) => t3.isObjectProperty(p) && t3.isIdentifier(p.key) && p.key.name === "component"
    ) || node.properties.unshift(value);
    return;
  }
  let [first, ...rest] = path2, existing = node.properties.find(
    (p) => t3.isObjectProperty(p) && t3.isIdentifier(p.key) && p.key.name === first && t3.isObjectExpression(p.value)
  ), subNode;
  existing ? subNode = existing.value : (subNode = t3.objectExpression([]), node.properties.push(t3.objectProperty(t3.identifier(first), subNode))), addComponentDescription(subNode, rest, value);
}, enrichCsfMeta = (csf, csfSource, options) => {
  let description = !options?.disableDescription && extractDescription(csfSource._metaStatement);
  if (description) {
    let metaNode = csf._metaNode;
    metaNode && t3.isObjectExpression(metaNode) && addComponentDescription(
      metaNode,
      ["parameters", "docs", "description"],
      t3.objectProperty(t3.identifier("component"), t3.stringLiteral(description))
    );
  }
}, enrichCsf = async (csf, csfSource, options) => {
  enrichCsfMeta(csf, csfSource, options), await options?.enrichCsf?.(csf, csfSource), Object.keys(csf._storyExports).forEach((key) => {
    enrichCsfStory(csf, csfSource, key, options);
  });
}, extractSource = (node) => {
  let src = t3.isVariableDeclarator(node) ? node.init : node, { code } = generate3(src, {});
  return code;
}, extractDescription = (node) => node?.leadingComments ? node.leadingComments.map((comment) => comment.type === "CommentLine" || !comment.value.startsWith("*") ? null : comment.value.split(`
`).map((line) => line.replace(/^(\s+)?(\*+)?(\s)?/, "")).join(`
`).trim()).filter(Boolean).join(`
`) : "";

// src/csf-tools/story-shape/import-statements.ts
import { babelParse as babelParse3, babelPrint, types as t4 } from "storybook/internal/babel";
function resolveComponentImport(componentName, bindings) {
  let dot = componentName.indexOf("."), base = dot === -1 ? componentName : componentName.slice(0, dot), member = dot === -1 ? void 0 : componentName.slice(dot + 1), binding = bindings.get(base);
  if (!binding)
    return { componentName, ...member ? { member } : {} };
  let isNamespace = binding.importName === "*";
  return {
    componentName,
    ...member ? { member } : {},
    localImportName: base,
    importId: binding.importId,
    importName: isNamespace && member ? member : binding.importName,
    ...isNamespace ? { namespace: base } : {}
  };
}
function parseImportOverride(code) {
  let declaration;
  try {
    declaration = babelParse3(code).program.body.find(
      (node) => t4.isImportDeclaration(node)
    );
  } catch {
    return;
  }
  if (!declaration)
    return;
  let source = declaration.source.value, specifier = (declaration.specifiers ?? []).find((s) => !isTypeSpecifier(s));
  return t4.isImportNamespaceSpecifier(specifier) ? { source, specifier: { kind: "namespace", local: specifier.local.name } } : t4.isImportDefaultSpecifier(specifier) ? { source, specifier: { kind: "default" } } : t4.isImportSpecifier(specifier) ? { source, specifier: { kind: "named", imported: importedName(specifier.imported) } } : { source };
}
function addUniqueBy(list, item, eq) {
  list.find(eq) || list.push(item);
}
function addNamed(bucket, local, imported) {
  addUniqueBy(
    bucket.named,
    t4.importSpecifier(t4.identifier(local), t4.identifier(imported)),
    (n) => n.local.name === local && importedName(n.imported) === imported
  );
}
function addSingle(list, name) {
  addUniqueBy(list, t4.identifier(name), (n) => n.name === name);
}
function collectSpecifier(bucket, ref, source, override) {
  let rewritten = source !== ref.importId;
  if (override?.specifier) {
    let { specifier } = override;
    if (specifier.kind === "namespace") {
      addSingle(bucket.namespaces, specifier.local);
      return;
    }
    if (!ref.localImportName)
      return;
    specifier.kind === "default" ? addSingle(bucket.defaults, ref.localImportName) : addNamed(bucket, ref.localImportName, specifier.imported);
    return;
  }
  if (ref.namespace) {
    let member = rewritten ? ref.importName : void 0;
    member && member !== "*" && !member.includes(".") ? addNamed(bucket, member, member) : addSingle(bucket.namespaces, ref.namespace);
    return;
  }
  if (ref.localImportName) {
    if (ref.importName === "default") {
      rewritten ? addNamed(bucket, ref.localImportName, ref.localImportName) : addSingle(bucket.defaults, ref.localImportName);
      return;
    }
    ref.importName && addNamed(bucket, ref.localImportName, ref.importName);
  }
}
function printBucket({ source, defaults, namespaces, named }) {
  let print = (specifiers) => babelPrint(t4.importDeclaration(specifiers, source)), extraDefaults = defaults.slice(1).map((d) => print([t4.importDefaultSpecifier(d)]));
  if (namespaces.length > 0) {
    let first2 = [];
    return defaults[0] && first2.push(t4.importDefaultSpecifier(defaults[0])), first2.push(t4.importNamespaceSpecifier(namespaces[0])), [
      print(first2),
      ...named.length > 0 ? [print(named)] : [],
      ...extraDefaults,
      ...namespaces.slice(1).map((ns) => print([t4.importNamespaceSpecifier(ns)]))
    ];
  }
  if (defaults.length === 0 && named.length === 0)
    return [];
  let first = [];
  return defaults[0] && first.push(t4.importDefaultSpecifier(defaults[0])), first.push(...named), [print(first), ...extraDefaults];
}
function buildImportStatements({
  refs,
  packageName
}) {
  let buckets = /* @__PURE__ */ new Map();
  return refs.forEach((ref) => {
    if (!ref.importId)
      return;
    let override = ref.importOverride ? parseImportOverride(ref.importOverride) : void 0, source = override?.source ?? (packageName && !ref.isPackage ? packageName : ref.importId), bucket = buckets.get(source);
    bucket || (bucket = { source: t4.stringLiteral(source), defaults: [], namespaces: [], named: [] }, buckets.set(source, bucket)), collectSpecifier(bucket, ref, source, override);
  }), Array.from(buckets.values()).flatMap(printBucket);
}

// src/csf-tools/story-shape/jsdoc.ts
function jsDocTagsForPath(path2) {
  let statement = path2?.getStatementParent(), jsdocComment = statement ? extractDescription(statement.node) : "";
  return jsdocComment ? extractJSDocInfo(jsdocComment).tags ?? {} : {};
}
function extractStoryJSDocInfo(storyStatement) {
  let { description, summary } = extractComponentDescription(
    extractDescription(storyStatement) || void 0,
    void 0
  );
  return { description, summary };
}

// src/csf-tools/story-shape/normalize-story.ts
function normalizeStoryDeclaration(storyDeclaration) {
  let storyPath = declarationExpression(storyDeclaration), normalizedPath = bindInitializer(storyDeclaration, storyPath) ?? factoryArgumentExpression(storyPath), unwrappedPath = unwrapTypeExpression(normalizedPath);
  return classifyStoryPath(unwrappedPath);
}
function declarationExpression(storyDeclaration) {
  if (storyDeclaration.isFunctionDeclaration())
    return storyDeclaration;
  if (storyDeclaration.isVariableDeclarator()) {
    let init = storyDeclaration.get("init");
    if (!init.isExpression())
      throw storyDeclaration.buildCodeFrameError("Expected story initializer to be an expression");
    return init;
  }
  throw storyDeclaration.buildCodeFrameError(
    "Expected story to be a function or variable declaration"
  );
}
function bindInitializer(storyDeclaration, storyPath) {
  if (!storyPath.isCallExpression() || !isCanonicalCsf2BindCall(storyPath.node))
    return null;
  let callee = storyPath.get("callee");
  if (!callee.isMemberExpression())
    return null;
  let obj = callee.get("object");
  return obj.isIdentifier() ? resolveIdentifierInit(storyDeclaration, obj) : null;
}
function factoryArgumentExpression(storyPath) {
  if (!storyPath.isCallExpression() || !isCsfFactoryCall(storyPath.node))
    return storyPath;
  let args = storyPath.get("arguments");
  if (args.length === 0)
    return storyPath;
  if (args.length !== 1 || !args[0].isExpression())
    throw storyPath.buildCodeFrameError("Could not evaluate story expression");
  return args[0];
}
function unwrapTypeExpression(storyPath) {
  return storyPath.isTSSatisfiesExpression() || storyPath.isTSAsExpression() ? storyPath.get("expression") : storyPath;
}
function classifyStoryPath(storyPath) {
  if (storyPath.isObjectExpression())
    return { type: "config", path: storyPath };
  if (storyPath.isArrowFunctionExpression() || storyPath.isFunctionExpression() || storyPath.isFunctionDeclaration())
    return { type: "fn", path: storyPath };
  if (storyPath.isCallExpression() && isCsfFactoryCall(storyPath.node) && storyPath.node.arguments.length === 0)
    return { type: "emptyConfig", path: storyPath };
  throw storyPath.buildCodeFrameError(
    "Expected story to be csf factory, function or an object expression"
  );
}

// src/csf-tools/story-shape/resolve-story-args.ts
import { types as t5 } from "storybook/internal/babel";
function createStoryArgsResolver(csf, references) {
  let ctx = {
    program: csf._file.path,
    filePath: csf._file.opts.filename ?? "",
    ...references
  }, metaNode = csf._metaNode, metaMembers = metaNode && t5.isObjectExpression(metaNode) ? resolveObjectMembers(metaNode, ctx) : { properties: {}, shadowed: [], unresolved: [] }, metaArgs = resolveArgsRecord(metaMembers.properties.args, ctx);
  return {
    ctx,
    resolve: (storyExport) => {
      let localName = csf._stories[storyExport]?.localName ?? storyExport, storyMembers = resolveBindingMembers(ctx, localName) ?? {
        properties: {},
        shadowed: [],
        unresolved: [sourceOf(csf._storyStatements[storyExport] ?? t5.identifier(storyExport))]
      }, storyArgs = resolveArgsRecord(storyMembers.properties.args, ctx), args = {}, imports = [], unresolved = [
        ...metaMembers.unresolved,
        ...metaArgs.unresolved,
        ...storyMembers.unresolved,
        ...storyArgs.unresolved
      ];
      for (let [key, node] of Object.entries({
        ...metaArgs.properties,
        ...storyArgs.properties
      })) {
        let value = resolveArgValue(node, ctx);
        args[key] = value.node, imports.push(...value.imports), unresolved.push(...value.unresolved);
      }
      return { args, imports, unresolved, storyMembers, metaMembers };
    }
  };
}
var unresolvedWarning = (unresolved) => {
  let sources = [...new Set(unresolved)];
  return sources.length === 0 ? void 0 : `Incomplete snippet: ${sources.map((source) => `\`${source}\``).join(", ")} could not be resolved statically.`;
}, noSnippetWarning = (unresolved) => {
  let sources = [...new Set(unresolved)];
  return sources.length === 0 ? void 0 : `No static snippet: ${sources.map((source) => `\`${source}\``).join(", ")} could not be resolved statically.`;
};

// src/csf-tools/story-shape/render.ts
var isRenderFunction = (path2) => path2.isArrowFunctionExpression() || path2.isFunctionExpression() || path2.isFunctionDeclaration();
function resolveRenderFunction(config, storyDeclaration, references) {
  let properties = config?.get("properties") ?? [], renderIndex = -1;
  for (let index = properties.length - 1; index >= 0; index -= 1) {
    let property = properties[index];
    if ((property.isObjectProperty() || property.isObjectMethod()) && keyOf(property.node) === "render") {
      renderIndex = index;
      break;
    }
  }
  let throughSpreads = () => config && references ? renderFromMembers(
    resolveObjectMembers(config.node, references),
    references,
    storyDeclaration
  ) : { kind: "unresolved" };
  if (renderIndex === -1)
    return properties.some((property) => property.isSpreadElement()) ? throughSpreads() : { kind: "missing" };
  let resolved = resolveRenderProperty(properties[renderIndex], storyDeclaration);
  if (properties.some((property, index) => index > renderIndex && property.isSpreadElement())) {
    let read = throughSpreads();
    return read.kind !== "unresolved" ? read : resolved.kind === "resolved" ? { kind: "unresolved", shadowedRender: resolved.path } : { kind: "unresolved" };
  }
  return resolved;
}
function renderFromMembers(members, references, storyDeclaration) {
  if (members.unresolved.length > 0)
    return { kind: "unresolved" };
  let node = members.properties.render;
  if (node === void 0)
    return { kind: "missing" };
  let path2 = pathForNode(references.program, node);
  if (!path2)
    return { kind: "unresolved" };
  if (path2.isObjectMethod())
    return path2.node.kind === "method" && !path2.node.generator ? { kind: "resolved", path: path2 } : { kind: "unresolved" };
  if (path2.isIdentifier()) {
    let resolved = resolveIdentifierInit(storyDeclaration, path2);
    return resolved && isRenderFunction(resolved) ? { kind: "resolved", path: resolved } : { kind: "unresolved" };
  }
  return isRenderFunction(path2) ? { kind: "resolved", path: path2 } : { kind: "unresolved" };
}
function resolveRenderProperty(renderProperty, storyDeclaration) {
  if (renderProperty.isObjectMethod())
    return renderProperty.node.kind === "method" && !renderProperty.node.generator ? { kind: "resolved", path: renderProperty } : { kind: "unresolved" };
  let renderPath = renderProperty.get("value");
  if (renderPath.isIdentifier()) {
    let resolved = resolveIdentifierInit(storyDeclaration, renderPath);
    return resolved && isRenderFunction(resolved) ? { kind: "resolved", path: resolved } : { kind: "unresolved" };
  }
  if (!isRenderFunction(renderPath))
    throw renderPath.buildCodeFrameError(
      "Expected render to be an arrow function or function expression"
    );
  return { kind: "resolved", path: renderPath };
}

// src/csf-tools/index.ts
import { babelParse as babelParse5 } from "storybook/internal/babel";

// src/csf-tools/vitest-plugin/transformer.ts
var import_ts_dedent3 = __toESM(require_dist(), 1);
import { types as t6 } from "storybook/internal/babel";
import { getStoryTitle } from "storybook/internal/common";
import { combineTags } from "storybook/internal/csf/csf-utils";
import { logger as logger3 } from "storybook/internal/node-logger";
var isValidTest = (storyTags, tagsFilter) => !(tagsFilter.include.length && !tagsFilter.include.some((tag) => storyTags?.includes(tag)) || tagsFilter.exclude.some((tag) => storyTags?.includes(tag))), DOUBLE_SPACES = "  ", getLiteralWithZeroWidthSpace = (testTitle) => t6.stringLiteral(`${testTitle}${DOUBLE_SPACES}`);
function createTestGuardDeclaration(scope, expectId, convertToFilePathId) {
  let isRunningFromThisFileId = scope.generateUidIdentifier("isRunningFromThisFile"), testPathProperty = t6.memberExpression(
    t6.callExpression(t6.memberExpression(expectId, t6.identifier("getState")), []),
    t6.identifier("testPath")
  ), filePathProperty = t6.memberExpression(
    t6.memberExpression(t6.identifier("globalThis"), t6.identifier("__vitest_worker__")),
    t6.identifier("filepath")
  ), nullishCoalescingExpression = t6.logicalExpression(
    "??",
    // TODO: switch order of testPathProperty and filePathProperty when the bug is fixed
    // https://github.com/vitest-dev/vitest/issues/6367 (or probably just use testPathProperty)
    filePathProperty,
    testPathProperty
  ), includesCall = t6.callExpression(
    t6.memberExpression(
      t6.callExpression(convertToFilePathId, [
        t6.memberExpression(
          t6.memberExpression(t6.identifier("import"), t6.identifier("meta")),
          t6.identifier("url")
        )
      ]),
      t6.identifier("includes")
    ),
    [nullishCoalescingExpression]
  );
  return {
    declaration: t6.variableDeclaration("const", [
      t6.variableDeclarator(isRunningFromThisFileId, includesCall)
    ]),
    identifier: isRunningFromThisFileId
  };
}
async function vitestTransform({
  code,
  fileName,
  configDir,
  stories,
  tagsFilter,
  previewLevelTags = []
}) {
  let parsed = loadCsf(code, {
    fileName,
    transformInlineMeta: !0,
    makeTitle: (title) => {
      let result = getStoryTitle({
        storyFilePath: fileName,
        configDir,
        stories,
        userTitle: title
      }) || "unknown";
      return result === "unknown" && logger3.warn(
        import_ts_dedent3.dedent`
            [Storybook]: Could not calculate story title for "${fileName}".
            Please make sure that this file matches the globs included in the "stories" field in your Storybook configuration at "${configDir}".
          `
      ), result;
    }
  }).parse(), ast = parsed._ast, metaExportName = parsed._metaVariableName, metaNode = parsed._metaNode, metaTitleProperty = metaNode.properties.find(
    (prop) => t6.isObjectProperty(prop) && t6.isIdentifier(prop.key) && prop.key.name === "title"
  ), metaTitle = t6.stringLiteral(parsed._meta?.title || "unknown");
  if (metaTitleProperty ? t6.isObjectProperty(metaTitleProperty) && (metaTitleProperty.value = metaTitle) : metaNode.properties.push(t6.objectProperty(t6.identifier("title"), metaTitle)), !metaNode || !parsed._meta)
    throw new Error(
      `The Storybook vitest plugin could not detect the meta (default export) object in the story file. 

Please make sure you have a default export with the meta object. If you are using a different export format that is not supported, please file an issue with details about your use case.`
    );
  let validStories = {};
  Object.keys(parsed._stories).forEach((key) => {
    let finalTags = combineTags(
      "test",
      "dev",
      ...previewLevelTags,
      ...parsed.meta?.tags || [],
      ...parsed._stories[key].tags || []
    );
    isValidTest(finalTags, tagsFilter) && (validStories[key] = parsed._storyStatements[key]);
  });
  let vitestTestId = parsed._file.path.scope.generateUidIdentifier("test"), vitestDescribeId = parsed._file.path.scope.generateUidIdentifier("describe");
  if (Object.keys(validStories).length === 0) {
    let describeSkipBlock = t6.expressionStatement(
      t6.callExpression(t6.memberExpression(vitestDescribeId, t6.identifier("skip")), [
        t6.stringLiteral("No valid tests found")
      ])
    );
    ast.program.body.push(describeSkipBlock);
    let imports2 = [
      t6.importDeclaration(
        [
          t6.importSpecifier(vitestTestId, t6.identifier("test")),
          t6.importSpecifier(vitestDescribeId, t6.identifier("describe"))
        ],
        t6.stringLiteral("vitest")
      )
    ];
    return ast.program.body.unshift(...imports2), formatCsf(parsed, { sourceMaps: !0, sourceFileName: fileName }, code);
  }
  let vitestExpectId = parsed._file.path.scope.generateUidIdentifier("expect"), testStoryId = parsed._file.path.scope.generateUidIdentifier("testStory"), skipTagsId = t6.identifier(JSON.stringify(tagsFilter.skip)), componentPathLiteral = parsed._rawComponentPath ? t6.stringLiteral(parsed._rawComponentPath) : null, componentNameLiteral = null;
  parsed._componentImportSpecifier && (t6.isImportSpecifier(parsed._componentImportSpecifier) || t6.isImportDefaultSpecifier(parsed._componentImportSpecifier)) && (componentNameLiteral = t6.stringLiteral(parsed._componentImportSpecifier.local.name));
  let { declaration: isRunningFromThisFileDeclaration, identifier: isRunningFromThisFileId } = createTestGuardDeclaration(
    parsed._file.path.scope,
    vitestExpectId,
    t6.identifier("convertToFilePath")
  );
  ast.program.body.push(isRunningFromThisFileDeclaration);
  let getTestStatementForStory = ({
    localName,
    exportName,
    testTitle,
    node,
    overrideSourcemap = !0,
    storyId
  }) => {
    let objectProperties = [
      t6.objectProperty(t6.identifier("exportName"), t6.stringLiteral(exportName)),
      t6.objectProperty(t6.identifier("story"), t6.identifier(localName)),
      t6.objectProperty(t6.identifier("meta"), t6.identifier(metaExportName)),
      t6.objectProperty(t6.identifier("skipTags"), skipTagsId),
      t6.objectProperty(t6.identifier("storyId"), t6.stringLiteral(storyId))
    ];
    componentPathLiteral && objectProperties.push(t6.objectProperty(t6.identifier("componentPath"), componentPathLiteral)), componentNameLiteral && objectProperties.push(t6.objectProperty(t6.identifier("componentName"), componentNameLiteral));
    let testStoryCall = t6.expressionStatement(
      t6.callExpression(vitestTestId, [
        t6.stringLiteral(testTitle),
        t6.callExpression(testStoryId, [t6.objectExpression(objectProperties)])
      ])
    );
    return overrideSourcemap && (testStoryCall.loc = node.loc), testStoryCall;
  }, getDescribeStatementForStory = (options) => {
    let { localName, describeTitle, exportName, tests, node, parentStoryId } = options, describeBlock = t6.callExpression(vitestDescribeId, [
      getLiteralWithZeroWidthSpace(describeTitle),
      t6.arrowFunctionExpression(
        [],
        t6.blockStatement([
          getTestStatementForStory({
            ...options,
            testTitle: "base story",
            overrideSourcemap: !1,
            storyId: parentStoryId
          }),
          ...tests.map(({ name: testName, node: testNode, id: storyId }) => {
            let objectProperties = [
              t6.objectProperty(t6.identifier("exportName"), t6.stringLiteral(exportName)),
              t6.objectProperty(t6.identifier("story"), t6.identifier(localName)),
              t6.objectProperty(t6.identifier("meta"), t6.identifier(metaExportName)),
              t6.objectProperty(t6.identifier("skipTags"), skipTagsId),
              t6.objectProperty(t6.identifier("storyId"), t6.stringLiteral(storyId))
            ];
            componentPathLiteral && objectProperties.push(
              t6.objectProperty(t6.identifier("componentPath"), componentPathLiteral)
            ), componentNameLiteral && objectProperties.push(
              t6.objectProperty(t6.identifier("componentName"), componentNameLiteral)
            ), testName && objectProperties.push(
              t6.objectProperty(t6.identifier("testName"), t6.stringLiteral(testName))
            );
            let testStatement = t6.expressionStatement(
              t6.callExpression(vitestTestId, [
                t6.stringLiteral(testName),
                t6.callExpression(testStoryId, [t6.objectExpression(objectProperties)])
              ])
            );
            return testStatement.loc = testNode.loc, testStatement;
          })
        ])
      )
    ]);
    return describeBlock.loc = node.loc, t6.expressionStatement(describeBlock);
  }, storyTestStatements = Object.entries(validStories).map(([exportName, node]) => {
    if (node === null) {
      logger3.warn(
        import_ts_dedent3.dedent`
            [Storybook]: Could not transform "${exportName}" story into test at "${fileName}".
            Please make sure to define stories in the same file and not re-export stories coming from other files".
          `
      );
      return;
    }
    let localName = parsed._stories[exportName].localName ?? exportName, testTitle = parsed._stories[exportName].name ?? exportName, storyId = parsed._stories[exportName].id, tests = parsed.getStoryTests(exportName);
    return tests?.length > 0 ? getDescribeStatementForStory({
      localName,
      describeTitle: testTitle,
      exportName,
      tests,
      node,
      parentStoryId: storyId
    }) : getTestStatementForStory({
      testTitle,
      localName,
      exportName,
      node,
      storyId
    });
  }).filter((st) => !!st), testBlock = t6.ifStatement(isRunningFromThisFileId, t6.blockStatement(storyTestStatements));
  ast.program.body.push(testBlock);
  let hasTests = Object.keys(validStories).some(
    (exportName) => parsed.getStoryTests(exportName).length > 0
  ), imports = [
    t6.importDeclaration(
      [
        t6.importSpecifier(vitestTestId, t6.identifier("test")),
        t6.importSpecifier(vitestExpectId, t6.identifier("expect")),
        ...hasTests ? [t6.importSpecifier(vitestDescribeId, t6.identifier("describe"))] : []
      ],
      t6.stringLiteral("vitest")
    ),
    t6.importDeclaration(
      [
        t6.importSpecifier(testStoryId, t6.identifier("testStory")),
        t6.importSpecifier(t6.identifier("convertToFilePath"), t6.identifier("convertToFilePath"))
      ],
      t6.stringLiteral("@storybook/addon-vitest/internal/test-utils")
    )
  ];
  return ast.program.body.unshift(...imports), formatCsf(parsed, { sourceMaps: !0, sourceFileName: fileName }, code);
}

// src/csf-tools/vitest-plugin/component-transformer.ts
import path from "node:path";
import {
  BabelFileClass,
  babelParse as babelParse4,
  generate as generate4,
  types as t7,
  traverse as traverse3
} from "storybook/internal/babel";

// src/core-server/utils/get-dummy-args-from-argtypes.ts
var STORYBOOK_FN_PLACEHOLDER = "[[STORYBOOK_FN_PLACEHOLDER]]";
function generateDummyArgsFromArgTypes(argTypes, options = {}) {
  let required = {}, optional = {};
  for (let [propName, argType] of Object.entries(argTypes)) {
    let isRequired = argType.type && typeof argType.type == "object" && argType.type.required, dummyValue;
    if (typeof argType.type == "string") {
      let sbType = { name: argType.type };
      dummyValue = generateDummyValueFromSBType(sbType, propName, options);
    } else argType.type && typeof argType.type == "object" ? dummyValue = generateDummyValueFromSBType(argType.type, propName, options) : dummyValue = void 0;
    isRequired ? required[propName] = dummyValue : optional[propName] = dummyValue;
  }
  return { required, optional };
}
function tokenize(name) {
  return name ? name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-]+/g, " ").split(" ").map((t8) => t8.toLowerCase()).filter(Boolean) : [];
}
function hasAny(tokens, set) {
  return tokens.some((t8) => set.has(t8));
}
var URL_TOKENS = /* @__PURE__ */ new Set(["url", "href", "link"]), IMAGE_TOKENS = /* @__PURE__ */ new Set(["image", "img", "photo", "avatar", "logo"]), EMAIL_TOKENS = /* @__PURE__ */ new Set(["email", "e-mail", "mail"]), PHONE_TOKENS = /* @__PURE__ */ new Set(["phone", "tel", "telephone", "mobile", "cell"]), COLOR_TOKENS = /* @__PURE__ */ new Set(["color", "background", "bg"]), DATE_TOKENS = /* @__PURE__ */ new Set(["date", "at", "time", "timestamp"]), NEGATIVE_IMAGE_TOKENS = /* @__PURE__ */ new Set([
  "size",
  "width",
  "height",
  "ratio",
  "count",
  "status",
  "loading",
  "config",
  "options",
  "variant"
]);
function getMostLikelyTypeFromTokens(tokens) {
  let score = {};
  for (let token of tokens)
    IMAGE_TOKENS.has(token) && (score.image = (score.image ?? 0) + 3), URL_TOKENS.has(token) && (score.url = (score.url ?? 0) + 2), EMAIL_TOKENS.has(token) && (score.email = (score.email ?? 0) + 3), PHONE_TOKENS.has(token) && (score.phone = (score.phone ?? 0) + 3);
  hasAny(tokens, NEGATIVE_IMAGE_TOKENS) && (score.image = (score.image ?? 0) - 4);
  let best = null, bestScore = 0;
  for (let [kind, value] of Object.entries(score))
    value > bestScore && (bestScore = value, best = kind);
  return best;
}
function normalizeStringLiteral(value) {
  return typeof value != "string" ? value : value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'") ? value.slice(1, -1) : value;
}
function generateDummyValueFromSBType(sbType, propName, options) {
  switch (sbType.name) {
    case "boolean":
      return !0;
    case "number":
      return 0;
    case "string": {
      let name = propName ?? "", tokens = tokenize(name);
      if (hasAny(tokens, COLOR_TOKENS))
        return "#ff4785";
      if (hasAny(tokens, DATE_TOKENS))
        return (/* @__PURE__ */ new Date()).toLocaleDateString();
      let mostLikelyType = getMostLikelyTypeFromTokens(tokens);
      if (options?.skipUrlGeneration && (mostLikelyType === "image" || mostLikelyType === "url"))
        return name;
      switch (mostLikelyType) {
        case "image":
          return "https://placehold.co/600x400?text=Storybook";
        case "url":
          return "https://example.com";
        case "email":
          return "storybook@example.com";
        case "phone":
          return "1234567890";
        default:
          return name ?? "Hello world";
      }
    }
    case "date":
      return /* @__PURE__ */ new Date();
    case "node":
      return propName ?? "Hello world";
    case "function":
      return STORYBOOK_FN_PLACEHOLDER;
    case "literal":
      return normalizeStringLiteral(sbType.value);
    case "object": {
      let result = {};
      for (let [key, valueType] of Object.entries(sbType.value))
        result[key] = generateDummyValueFromSBType(valueType, key, options);
      return result;
    }
    case "union": {
      if (!sbType.value?.length)
        return "";
      let literalType = sbType.value.find((t8) => t8.name === "literal");
      return literalType?.name === "literal" ? normalizeStringLiteral(literalType.value) : generateDummyValueFromSBType(sbType.value[0], propName, options);
    }
    case "array": {
      let v = sbType.value;
      return v?.length < 1 || v[0]?.name === "other" ? [] : [generateDummyValueFromSBType(v[0], propName, options)];
    }
    case "tuple":
      return sbType.value.map((el) => generateDummyValueFromSBType(el, void 0, options));
    case "enum":
      return sbType.value[0] ?? propName;
    case "intersection": {
      let objectTypes = sbType.value.filter((t8) => t8.name === "object");
      if (objectTypes.length > 0) {
        let result = {};
        return objectTypes.forEach((objType) => {
          objType.name === "object" && Object.entries(objType.value).forEach(([key, type]) => {
            result[key] = generateDummyValueFromSBType(type, key, options);
          });
        }), result;
      }
      return {};
    }
    case "other": {
      let value = sbType.value;
      return value?.startsWith("React") || value?.includes("Event") || value?.includes("Element") ? STORYBOOK_FN_PLACEHOLDER : value === "null" ? null : value === "void" || value === "undefined" ? void 0 : null;
    }
  }
}

// src/csf-tools/vitest-plugin/component-transformer.ts
var VITEST_IMPORT_SOURCE = "vitest", TEST_UTILS_IMPORT_SOURCE = "@storybook/addon-vitest/internal/test-utils", STORYBOOK_TEST_IMPORT_SOURCE = "storybook/test", sanitizeIdentifier = (value) => value.replace(/[^a-zA-Z0-9_$]+/g, "") || "Component", createComponentNameFromFileName = (fileName) => {
  if (!fileName)
    return "Component";
  let basename = path.basename(fileName, path.extname(fileName));
  return sanitizeIdentifier(basename);
}, containsJsxNode = (valuePath) => {
  if (!valuePath?.node)
    return !1;
  let found = !1;
  return valuePath.traverse({
    JSXElement(path2) {
      found = !0, path2.stop();
    },
    JSXFragment(path2) {
      found = !0, path2.stop();
    }
  }), found;
}, unwrapExpression2 = (node) => node ? t7.isTSAsExpression(node) || t7.isTSSatisfiesExpression(node) ? unwrapExpression2(node.expression) : node : null, dedupeImports = (program, source, specifiers) => {
  let existing = program.body.find(
    (node) => t7.isImportDeclaration(node) && node.source.value === source
  );
  if (existing) {
    specifiers.forEach((specifier) => {
      existing.specifiers.every(
        (existingSpecifier) => !t7.isImportSpecifier(existingSpecifier) || existingSpecifier.local.name !== specifier.local.name
      ) && existing.specifiers.push(specifier);
    });
    return;
  }
  program.body.unshift(t7.importDeclaration(specifiers, t7.stringLiteral(source)));
}, collectComponentExports = (program, fileName) => {
  let components = [], addComponent = (exportedName, localIdentifier, valuePath) => {
    !valuePath || !valuePath.node || !unwrapExpression2(valuePath.node) || containsJsxNode(valuePath) && components.push({ exportedName, localIdentifier });
  };
  return traverse3(program, {
    ExportNamedDeclaration(path2) {
      let { node } = path2;
      if (node.source)
        return;
      let declarationPath = path2.get("declaration");
      if (declarationPath.isVariableDeclaration())
        declarationPath.get("declarations").forEach((declPath) => {
          if (!declPath.isVariableDeclarator())
            return;
          let id = declPath.node.id;
          if (!t7.isIdentifier(id))
            return;
          let initPath = declPath.get("init");
          addComponent(id.name, id, initPath);
        });
      else if (declarationPath.isFunctionDeclaration() && declarationPath.node.id) {
        let declarationId = declarationPath.node.id;
        t7.isIdentifier(declarationId) && addComponent(declarationId.name, declarationId, declarationPath);
      } else if (declarationPath.isClassDeclaration() && declarationPath.node.id) {
        let declarationId = declarationPath.node.id;
        t7.isIdentifier(declarationId) && addComponent(declarationId.name, declarationId, declarationPath);
      }
      path2.get("specifiers").forEach((specifierPath) => {
        if (!specifierPath.isExportSpecifier())
          return;
        let { local, exported } = specifierPath.node;
        if (!t7.isIdentifier(local) || !t7.isIdentifier(exported))
          return;
        let binding = specifierPath.scope.getBinding(local.name);
        if (!binding)
          return;
        let bindingPath = binding.path, localIdentifier = binding.identifier;
        if (t7.isIdentifier(localIdentifier)) {
          if (bindingPath.isVariableDeclarator())
            addComponent(exported.name, localIdentifier, bindingPath.get("init"));
          else if (bindingPath.isFunctionDeclaration() || bindingPath.isClassDeclaration()) {
            let bindingNodeId = bindingPath.node.id;
            t7.isIdentifier(bindingNodeId) && addComponent(exported.name, localIdentifier, bindingPath);
          }
        }
      });
    },
    ExportDefaultDeclaration(path2) {
      let { node } = path2, declaration = node.declaration;
      if (t7.isFunctionExpression(declaration) || t7.isArrowFunctionExpression(declaration) || t7.isClassExpression(declaration)) {
        let identifierName = createComponentNameFromFileName(fileName), identifier = path2.scope.generateUidIdentifier(identifierName), variableDeclaration = t7.variableDeclaration("const", [
          t7.variableDeclarator(identifier, declaration)
        ]);
        variableDeclaration.loc = node.loc, path2.insertBefore(variableDeclaration), node.declaration = identifier;
        let insertedVarPath = path2.getPrevSibling(), initPath = null;
        if (insertedVarPath?.isVariableDeclaration()) {
          let declarationPath = insertedVarPath.get("declarations")[0];
          declarationPath?.isVariableDeclarator() && (initPath = declarationPath.get("init"));
        }
        addComponent(identifierName, identifier, initPath);
        return;
      }
      if (t7.isCallExpression(declaration)) {
        let identifierName = createComponentNameFromFileName(fileName), identifier = path2.scope.generateUidIdentifier(identifierName), variableDeclaration = t7.variableDeclaration("const", [
          t7.variableDeclarator(identifier, declaration)
        ]);
        variableDeclaration.loc = node.loc, path2.insertBefore(variableDeclaration), node.declaration = identifier, components.push({ exportedName: identifierName, localIdentifier: identifier });
        return;
      }
      if (t7.isIdentifier(declaration)) {
        let binding = path2.scope.getBinding(declaration.name);
        if (!binding)
          return;
        let bindingIdentifier = binding.identifier;
        if (!t7.isIdentifier(bindingIdentifier))
          return;
        if (binding.path.isVariableDeclarator())
          addComponent(
            createComponentNameFromFileName(fileName),
            bindingIdentifier,
            binding.path.get("init")
          );
        else if (binding.path.isFunctionDeclaration() || binding.path.isClassDeclaration()) {
          let bindingNodeId = binding.path.node.id;
          t7.isIdentifier(bindingNodeId) && addComponent(bindingNodeId.name, bindingIdentifier, binding.path);
        }
        return;
      }
      if (t7.isFunctionDeclaration(declaration) && declaration.id) {
        addComponent(
          declaration.id.name,
          declaration.id,
          path2.get("declaration")
        );
        return;
      }
      t7.isClassDeclaration(declaration) && declaration.id && addComponent(
        declaration.id.name,
        declaration.id,
        path2.get("declaration")
      );
    }
  }), components;
}, componentTransform = async ({
  code,
  fileName,
  getComponentArgTypes
}) => {
  let ast = babelParse4(code), file = new BabelFileClass({ filename: fileName, highlightCode: !1 }, { code, ast }), components = collectComponentExports(ast.program, fileName);
  if (!components.length)
    return { code, map: null };
  let vitestTestId = file.path.scope.generateUidIdentifier("test"), vitestExpectId = file.path.scope.generateUidIdentifier("expect"), testStoryId = file.path.scope.generateUidIdentifier("testStory"), convertToFilePathId = t7.identifier("convertToFilePath"), fnId = file.path.scope.generateUidIdentifier("fn");
  dedupeImports(ast.program, VITEST_IMPORT_SOURCE, [
    t7.importSpecifier(vitestTestId, t7.identifier("test")),
    t7.importSpecifier(vitestExpectId, t7.identifier("expect"))
  ]), dedupeImports(ast.program, TEST_UTILS_IMPORT_SOURCE, [
    t7.importSpecifier(testStoryId, t7.identifier("testStory")),
    t7.importSpecifier(convertToFilePathId, t7.identifier("convertToFilePath"))
  ]);
  let testStatements = [], hasFunctionPlaceholder = (value) => JSON.stringify(value).includes(STORYBOOK_FN_PLACEHOLDER), valueToNodeRecursive = (value, replaceFnCalls) => {
    if (!replaceFnCalls)
      return t7.valueToNode(value);
    if (value === STORYBOOK_FN_PLACEHOLDER)
      return t7.callExpression(fnId, []);
    if (typeof value == "object" && value !== null) {
      if (Array.isArray(value))
        return t7.arrayExpression(value.map((val) => valueToNodeRecursive(val, replaceFnCalls)));
      let properties = Object.entries(value).map(([key, val]) => {
        let keyNode = t7.isValidIdentifier(key) ? t7.identifier(key) : t7.stringLiteral(key);
        return t7.objectProperty(keyNode, valueToNodeRecursive(val, replaceFnCalls));
      });
      return t7.objectExpression(properties);
    }
    return t7.valueToNode(value);
  }, buildArgsExpression = (args, useFnImport = !1) => {
    if (!args || Object.keys(args).length === 0)
      return t7.objectExpression([]);
    let properties = Object.entries(args).map(([key, value]) => {
      let keyNode = t7.isValidIdentifier(key) ? t7.identifier(key) : t7.stringLiteral(key);
      return t7.objectProperty(keyNode, valueToNodeRecursive(value, useFnImport));
    });
    return t7.objectExpression(properties);
  }, hasAnyFunctionPlaceholders = !1;
  for (let component of components) {
    let argTypes = getComponentArgTypes ? await getComponentArgTypes({ componentName: component.exportedName, fileName }) : void 0, generatedArgs = argTypes ? generateDummyArgsFromArgTypes(argTypes, { skipUrlGeneration: !0 }).required : void 0;
    !hasAnyFunctionPlaceholders && generatedArgs && hasFunctionPlaceholder(generatedArgs) && (hasAnyFunctionPlaceholders = !0);
    let meta = t7.objectExpression([
      t7.objectProperty(
        t7.identifier("title"),
        t7.stringLiteral(`generated/tests/${component.exportedName}`)
      ),
      t7.objectProperty(t7.identifier("component"), component.localIdentifier)
    ]), testStoryArgs = t7.objectExpression([
      t7.objectProperty(t7.identifier("exportName"), t7.stringLiteral(component.exportedName)),
      // This is where the story annotation for a particular component is defined, inline
      t7.objectProperty(
        t7.identifier("story"),
        t7.objectExpression([
          t7.objectProperty(
            t7.identifier("args"),
            buildArgsExpression(generatedArgs, hasAnyFunctionPlaceholders)
          )
        ])
      ),
      t7.objectProperty(t7.identifier("meta"), meta),
      t7.objectProperty(t7.identifier("skipTags"), t7.arrayExpression([])),
      t7.objectProperty(
        t7.identifier("storyId"),
        t7.stringLiteral(`generated-${component.exportedName}`)
      ),
      t7.objectProperty(t7.identifier("componentPath"), t7.stringLiteral(fileName)),
      t7.objectProperty(
        t7.identifier("componentName"),
        t7.stringLiteral(component.localIdentifier.name)
      )
    ]), testCall = t7.expressionStatement(
      t7.callExpression(vitestTestId, [
        t7.stringLiteral(component.exportedName),
        t7.callExpression(testStoryId, [testStoryArgs])
      ])
    );
    testStatements.push(testCall);
  }
  hasAnyFunctionPlaceholders && dedupeImports(ast.program, STORYBOOK_TEST_IMPORT_SOURCE, [
    t7.importSpecifier(fnId, t7.identifier("fn"))
  ]);
  let { declaration: guardDeclaration, identifier: guardIdentifier } = createTestGuardDeclaration(
    file.path.scope,
    vitestExpectId,
    convertToFilePathId
  );
  return ast.program.body.push(guardDeclaration), ast.program.body.push(t7.ifStatement(guardIdentifier, t7.blockStatement(testStatements))), generate4(ast, { sourceMaps: !0, sourceFileName: fileName }, code);
};

export {
  ConfigFile,
  loadConfig,
  formatConfig,
  printConfig,
  readConfig,
  writeConfig,
  isCsfFactoryPreview,
  getStorySortParameter,
  extractJSDocInfo,
  extractComponentDescription,
  enrichCsfStory,
  enrichCsfMeta,
  enrichCsf,
  extractSource,
  extractDescription,
  resolveComponentImport,
  buildImportStatements,
  jsDocTagsForPath,
  extractStoryJSDocInfo,
  normalizeStoryDeclaration,
  createStoryArgsResolver,
  unresolvedWarning,
  noSnippetWarning,
  resolveRenderFunction,
  vitestTransform,
  STORYBOOK_FN_PLACEHOLDER,
  generateDummyArgsFromArgTypes,
  componentTransform,
  babelParse5 as babelParse
};