Repository: withastro/astro
File: packages/astro/src/assets/utils/node/emitAsset.ts (TypeScript)

Code before:
import { createHash } from 'node:crypto';
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import type * as vite from 'vite';
import { prependForwardSlash, slash } from '../../../core/path.js';
import type { ImageMetadata } from '../../types.js';
import { imageMetadata } from '../metadata.js';

type FileEmitter = vite.Rollup.EmitFile;
type ImageMetadataWithContents = ImageMetadata & { contents?: Buffer };

// Global cache for SVG content deduplication
// Maps content hash -> { handle: string, filename: string }
const svgContentCache = new Map<string, { handle: string; filename: string }>();

/**
 * Generate SHA-256 hash of file content for deduplication
 */
function getContentHash(fileData: Buffer): string {
	return createHash('sha256').update(fileData).digest('hex').slice(0, 16);
}

/**
 * Processes an image file and emits its metadata and optionally its contents. This function supports both build and development modes.
 *
 * @param {string | undefined} id - The identifier or path of the image file to process. If undefined, the function returns immediately.
 * @param {boolean} _watchMode - **Deprecated**: Indicates if the method is operating in watch mode. This parameter will be removed or updated in the future.
 * @param {boolean} _experimentalSvgEnabled - **Deprecated**: A flag to enable experimental handling of SVG files. Embeds SVG file data if set to true.
 * @param {FileEmitter | undefined} [fileEmitter] - Function for emitting files during the build process. May throw in certain scenarios.
 * @return {Promise<ImageMetadataWithContents | undefined>} Resolves to metadata with optional image contents or `undefined` if processing fails.
 */
// We want to internally use this function until we fix the memory in the SVG features
export async function emitESMImage(
	id: string | undefined,
	/** @deprecated */
	_watchMode: boolean,
	// FIX: in Astro 6, this function should not be passed in dev mode at all.
	// Or rethink the API so that a function that throws isn't passed through.
	/** @deprecated */

Review comment (performance):
Since this is a cache and we don't do any iteration, I think we can use a [`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) instead. However, a `WeakMap` needs an object as a key. So maybe we can create a new object e.g. `SvgCacheKey = { hash: string, fileData: Buffer }`. Not sure if we need `fileData`, so try to experiment

Code after:
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import type * as vite from 'vite';
import { generateContentHash } from '../../../core/encryption.js';
import { prependForwardSlash, slash } from '../../../core/path.js';
import type { ImageMetadata } from '../../types.js';
import { imageMetadata } from '../metadata.js';

type FileEmitter = vite.Rollup.EmitFile;
type ImageMetadataWithContents = ImageMetadata & { contents?: Buffer };

type SvgCacheKey = { hash: string };

// Global cache for SVG content deduplication
const svgContentCache = new WeakMap<SvgCacheKey, { handle: string; filename: string }>();

const keyRegistry = new Map<string, SvgCacheKey>();

function keyFor(hash: string): SvgCacheKey {
	let key = keyRegistry.get(hash);
	if (!key) {
		key = { hash };
		keyRegistry.set(hash, key);
	}
	return key;
}

/**
 * Handles SVG deduplication by checking if the content already exists in cache.
 */
async function handleSvgDeduplication(
	fileData: Buffer,
	filename: string,
	fileEmitter: FileEmitter
): Promise<string> {
	const contentHash = await generateContentHash(fileData);
	const key = keyFor(contentHash);
	const existing = svgContentCache.get(key);