{"version":3,"sources":["../src/index.ts","../src/transform.ts","../src/steps/fileHash.ts","../src/steps/buildOutputPath.ts","../src/steps/copyFile.ts","../src/steps/replaceNode.ts"],"sourcesContent":["import type { PluginObj, NodePath } from '@babel/core';\nimport type { ImportDeclaration, CallExpression } from '@babel/types';\nimport { transform } from './transform.js';\nimport type { PluginOptions, CopyCache } from './types.js';\n\nconst DEFAULT_EXTENSIONS = ['.gif', '.jpeg', '.jpg', '.png', '.svg'];\n\nfunction isRequireStatement(path: NodePath<CallExpression>): boolean {\n  const callee = path.get('callee');\n  return (\n    !Array.isArray(callee) &&\n    callee.isIdentifier() &&\n    callee.node.name === 'require'\n  );\n}\n\nfunction isValidArgument(path: NodePath<CallExpression>): boolean {\n  const args = path.get('arguments');\n  const arg = args[0];\n  return arg !== undefined && arg.isStringLiteral();\n}\n\ninterface PluginState {\n  opts: PluginOptions;\n  filename: string;\n  cwd: string;\n}\n\n// Module-level cache shared across all files in a build\nlet buildCache: CopyCache | null = null;\n\nexport default function plugin({\n  types: t,\n}: {\n  types: typeof import('@babel/types');\n}): PluginObj<PluginState> {\n  return {\n    name: 'transform-assets-import-to-string',\n    pre() {\n      // Initialize cache at start of build if not exists\n      if (!buildCache) {\n        buildCache = {\n          pathMap: new Map(),\n          outputMap: new Map(),\n        };\n      }\n    },\n    post() {\n      // Clear cache after build completes\n      // Note: This runs per-file, so we don't clear here\n      // Cache persists for the entire build\n    },\n    visitor: {\n      ImportDeclaration(\n        nodePath: NodePath<ImportDeclaration>,\n        state: PluginState\n      ) {\n        const opts: PluginOptions = {\n          baseUri: '',\n          extensions: DEFAULT_EXTENSIONS,\n          hashLength: 8,\n          ...state.opts,\n        };\n\n        const projectRoot = state.cwd || process.cwd();\n\n        transform(\n          {\n            path: nodePath,\n            filename: state.filename,\n            value: nodePath.node.source.value,\n            callee: 'import',\n          },\n          opts,\n          t,\n          buildCache!,\n          projectRoot\n        );\n      },\n      CallExpression(nodePath: NodePath<CallExpression>, state: PluginState) {\n        if (isRequireStatement(nodePath) && isValidArgument(nodePath)) {\n          const args = nodePath.get('arguments');\n          const arg = args[0];\n\n          if (!arg.isStringLiteral()) return;\n\n          const opts: PluginOptions = {\n            baseUri: '',\n            extensions: DEFAULT_EXTENSIONS,\n            hashLength: 8,\n            ...state.opts,\n          };\n\n          const projectRoot = state.cwd || process.cwd();\n\n          transform(\n            {\n              path: nodePath,\n              filename: state.filename,\n              value: arg.node.value,\n              callee: 'require',\n            },\n            opts,\n            t,\n            buildCache!,\n            projectRoot\n          );\n        }\n      },\n    },\n  };\n}\n\n// Export for testing - allows resetting cache between test runs\nexport function resetBuildCache(): void {\n  buildCache = null;\n}\n\nexport type { PluginOptions } from './types.js';\n","import path from 'node:path';\nimport type { types as t } from '@babel/core';\nimport { computeFileHash } from './steps/fileHash.js';\nimport { buildOutputPath } from './steps/buildOutputPath.js';\nimport { copyFile } from './steps/copyFile.js';\nimport { replaceNode } from './steps/replaceNode.js';\nimport type { PluginOptions, TransformScope, CopyCache } from './types.js';\n\nexport function transform(\n  scope: TransformScope,\n  options: PluginOptions,\n  types: typeof t,\n  cache: CopyCache,\n  projectRoot: string\n): void {\n  const ext = path.extname(scope.value);\n\n  if (!options.extensions || !options.extensions.includes(ext)) {\n    return;\n  }\n\n  const dir = path.dirname(path.resolve(scope.filename));\n  const absPath = path.resolve(dir, scope.value);\n\n  // Skip node_modules\n  if (absPath.includes('node_modules')) {\n    return;\n  }\n\n  // Compute content hash\n  const hashLength = options.hashLength ?? 8;\n  const hash = computeFileHash(absPath, hashLength);\n\n  // Build output path\n  const outputPath = buildOutputPath({\n    absPath,\n    hash,\n    preservePaths: options.preservePaths,\n    projectRoot,\n  });\n\n  // Copy file if outputDir is set\n  if (options.outputDir) {\n    copyFile({\n      absPath,\n      outputPath,\n      outputDir: options.outputDir,\n      cache,\n    });\n  }\n\n  // Build final URI\n  const baseUri = options.baseUri || '';\n  const separator = baseUri && !baseUri.endsWith('/') ? '/' : '';\n  const uri = `${baseUri}${separator}${outputPath}`;\n\n  replaceNode(scope, uri, types);\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\n\n/**\n * Compute SHA1 content hash of a file\n * @param absPath - Absolute path to the file\n * @param hashLength - Number of hex characters to return (0 = no hash)\n * @returns Hash string or empty string if hashLength is 0\n */\nexport function computeFileHash(absPath: string, hashLength: number): string {\n  if (hashLength === 0) {\n    return '';\n  }\n\n  const content = fs.readFileSync(absPath);\n\n  const hash = crypto\n    .createHash('sha1')\n    .update(content)\n    .digest('hex')\n    .slice(0, hashLength);\n\n  return hash;\n}\n","import path from 'node:path';\n\nexport interface BuildOutputPathOptions {\n  absPath: string;\n  hash: string;\n  preservePaths: string | undefined;\n  projectRoot: string;\n}\n\nexport function buildOutputPath(options: BuildOutputPathOptions): string {\n  const { absPath, hash, preservePaths, projectRoot } = options;\n\n  const ext = path.extname(absPath);\n  const basename = path.basename(absPath, ext);\n  const hashedName = hash ? `${basename}.${hash}${ext}` : `${basename}${ext}`;\n\n  if (!preservePaths) {\n    return hashedName;\n  }\n\n  // Normalize preservePaths: strip leading/trailing slashes\n  const normalizedBase = preservePaths.replace(/^\\/|\\/$/g, '');\n\n  // Get relative path from project root\n  const relativePath = path.relative(projectRoot, absPath);\n\n  // Find the preservePaths segment in the path\n  const segments = relativePath.split(path.sep);\n  const baseIndex = segments.indexOf(normalizedBase);\n\n  let dirPath: string;\n  if (baseIndex !== -1) {\n    // Strip everything up to and including the base\n    dirPath = segments.slice(baseIndex + 1, -1).join('/');\n  } else {\n    // Base not found, use full relative directory path\n    dirPath = path.dirname(relativePath).split(path.sep).join('/');\n  }\n\n  if (dirPath && dirPath !== '.') {\n    return `${dirPath}/${hashedName}`;\n  }\n\n  return hashedName;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { CopyCache } from '../types.js';\n\nexport interface CopyFileOptions {\n  absPath: string;\n  outputPath: string;\n  outputDir: string;\n  cache: CopyCache;\n}\n\nexport function copyFile(options: CopyFileOptions): void {\n  const { absPath, outputPath, outputDir, cache } = options;\n\n  // Check if already copied from this source\n  if (cache.pathMap.has(absPath)) {\n    return;\n  }\n\n  // Check for collision (different source, same output)\n  const existingSource = cache.outputMap.get(outputPath);\n  if (existingSource && existingSource !== absPath) {\n    throw new Error(\n      `Filename collision detected (hashLength is 0)\\n` +\n      `  - ${existingSource}\\n` +\n      `  - ${absPath}\\n` +\n      `  Both would output to: ${path.join(outputDir, outputPath)}\\n` +\n      `  Consider enabling hashLength or renaming one of the files.`\n    );\n  }\n\n  // Build full destination path\n  const destPath = path.join(outputDir, outputPath);\n  const destDir = path.dirname(destPath);\n\n  // Create directories if needed\n  if (!fs.existsSync(destDir)) {\n    fs.mkdirSync(destDir, { recursive: true });\n  }\n\n  // Copy the file\n  fs.copyFileSync(absPath, destPath);\n\n  // Update cache\n  cache.pathMap.set(absPath, outputPath);\n  cache.outputMap.set(outputPath, absPath);\n}\n","import type { types as t } from '@babel/core';\nimport type { TransformScope } from '../types.js';\n\nfunction getVariableName(node: t.ImportDeclaration): string | undefined {\n  if (node.specifiers?.[0]?.type === 'ImportDefaultSpecifier') {\n    return node.specifiers[0].local.name;\n  }\n  return undefined;\n}\n\nexport function replaceNode(\n  scope: TransformScope,\n  uri: string,\n  types: typeof t\n): void {\n  const content = types.stringLiteral(uri);\n\n  if (scope.callee === 'require') {\n    scope.path.replaceWith(content);\n    return;\n  }\n\n  const importPath = scope.path as import('@babel/core').NodePath<t.ImportDeclaration>;\n  const variableName = getVariableName(importPath.node);\n\n  if (variableName) {\n    scope.path.replaceWith(\n      types.variableDeclaration('const', [\n        types.variableDeclarator(types.identifier(variableName), content),\n      ])\n    );\n  }\n}\n"],"mappings":"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","names":["import_node_path","fs","crypto","path","import_node_fs","import_node_path","path","fs","path","path"]}