{"version":3,"sources":["../package.json","../src/cartographer.ts","../src/constants.ts"],"sourcesContent":["{\n  \"name\": \"@replit/vite-plugin-cartographer\",\n  \"version\": \"0.5.1\",\n  \"private\": false,\n  \"devDependencies\": {\n    \"@replit/tsconfig\": \"workspace:*\",\n    \"@types/babel__core\": \"^7.20.5\",\n    \"@types/babel__traverse\": \"^7.20.6\",\n    \"@types/node\": \"^22.5.5\",\n    \"@typescript-eslint/eslint-plugin\": \"^6.7.0\",\n    \"@typescript-eslint/parser\": \"^6.7.0\",\n    \"happy-dom\": \"catalog:\",\n    \"tsup\": \"^8.3.5\",\n    \"tsx\": \"^4.9.5\",\n    \"vite\": \"^5.4.10\",\n    \"vitest\": \"catalog:\"\n  },\n  \"main\": \"./src/index.ts\",\n  \"files\": [\n    \"src\"\n  ],\n  \"scripts\": {\n    \"build\": \"tsup\",\n    \"lint\": \"eslint src\",\n    \"format\": \"prettier --write \\\"src/**/*.ts\\\"\",\n    \"test\": \"vitest\"\n  },\n  \"dependencies\": {\n    \"@babel/parser\": \"^7.26.9\",\n    \"@babel/traverse\": \"^7.26.9\",\n    \"@babel/types\": \"^7.26.9\",\n    \"magic-string\": \"^0.30.12\",\n    \"modern-screenshot\": \"^4.6.0\"\n  }\n}\n","import fs from 'fs/promises';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport { parse } from '@babel/parser';\nimport type {\n  File as BabelFile,\n  JSXIdentifier,\n  JSXMemberExpression,\n  JSXOpeningElement,\n} from '@babel/types';\nimport MagicString from 'magic-string';\nimport type { Plugin } from 'vite';\n\nimport {\n  DATA_ATTRIBUTES,\n  R3F_BAILOUT_ELEMENTS,\n  R3F_BAILOUT_PATTERNS,\n  R3F_IMPORT_SOURCES,\n} from './constants';\n\nconst validExtensions = new Set(['.jsx', '.tsx']);\n\n/**\n * Normalize a file path to use forward slashes.\n * Vite normalizes all module IDs to forward-slash form (even on Windows),\n * so every path that will be compared to a Vite `id` must be normalized the\n * same way.  We explicitly replace backslashes rather than relying on\n * `path.sep` so the behaviour is correct even when the path string contains\n * Windows separators on a non-Windows host (e.g. in tests).\n */\nfunction normalizePath(p: string): string {\n  return p.replace(/\\\\/g, '/');\n}\n\n/** Tailwind config file names to search for in the project root, in priority order */\nconst TAILWIND_CONFIG_FILES = [\n  'tailwind.config.ts',\n  'tailwind.config.js',\n  'tailwind.config.mjs',\n  'tailwind.config.cjs',\n];\n\n// Keep in sync with versions used in stack templates\nconst TAILWIND_V4_CDN_URL =\n  'https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.1.14';\nconst TAILWIND_V3_CDN_URL = 'https://cdn.tailwindcss.com/3.4.17';\n\nexport interface CartographerOptions {\n  /** Override the root directory for metadata path resolution (absolute or relative to Vite root) */\n  root?: string;\n}\n\nexport function cartographer(options?: CartographerOptions): Plugin {\n  let clientScript: string;\n  let viteRoot: string;\n  let configuredRoot: string;\n  let configuredRootName: string;\n  let tailwindConfigPath: string | null = null;\n  let tailwindMajorVersion: number = 4;\n  /** The file ID that was selected for tailwind config injection (null = not yet chosen). */\n  let tailwindInjectionTarget: string | null = null;\n\n  return {\n    name: '@replit/vite-plugin-cartographer',\n    enforce: 'pre' as const,\n\n    async configResolved(config) {\n      // Vite normalizes config.root to forward slashes on all platforms.\n      // We normalize every stored path the same way so that comparisons\n      // against Vite module IDs (which always use forward slashes) work\n      // correctly on Windows.\n      viteRoot = normalizePath(config.root);\n\n      if (options?.root) {\n        configuredRoot = normalizePath(\n          path.isAbsolute(options.root)\n            ? options.root\n            : path.resolve(config.root, options.root),\n        );\n      } else {\n        configuredRoot = viteRoot;\n      }\n\n      // The name of the directory that contains the configured root\n      configuredRootName = path.basename(configuredRoot);\n\n      // Resolve the client script path in both CJS and ESM environments\n      const currentFileUrl =\n        typeof __dirname === 'string'\n          ? path.join(__dirname, '../dist/beacon/index.global.js')\n          : fileURLToPath(\n              new URL('../dist/beacon/index.global.js', import.meta.url),\n            );\n\n      try {\n        clientScript = await fs.readFile(currentFileUrl, 'utf-8');\n      } catch (error) {\n        // eslint-disable-next-line no-console -- This is an error in the plugin\n        console.error(\n          '[replit-cartographer] Failed to load client script:',\n          error,\n        );\n      }\n\n      // Walk up from the configured root to find the closest package.json.\n      // The directory containing package.json is used as the project root for\n      // both tailwind config lookup and version detection. This handles cases\n      // where the Vite/cartographer root is a subdirectory (e.g. `client/`)\n      // but the package.json and tailwind config live in the repository root.\n      const packageDir = await findClosestPackageDir(configuredRoot);\n      const tailwindSearchRoot = packageDir ?? configuredRoot;\n\n      const rawTailwindConfigPath =\n        await findTailwindConfigPath(tailwindSearchRoot);\n      tailwindConfigPath = rawTailwindConfigPath\n        ? normalizePath(rawTailwindConfigPath)\n        : null;\n      tailwindMajorVersion =\n        await detectTailwindMajorVersion(tailwindSearchRoot);\n    },\n\n    resolveId(_source, _importer) {\n      // Let Vite handle the resolution\n      return null;\n    },\n\n    transform: {\n      order: 'pre',\n      async handler(code: string, id: string) {\n        // One day the JS ecosystem will decide on one standard to use, until then\n        // We need to dynamically import this so that we don't mess with vite resolving this module\n        // Handle CommonJS/ESM interop: @babel/traverse may be wrapped differently depending on the module system\n        const traverseModule = await import('@babel/traverse');\n        const traverse =\n          (\n            traverseModule.default as {\n              default?: typeof traverseModule.default;\n            }\n          )?.default ||\n          traverseModule.default ||\n          traverseModule;\n\n        // Make the tailwind config browser-safe by converting CJS\n        // `require()` calls to ESM imports so Vite can bundle them.\n        // Plugins are kept as-is — if a plugin is Node-only and throws\n        // during initialization, the error is caught at the import site\n        // in the entry file (see below) and we skip the config entirely.\n        if (tailwindConfigPath && id === tailwindConfigPath) {\n          if (typeof traverse !== 'function') {\n            return null;\n          }\n\n          return makeTailwindConfigBrowserSafe(code, traverse);\n        }\n\n        if (\n          !validExtensions.has(path.extname(id)) ||\n          id.includes('node_modules')\n        ) {\n          return null;\n        }\n\n        try {\n          const ast = parse(code, {\n            sourceType: 'module',\n            plugins: ['jsx', 'typescript'],\n          });\n\n          let isR3FFile = usesReactThreeFiber(ast);\n\n          if (isR3FFile) {\n            return null;\n          }\n\n          const magicString = new MagicString(code);\n\n          // Inject the tailwind config import into the entry file.\n          //\n          // On the first eligible .tsx/.jsx file under viteRoot we lock in its\n          // id as the injection target. Every subsequent transform of that same\n          // file (including HMR re-transforms) gets the import prepended again,\n          // which keeps `window.REPLIT_APP_TAILWIND_CONFIG` alive across edits.\n          //\n          // Files outside viteRoot (e.g. in node_modules) are excluded by the\n          // check above. Files are eligible if they live anywhere under (or at)\n          // viteRoot — this covers both `App.tsx` at root and `src/main.tsx`.\n          //\n          // We prepend the import optimistically here but only commit the\n          // target (tailwindInjectionTarget = id) after all bail-out points\n          // have passed, so that an R3F file doesn't permanently lock the\n          // target while returning null.\n          let didInjectTailwind = false;\n\n          if (tailwindConfigPath) {\n            const isInjectionCandidate =\n              tailwindInjectionTarget === id ||\n              (tailwindInjectionTarget === null &&\n                id.startsWith(viteRoot + '/'));\n\n            if (isInjectionCandidate) {\n              const relativeConfigPath = normalizePath(\n                path.relative(path.dirname(id), tailwindConfigPath),\n              );\n              const importPath = relativeConfigPath.startsWith('.')\n                ? relativeConfigPath\n                : `./${relativeConfigPath}`;\n\n              magicString.prepend(\n                [\n                  `(async () => {`,\n                  `  try {`,\n                  `    const __replit_tw_mod = await import(${JSON.stringify(importPath)});`,\n                  `    const __replit_tw_raw = __replit_tw_mod.default ?? __replit_tw_mod;`,\n                  `    if (typeof window !== 'undefined' && __replit_tw_raw && typeof __replit_tw_raw === 'object') {`,\n                  `      window.REPLIT_APP_TAILWIND_CONFIG = __replit_tw_raw;`,\n                  `      window.dispatchEvent(new CustomEvent('replit-tailwind-config-ready'));`,\n                  `    }`,\n                  `  } catch (e) {`,\n                  `    console.warn('[replit-cartographer] Skipped loading tailwind config due to an incompatible Node plugin:', e);`,\n                  `  }`,\n                  `})();\\n`,\n                ].join('\\n'),\n              );\n              didInjectTailwind = true;\n            }\n          }\n\n          let currentElement: import('@babel/types').Node | null = null;\n\n          if (typeof traverse !== 'function') {\n            // eslint-disable-next-line no-console -- I want my errors to be logged :D\n            console.error(\n              `[replit-cartographer] @babel/traverse did not resolve to a function.`,\n            );\n\n            return null; // Skip transformation instead of crashing\n          }\n\n          // Collect local names that resolve to React.Fragment so we can\n          // skip instrumenting them regardless of aliasing.  This handles:\n          //   import { Fragment } from 'react'\n          //   import { Fragment as F } from 'react'\n          const fragmentAliases = collectFragmentAliases(ast);\n\n          traverse(ast, {\n            JSXElement: {\n              enter(elementPath) {\n                if (isR3FFile) {\n                  return; // Optimization: Stop processing if we already know it's R3F\n                }\n\n                currentElement = elementPath.node;\n              },\n              exit() {\n                currentElement = null;\n              },\n            },\n            JSXOpeningElement(elementPath) {\n              if (isR3FFile) {\n                return;\n              }\n\n              const jsxNode = elementPath.node;\n              const elementName = getElementName(jsxNode);\n\n              if (!elementName) {\n                return;\n              }\n\n              // Check if this file contains R3F elements that should trigger a file-level bailout\n              if (shouldBailout(elementName)) {\n                isR3FFile = true;\n                elementPath.stop(); // Stop traversing this file entirely\n\n                return;\n              }\n\n              // Skip instrumentation for specific ambiguous elements (like 'line')\n              // We don't instrument them, but they don't trigger a file-level bailout\n              if (elementName === 'line') {\n                return;\n              }\n\n              // Skip React.Fragment and aliases (e.g. `import { Fragment as F }`).\n              // Fragments only accept `key` and `children` props — injecting\n              // data attributes causes a React warning at runtime.\n              if (isFragment(elementName, fragmentAliases)) {\n                return;\n              }\n\n              if (currentElement) {\n                const { line = 0, column: col = 0 } = jsxNode.loc?.start ?? {};\n\n                const relativeToConfigured = path.relative(configuredRoot, id);\n                const componentPath = normalizePath(\n                  path.join(configuredRootName, relativeToConfigured),\n                );\n\n                const componentMetadata =\n                  col === 0\n                    ? `${componentPath}:${line}`\n                    : `${componentPath}:${line}:${col}`;\n\n                magicString.appendLeft(\n                  jsxNode.name.end ?? 0,\n                  ` ${DATA_ATTRIBUTES.METADATA}=\"${componentMetadata}\" ${DATA_ATTRIBUTES.COMPONENT_NAME}=\"${elementName}\"`,\n                );\n              }\n            },\n          });\n\n          if (isR3FFile) {\n            return null;\n          }\n\n          // Lock in the injection target now that we know the transform will\n          // succeed.  If we committed earlier, an R3F bail-out above would\n          // have permanently locked the target while returning null, and no\n          // other file would ever receive the injection.\n          if (didInjectTailwind) {\n            tailwindInjectionTarget = id;\n          }\n\n          return {\n            code: magicString.toString(),\n            map: magicString.generateMap({ hires: true }),\n          };\n        } catch (error) {\n          // eslint-disable-next-line no-console -- I want my errors to be logged :D\n          console.error(`[replit-cartographer] Error processing ${id}:`, error);\n\n          return null;\n        }\n      },\n    },\n\n    transformIndexHtml() {\n      if (!clientScript) {\n        return [];\n      }\n\n      const tags: Array<{\n        tag: string;\n        attrs: Record<string, string>;\n        children?: string;\n        injectTo: 'head' | 'body';\n      }> = [\n        {\n          tag: 'script',\n          attrs: { type: 'module' },\n          children: clientScript,\n          injectTo: 'head',\n        },\n        {\n          tag: 'script',\n          attrs: {},\n          children: generateClientSideTailwindScript(tailwindMajorVersion),\n          injectTo: 'head',\n        },\n      ];\n\n      return tags;\n    },\n  };\n}\n\n// Helper function to extract element name from JSX node\nfunction getElementName(jsxNode: JSXOpeningElement): string | null {\n  if (jsxNode.name.type === 'JSXIdentifier') {\n    return jsxNode.name.name;\n  }\n\n  if (jsxNode.name.type === 'JSXMemberExpression') {\n    const memberExpr = jsxNode.name as JSXMemberExpression;\n    const object = memberExpr.object as JSXIdentifier;\n    const property = memberExpr.property as JSXIdentifier;\n\n    return `${object.name}.${property.name}`;\n  }\n\n  return null;\n}\n\nfunction usesReactThreeFiber(ast: BabelFile): boolean {\n  return ast.program.body.some(\n    (node) =>\n      node.type === 'ImportDeclaration' &&\n      typeof node.source.value === 'string' &&\n      R3F_IMPORT_SOURCES.has(node.source.value),\n  );\n}\n\nfunction shouldBailout(name: string): boolean {\n  if (R3F_BAILOUT_ELEMENTS.has(name)) {\n    return true;\n  }\n\n  return R3F_BAILOUT_PATTERNS.some((pattern) => name.match(pattern));\n}\n\n/**\n * Scan the file's import declarations for Fragment-related bindings from\n * React packages and return two sets:\n *\n * 1. `namedAliases` — local names for the `Fragment` named export:\n *      import { Fragment } from 'react';           -> \"Fragment\"\n *      import { Fragment as F } from 'react';      -> \"F\"\n *\n * 2. `namespaceAliases` — local names for default/namespace imports whose\n *    `.Fragment` member is React.Fragment:\n *      import React from 'react';                  -> \"React\"\n *      import * as R from 'react';                 -> \"R\"\n *\n * Together these let `isFragment` skip `<F>`, `<React.Fragment>`, and\n * `<R.Fragment>` while still instrumenting unrelated userland components\n * like `<Layout.Fragment>`.\n */\nconst FRAGMENT_IMPORT_SOURCES = new Set(['react', 'react/jsx-runtime']);\n\ninterface FragmentBindings {\n  namedAliases: Set<string>;\n  namespaceAliases: Set<string>;\n}\n\nfunction collectFragmentAliases(ast: BabelFile): FragmentBindings {\n  const namedAliases = new Set<string>();\n  const namespaceAliases = new Set<string>();\n\n  for (const node of ast.program.body) {\n    if (\n      node.type === 'ImportDeclaration' &&\n      typeof node.source.value === 'string' &&\n      FRAGMENT_IMPORT_SOURCES.has(node.source.value)\n    ) {\n      for (const specifier of node.specifiers) {\n        if (\n          specifier.type === 'ImportSpecifier' &&\n          specifier.imported.type === 'Identifier' &&\n          specifier.imported.name === 'Fragment'\n        ) {\n          // import { Fragment } or import { Fragment as F }\n          namedAliases.add(specifier.local.name);\n        } else if (\n          specifier.type === 'ImportDefaultSpecifier' ||\n          specifier.type === 'ImportNamespaceSpecifier'\n        ) {\n          // import React from 'react' or import * as React from 'react'\n          namespaceAliases.add(specifier.local.name);\n        }\n      }\n    }\n  }\n\n  return { namedAliases, namespaceAliases };\n}\n\n/**\n * Check if the element name refers to a React Fragment.\n * Matches:\n *   - Named imports: `Fragment` or any alias imported from react\n *   - Member expressions: `React.Fragment` where `React` is a known\n *     default/namespace import from react\n *\n * Does NOT match unrelated userland components like `Layout.Fragment`.\n * Fragments only accept `key` and `children` — injecting data attributes\n * causes a runtime warning.\n */\nfunction isFragment(name: string, bindings: FragmentBindings): boolean {\n  if (bindings.namedAliases.has(name)) {\n    return true;\n  }\n\n  // Check for <Namespace>.Fragment where Namespace is a known react import\n  const dotIndex = name.indexOf('.');\n\n  if (dotIndex !== -1) {\n    const ns = name.slice(0, dotIndex);\n    const prop = name.slice(dotIndex + 1);\n\n    if (prop === 'Fragment' && bindings.namespaceAliases.has(ns)) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\n/**\n * Walk up the directory tree from `startDir` looking for the closest `package.json`.\n * Returns the absolute path to the directory containing `package.json`, or null if\n * none is found before reaching the filesystem root.\n */\nasync function findClosestPackageDir(startDir: string): Promise<string | null> {\n  let current = path.resolve(startDir);\n\n  // eslint-disable-next-line no-constant-condition -- intentional walk-up loop\n  while (true) {\n    try {\n      await fs.access(path.join(current, 'package.json'));\n\n      return current;\n    } catch {\n      // package.json not here, move up\n    }\n\n    const parent = path.dirname(current);\n\n    if (parent === current) {\n      // Reached the filesystem root without finding package.json\n      return null;\n    }\n\n    current = parent;\n  }\n}\n\n/**\n * Search for a tailwind config file in the given directory.\n * Returns the absolute file path if found, or null if no config file exists.\n */\nasync function findTailwindConfigPath(dir: string): Promise<string | null> {\n  for (const configFile of TAILWIND_CONFIG_FILES) {\n    const configPath = path.join(dir, configFile);\n\n    try {\n      await fs.access(configPath);\n\n      return configPath;\n    } catch {\n      // File doesn't exist, try the next one\n    }\n  }\n\n  return null;\n}\n\n/**\n * Detect the major version of tailwindcss from a `package.json` file.\n * Reads the `tailwindcss` dependency (or devDependency) version string and\n * extracts the major version number. Defaults to 4 if detection fails.\n */\nasync function detectTailwindMajorVersion(packageDir: string): Promise<number> {\n  try {\n    const packageJsonPath = path.join(packageDir, 'package.json');\n    const packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8');\n    const packageJson = JSON.parse(packageJsonContent) as {\n      dependencies?: Record<string, string>;\n      devDependencies?: Record<string, string>;\n    };\n\n    const versionSpec =\n      packageJson.dependencies?.tailwindcss ??\n      packageJson.devDependencies?.tailwindcss;\n\n    if (!versionSpec) {\n      return 4;\n    }\n\n    // Strip common range prefixes (^, ~, >=, etc.) and extract the major version\n    const match = versionSpec.match(/(\\d+)/);\n\n    if (match) {\n      return parseInt(match[1], 10);\n    }\n\n    return 4;\n  } catch {\n    // package.json not found or unreadable — default to v4\n    return 4;\n  }\n}\n\n/**\n * Make a tailwind config file browser-safe by converting CJS `require()`\n * calls to ESM `import` statements.\n *\n * Tailwind configs often use `require(\"tailwindcss-animate\")` or similar\n * CJS patterns that don't work in the browser.  This function rewrites\n * each `require(\"pkg\")` into a top-level `import __req_N from \"pkg\"`\n * and replaces the call-site with the binding identifier.  The actual\n * error handling (in case the imported package is Node-only and crashes)\n * happens at the import site in the entry file — see the `transform`\n * hook where the config is dynamically imported inside a try/catch.\n *\n * @param traverseFn - A resolved `@babel/traverse` default export.\n * Returns `null` if no modifications were needed (config is already ESM-safe).\n */\nfunction makeTailwindConfigBrowserSafe(\n  code: string,\n  traverseFn: typeof import('@babel/traverse').default,\n): { code: string; map: ReturnType<MagicString['generateMap']> } | null {\n  const ast = parse(code, {\n    sourceType: 'module',\n    plugins: ['typescript'],\n  });\n\n  const s = new MagicString(code);\n  let modified = false;\n\n  // Collect all require() calls and assign each unique module a binding name.\n  const importBindings = new Map<string, string>();\n  let bindingCounter = 0;\n\n  const requireSites: Array<{\n    start: number;\n    end: number;\n    binding: string;\n  }> = [];\n\n  traverseFn(ast, {\n    CallExpression(nodePath) {\n      const node = nodePath.node;\n\n      if (\n        node.callee.type === 'Identifier' &&\n        node.callee.name === 'require' &&\n        node.arguments.length === 1 &&\n        node.arguments[0].type === 'StringLiteral'\n      ) {\n        if (node.start == null || node.end == null) {\n          return;\n        }\n\n        const moduleName = node.arguments[0].value;\n        let binding = importBindings.get(moduleName);\n\n        if (!binding) {\n          binding = `__req_${bindingCounter++}`;\n          importBindings.set(moduleName, binding);\n        }\n\n        requireSites.push({ start: node.start, end: node.end, binding });\n      }\n    },\n  });\n\n  if (requireSites.length === 0) {\n    return null;\n  }\n\n  // Build the import statements to prepend\n  const importLines: Array<string> = [];\n\n  for (const [moduleName, binding] of importBindings) {\n    importLines.push(`import ${binding} from ${JSON.stringify(moduleName)};`);\n  }\n\n  s.prepend(importLines.join('\\n') + '\\n');\n\n  // Replace each require() call-site with its binding\n  for (const site of requireSites) {\n    s.overwrite(site.start, site.end, site.binding);\n    modified = true;\n  }\n\n  if (!modified) {\n    return null;\n  }\n\n  return {\n    code: s.toString(),\n    map: s.generateMap({ hires: true }),\n  };\n}\n\n/**\n * Generate the client-side tailwind initialization script.\n *\n * Listens for the `replit-tailwind-config-ready` CustomEvent (dispatched by\n * the injected config loader in the entry file) to know when the config is\n * available.  The beacon dispatches a `replit-init-tailwind` CustomEvent to\n * trigger loading, and this script listens for it.\n *\n * The script includes a built-in once-guard so Tailwind is only loaded once\n * even if the event fires multiple times.\n *\n * **v4** (`@tailwindcss/browser@4`): config is passed as a JSON-serialized `data-config` attribute.\n * **v3** (`cdn.tailwindcss.com`): config is set on `tailwind.config` global via `script.onload`.\n */\nfunction generateClientSideTailwindScript(majorVersion: number): string {\n  const cdnUrl =\n    majorVersion >= 4\n      ? JSON.stringify(TAILWIND_V4_CDN_URL)\n      : JSON.stringify(TAILWIND_V3_CDN_URL);\n\n  const loadTailwindBody =\n    majorVersion >= 4\n      ? `\n        var script = document.createElement('script');\n        script.src = ${cdnUrl};\n\n        if (window.REPLIT_APP_TAILWIND_CONFIG) {\n          script.setAttribute('data-config', JSON.stringify(window.REPLIT_APP_TAILWIND_CONFIG));\n        }\n\n        document.head.appendChild(script);`\n      : `\n        var script = document.createElement('script');\n        script.src = ${cdnUrl};\n\n        script.onload = function() {\n          if (window.REPLIT_APP_TAILWIND_CONFIG) {\n            window.tailwind.config = window.REPLIT_APP_TAILWIND_CONFIG;\n          }\n        };\n\n        document.head.appendChild(script);`;\n\n  return `\n(function() {\n  var initialized = false;\n\n  function loadTailwind() {\n    if (initialized) { return; }\n    initialized = true;\n${loadTailwindBody}\n  }\n\n  // Listen for the beacon requesting tailwind initialization.\n  window.addEventListener('replit-init-tailwind', function() {\n    if (window.REPLIT_APP_TAILWIND_CONFIG) {\n      loadTailwind();\n    } else {\n      // Config not ready yet — wait for the config-ready event, but proceed\n      // without a config after 1 second.  The config is optional (projects\n      // without a tailwind config file will never fire the ready event).\n      var configTimeout = setTimeout(function() { loadTailwind(); }, 1000);\n      window.addEventListener('replit-tailwind-config-ready', function() {\n        clearTimeout(configTimeout);\n        loadTailwind();\n      }, { once: true });\n    }\n  });\n})();\n`.trim();\n}\n","export const DATA_ATTRIBUTES = {\n  METADATA: 'data-replit-metadata',\n  COMPONENT_NAME: 'data-component-name',\n} as const;\n\nexport const R3F_IMPORT_SOURCES = new Set([\n  '@react-three/fiber',\n  '@react-three/drei',\n  'react-three-fiber',\n]);\n\n// Elements that, if present, indicate the file is likely an R3F scene\nexport const R3F_BAILOUT_ELEMENTS = new Set([\n  'Canvas', // Top-level R3F component\n  'mesh',\n  'group',\n  'scene',\n  'primitive',\n  'points',\n  'instancedMesh',\n  'fog',\n  'fogExp2',\n  'object3D',\n]);\n\n// Regex patterns for elements that indicate R3F (e.g. <boxGeometry>, <ambientLight>)\nexport const R3F_BAILOUT_PATTERNS = [\n  /Geometry$/,\n  /Material$/,\n  /Light$/,\n  /Camera$/,\n  /Helper$/,\n  /Control$/,\n];\n"],"mappings":"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","names":[]}