{"version":3,"sources":["../src/index.ts"],"names":["path","fs","crypto"],"mappings":"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","file":"index.cjs","sourcesContent":["import crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { Plugin } from \"vite\";\n\ninterface NativeFileInfo {\n  /** File content for emission */\n  content: Buffer;\n  /** Hashed filename for output (e.g., addon-a1b2c3d4.node) */\n  hashedFilename: string;\n  /** Absolute path to the original .node file */\n  originalPath: string;\n}\n\ninterface PackageConfig {\n  /** Package name to target (e.g., 'native-package-123') */\n  package: string;\n  /** Additional file names to copy (e.g., ['native-file.node-macos', 'addon.node-linux']) */\n  fileNames: string[];\n}\n\nexport interface NativeFilePluginOptions {\n  /** Enable the plugin. Defaults to true in build mode, false in dev mode */\n  forced?: boolean;\n  /** Additional native file configurations for packages with non-standard file extensions */\n  additionalNativeFiles?: PackageConfig[];\n}\n\n// ESTree AST Node types\ninterface BaseASTNode {\n  type: string;\n  start?: number;\n  end?: number;\n}\n\ninterface LiteralNode extends BaseASTNode {\n  type: \"Literal\";\n  value: string | number | boolean | null;\n  start: number;\n  end: number;\n}\n\ninterface CallExpressionNode extends BaseASTNode {\n  type: \"CallExpression\";\n  arguments: BaseASTNode[];\n}\n\n// Type guard functions\nfunction isCallExpression(node: BaseASTNode): node is CallExpressionNode {\n  return node.type === \"CallExpression\";\n}\n\nfunction isLiteral(node: BaseASTNode): node is LiteralNode {\n  return node.type === \"Literal\";\n}\n\nexport default function nativeFilePlugin(\n  options: NativeFilePluginOptions = {}\n): Plugin {\n  const name = \"plugin-native-modules\";\n  const nativeFiles = new Map<string, NativeFileInfo>();\n  let command: \"build\" | \"serve\" = \"build\";\n\n  // Helper function to check if a file path should be processed based on package configs\n  function shouldProcessFile(filePath: string, currentFileId: string): boolean {\n    // Always process .node files\n    if (filePath.endsWith(\".node\")) return true;\n\n    // Check additional native file configurations\n    if (options.additionalNativeFiles) {\n      for (const pkgConfig of options.additionalNativeFiles) {\n        // Check if current file is within this package's node_modules\n        const pkgPath = `node_modules/${pkgConfig.package}`;\n        if (currentFileId.includes(pkgPath)) {\n          // Check if this file matches any of the configured file names\n          for (const fileName of pkgConfig.fileNames) {\n            if (\n              filePath.endsWith(fileName) ||\n              filePath.includes(`/${fileName}`)\n            ) {\n              return true;\n            }\n          }\n        }\n      }\n    }\n\n    return false;\n  }\n\n  return {\n    configResolved(config) {\n      command = config.command;\n    },\n\n    generateBundle() {\n      // Emit each .node file as an asset\n      nativeFiles.forEach((info) => {\n        this.emitFile({\n          fileName: info.hashedFilename,\n          source: info.content,\n          type: \"asset\",\n        });\n      });\n    },\n\n    load(id) {\n      if (!id.startsWith(\"\\0native:\")) return null;\n\n      const originalPath = id.slice(\"\\0native:\".length);\n      const info = nativeFiles.get(originalPath);\n\n      if (!info) return null;\n\n      // Return proxy code that requires the hashed file\n      // The hashed file will be in the same directory as the output bundle\n      return `\n        const { createRequire } = require('node:module');\n        const require = createRequire(import.meta.url);\n        module.exports = require('./${info.hashedFilename}');\n      `;\n    },\n\n    name,\n\n    async resolveId(source, importer) {\n      // Check if enabled\n      const enabled = options.forced ?? command === \"build\";\n\n      if (!enabled) return null;\n      if (!importer) return null;\n\n      // Check if this file should be processed\n      if (!shouldProcessFile(source, importer)) return null;\n\n      // Resolve the path\n      const resolved = path.resolve(path.dirname(importer), source);\n\n      // Check if file exists\n      if (!fs.existsSync(resolved)) return null;\n\n      // Generate hash from file content\n      const content = fs.readFileSync(resolved);\n      const hash = crypto\n        .createHash(\"md5\")\n        .update(content)\n        .digest(\"hex\")\n        .slice(0, 8);\n\n      // Insert hash before the last extension\n      const filename = path.basename(source);\n      const lastDotIndex = filename.lastIndexOf(\".\");\n      const hashedFilename =\n        lastDotIndex > 0\n          ? `${filename.slice(\n              0,\n              lastDotIndex\n            )}-${hash.toUpperCase()}${filename.slice(lastDotIndex)}`\n          : `${filename}-${hash.toUpperCase()}`;\n\n      // Store the mapping\n      nativeFiles.set(resolved, {\n        content,\n        hashedFilename,\n        originalPath: resolved,\n      });\n\n      // Return a virtual module ID\n      return `\\0native:${resolved}`;\n    },\n\n    transform(code, id) {\n      // Check if enabled\n      const enabled = options.forced ?? command === \"build\";\n\n      if (!enabled) return null;\n\n      // Only process files that mention .node\n      if (!code.includes(\".node\")) return null;\n\n      let modified = false;\n      const replacements: Array<{ start: number; end: number; value: string }> =\n        [];\n\n      try {\n        // Parse the code using Rollup's built-in parser\n        // In tests, this.parse may not be available, so we check first\n\n        const ast = this.parse(code);\n\n        // Walk the AST to find CallExpression nodes\n        const walk = (node: BaseASTNode): void => {\n          if (isCallExpression(node)) {\n            // Check if this call has a single string literal argument\n            if (\n              node.arguments.length === 1 &&\n              isLiteral(node.arguments[0]) &&\n              typeof node.arguments[0].value === \"string\"\n            ) {\n              const literalNode = node.arguments[0];\n              const relativePath = literalNode.value as string;\n\n              // Check if this file should be processed (either .node or package-specific)\n              if (!shouldProcessFile(relativePath, id)) return;\n\n              // Resolve the actual path\n              const absolutePath = path.resolve(path.dirname(id), relativePath);\n\n              if (!fs.existsSync(absolutePath)) return;\n\n              // Check if we already processed this file\n              let info = nativeFiles.get(absolutePath);\n\n              if (!info) {\n                // Generate hash and store\n                const content = fs.readFileSync(absolutePath);\n                const hash = crypto\n                  .createHash(\"md5\")\n                  .update(content)\n                  .digest(\"hex\")\n                  .slice(0, 8);\n\n                // Insert hash before the last extension\n                // e.g., addon.node -> addon-HASH.node\n                //       native-file.node-macos -> native-file-HASH.node-macos\n                const filename = path.basename(relativePath);\n                const lastDotIndex = filename.lastIndexOf(\".\");\n                const hashedFilename =\n                  lastDotIndex > 0\n                    ? `${filename.slice(\n                        0,\n                        lastDotIndex\n                      )}-${hash.toUpperCase()}${filename.slice(lastDotIndex)}`\n                    : `${filename}-${hash.toUpperCase()}`;\n\n                info = {\n                  content,\n                  hashedFilename,\n                  originalPath: absolutePath,\n                };\n                nativeFiles.set(absolutePath, info);\n              }\n\n              // Record the replacement\n              replacements.push({\n                start: literalNode.start,\n                end: literalNode.end,\n                value: `\"./${info.hashedFilename}\"`,\n              });\n              modified = true;\n            }\n          }\n\n          // Recursively walk child nodes\n          for (const key in node) {\n            if (key === \"type\" || key === \"start\" || key === \"end\") continue;\n            const child = (node as unknown as Record<string, unknown>)[key];\n            if (child && typeof child === \"object\") {\n              if (Array.isArray(child)) {\n                child.forEach((c) => {\n                  if (c && typeof c === \"object\" && \"type\" in c) {\n                    walk(c as BaseASTNode);\n                  }\n                });\n              } else if (\"type\" in child) {\n                walk(child as BaseASTNode);\n              }\n            }\n          }\n        };\n\n        walk(ast);\n\n        // Apply replacements in reverse order to maintain correct positions\n        if (modified) {\n          let newCode = code;\n          replacements\n            .sort((a, b) => b.start - a.start)\n            .forEach((replacement) => {\n              newCode =\n                newCode.slice(0, replacement.start) +\n                replacement.value +\n                newCode.slice(replacement.end);\n            });\n\n          return { code: newCode, map: null };\n        }\n      } catch (error) {\n        // If parsing fails, log and skip transformation\n        console.warn(\n          `Failed to parse ${id} for native module transformation:`,\n          error\n        );\n        return null;\n      }\n\n      return null;\n    },\n  };\n}\n"]}