{"version":3,"file":"writeFileIfChanged.mjs","names":[],"sources":["../../src/writeFileIfChanged.ts"],"sourcesContent":["import { createHash, randomBytes } from 'node:crypto';\nimport { createReadStream, rmSync } from 'node:fs';\nimport { chmod, mkdir, rename, rm, stat, writeFile } from 'node:fs/promises';\nimport { basename, join } from 'node:path';\n\nconst activeTempFiles = new Set<string>();\n\n// Synchronous cleanup on process exit\nprocess.on('exit', () => {\n  for (const file of activeTempFiles) {\n    try {\n      rmSync(file, { force: true });\n    } catch {}\n  }\n});\n\n// Helper to hash existing file via stream\nconst getFileHash = (path: string): Promise<string | null> => {\n  return new Promise((resolve) => {\n    const hash = createHash('sha256');\n    const stream = createReadStream(path);\n    stream.on('data', (chunk) => hash.update(chunk));\n    stream.on('end', () => resolve(hash.digest('hex')));\n    stream.on('error', () => resolve(null));\n  });\n};\n\nexport const writeFileIfChanged = async (\n  path: string,\n  data: string,\n  {\n    encoding = 'utf8',\n    tempDir,\n  }: { encoding?: BufferEncoding; tempDir?: string } = {}\n): Promise<boolean> => {\n  const newDataHash = createHash('sha256').update(data, encoding).digest('hex');\n  const existingHash = await getFileHash(path);\n\n  if (newDataHash === existingHash) {\n    return false;\n  }\n\n  if (tempDir) {\n    await mkdir(tempDir, { recursive: true });\n  }\n\n  const tempFileName = `${basename(path)}.${Date.now()}-${randomBytes(4).toString('hex')}.tmp`;\n  const tempPath = tempDir\n    ? join(tempDir, tempFileName)\n    : `${path}.${tempFileName}`;\n  activeTempFiles.add(tempPath);\n\n  try {\n    let mode: number | undefined;\n    try {\n      mode = (await stat(path)).mode;\n    } catch {}\n\n    await writeFile(tempPath, data, { encoding });\n\n    if (mode !== undefined) {\n      await chmod(tempPath, mode);\n    }\n\n    await rename(tempPath, path);\n  } catch (error) {\n    try {\n      await rm(tempPath, { force: true });\n    } catch {}\n    throw error;\n  } finally {\n    activeTempFiles.delete(tempPath);\n  }\n\n  return true;\n};\n"],"mappings":";;;;;;AAKA,MAAM,kCAAkB,IAAI,IAAY;AAGxC,QAAQ,GAAG,cAAc;CACvB,KAAK,MAAM,QAAQ,iBACjB,IAAI;EACF,OAAO,MAAM,EAAE,OAAO,KAAK,CAAC;CAC9B,QAAQ,CAAC;AAEb,CAAC;AAGD,MAAM,eAAe,SAAyC;CAC5D,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,OAAO,WAAW,QAAQ;EAChC,MAAM,SAAS,iBAAiB,IAAI;EACpC,OAAO,GAAG,SAAS,UAAU,KAAK,OAAO,KAAK,CAAC;EAC/C,OAAO,GAAG,aAAa,QAAQ,KAAK,OAAO,KAAK,CAAC,CAAC;EAClD,OAAO,GAAG,eAAe,QAAQ,IAAI,CAAC;CACxC,CAAC;AACH;AAEA,MAAa,qBAAqB,OAChC,MACA,MACA,EACE,WAAW,QACX,YACmD,CAAC,MACjC;CAIrB,IAHoB,WAAW,QAAQ,EAAE,OAAO,MAAM,QAAQ,EAAE,OAAO,KAGzD,MAAM,MAFO,YAAY,IAAI,GAGzC,OAAO;CAGT,IAAI,SACF,MAAM,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;CAG1C,MAAM,eAAe,GAAG,SAAS,IAAI,EAAE,GAAG,KAAK,IAAI,EAAE,GAAG,YAAY,CAAC,EAAE,SAAS,KAAK,EAAE;CACvF,MAAM,WAAW,UACb,KAAK,SAAS,YAAY,IAC1B,GAAG,KAAK,GAAG;CACf,gBAAgB,IAAI,QAAQ;CAE5B,IAAI;EACF,IAAI;EACJ,IAAI;GACF,QAAQ,MAAM,KAAK,IAAI,GAAG;EAC5B,QAAQ,CAAC;EAET,MAAM,UAAU,UAAU,MAAM,EAAE,SAAS,CAAC;EAE5C,IAAI,SAAS,QACX,MAAM,MAAM,UAAU,IAAI;EAG5B,MAAM,OAAO,UAAU,IAAI;CAC7B,SAAS,OAAO;EACd,IAAI;GACF,MAAM,GAAG,UAAU,EAAE,OAAO,KAAK,CAAC;EACpC,QAAQ,CAAC;EACT,MAAM;CACR,UAAU;EACR,gBAAgB,OAAO,QAAQ;CACjC;CAEA,OAAO;AACT"}