@intlayer/engine 9.1.3 → 9.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/assets/initConfig/templates/cjs.txt +4 -1
  2. package/dist/assets/initConfig/templates/json.txt +4 -1
  3. package/dist/assets/initConfig/templates/mjs.txt +4 -1
  4. package/dist/assets/initConfig/templates/ts.txt +4 -1
  5. package/dist/cjs/buildIntlayerDictionary/buildIntlayerDictionary.cjs +14 -2
  6. package/dist/cjs/buildIntlayerDictionary/buildIntlayerDictionary.cjs.map +1 -1
  7. package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs +24 -4
  8. package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs.map +1 -1
  9. package/dist/cjs/buildIntlayerDictionary/writeFetchDictionary.cjs +43 -6
  10. package/dist/cjs/buildIntlayerDictionary/writeFetchDictionary.cjs.map +1 -1
  11. package/dist/cjs/buildIntlayerDictionary/writeNestedDictionary.cjs +96 -0
  12. package/dist/cjs/buildIntlayerDictionary/writeNestedDictionary.cjs.map +1 -0
  13. package/dist/cjs/docReview/alignBlocks.cjs +57 -8
  14. package/dist/cjs/docReview/alignBlocks.cjs.map +1 -1
  15. package/dist/cjs/docReview/rebuildDocument.cjs +37 -9
  16. package/dist/cjs/docReview/rebuildDocument.cjs.map +1 -1
  17. package/dist/cjs/docReview/segmentDocument.cjs +19 -2
  18. package/dist/cjs/docReview/segmentDocument.cjs.map +1 -1
  19. package/dist/cjs/fetchDistantDictionaries.cjs +107 -13
  20. package/dist/cjs/fetchDistantDictionaries.cjs.map +1 -1
  21. package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs +1 -1
  22. package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs.map +1 -1
  23. package/dist/cjs/loadDictionaries/loadDictionaries.cjs +4 -2
  24. package/dist/cjs/loadDictionaries/loadDictionaries.cjs.map +1 -1
  25. package/dist/cjs/loadDictionaries/loadRemoteDictionaries.cjs +3 -2
  26. package/dist/cjs/loadDictionaries/loadRemoteDictionaries.cjs.map +1 -1
  27. package/dist/cjs/loadDictionaries/log.cjs +28 -5
  28. package/dist/cjs/loadDictionaries/log.cjs.map +1 -1
  29. package/dist/cjs/prepareIntlayer.cjs +7 -3
  30. package/dist/cjs/prepareIntlayer.cjs.map +1 -1
  31. package/dist/cjs/utils/chunkArray.cjs +24 -0
  32. package/dist/cjs/utils/chunkArray.cjs.map +1 -0
  33. package/dist/cjs/utils/index.cjs +2 -0
  34. package/dist/cjs/utils/runOnce.cjs +129 -50
  35. package/dist/cjs/utils/runOnce.cjs.map +1 -1
  36. package/dist/cjs/writeContentDeclaration/transformJSFile.cjs +153 -92
  37. package/dist/cjs/writeContentDeclaration/transformJSFile.cjs.map +1 -1
  38. package/dist/cjs/writeFileIfChanged.cjs +14 -7
  39. package/dist/cjs/writeFileIfChanged.cjs.map +1 -1
  40. package/dist/esm/buildIntlayerDictionary/buildIntlayerDictionary.mjs +15 -3
  41. package/dist/esm/buildIntlayerDictionary/buildIntlayerDictionary.mjs.map +1 -1
  42. package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs +24 -4
  43. package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs.map +1 -1
  44. package/dist/esm/buildIntlayerDictionary/writeFetchDictionary.mjs +43 -6
  45. package/dist/esm/buildIntlayerDictionary/writeFetchDictionary.mjs.map +1 -1
  46. package/dist/esm/buildIntlayerDictionary/writeNestedDictionary.mjs +92 -0
  47. package/dist/esm/buildIntlayerDictionary/writeNestedDictionary.mjs.map +1 -0
  48. package/dist/esm/docReview/alignBlocks.mjs +57 -8
  49. package/dist/esm/docReview/alignBlocks.mjs.map +1 -1
  50. package/dist/esm/docReview/rebuildDocument.mjs +37 -9
  51. package/dist/esm/docReview/rebuildDocument.mjs.map +1 -1
  52. package/dist/esm/docReview/segmentDocument.mjs +19 -2
  53. package/dist/esm/docReview/segmentDocument.mjs.map +1 -1
  54. package/dist/esm/fetchDistantDictionaries.mjs +107 -13
  55. package/dist/esm/fetchDistantDictionaries.mjs.map +1 -1
  56. package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs +1 -1
  57. package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs.map +1 -1
  58. package/dist/esm/loadDictionaries/loadDictionaries.mjs +4 -2
  59. package/dist/esm/loadDictionaries/loadDictionaries.mjs.map +1 -1
  60. package/dist/esm/loadDictionaries/loadRemoteDictionaries.mjs +3 -2
  61. package/dist/esm/loadDictionaries/loadRemoteDictionaries.mjs.map +1 -1
  62. package/dist/esm/loadDictionaries/log.mjs +28 -5
  63. package/dist/esm/loadDictionaries/log.mjs.map +1 -1
  64. package/dist/esm/prepareIntlayer.mjs +7 -3
  65. package/dist/esm/prepareIntlayer.mjs.map +1 -1
  66. package/dist/esm/utils/chunkArray.mjs +22 -0
  67. package/dist/esm/utils/chunkArray.mjs.map +1 -0
  68. package/dist/esm/utils/index.mjs +2 -1
  69. package/dist/esm/utils/runOnce.mjs +129 -50
  70. package/dist/esm/utils/runOnce.mjs.map +1 -1
  71. package/dist/esm/writeContentDeclaration/transformJSFile.mjs +153 -92
  72. package/dist/esm/writeContentDeclaration/transformJSFile.mjs.map +1 -1
  73. package/dist/esm/writeFileIfChanged.mjs +15 -8
  74. package/dist/esm/writeFileIfChanged.mjs.map +1 -1
  75. package/dist/types/buildIntlayerDictionary/buildIntlayerDictionary.d.ts.map +1 -1
  76. package/dist/types/buildIntlayerDictionary/writeDynamicDictionary.d.ts +14 -2
  77. package/dist/types/buildIntlayerDictionary/writeDynamicDictionary.d.ts.map +1 -1
  78. package/dist/types/buildIntlayerDictionary/writeFetchDictionary.d.ts +16 -2
  79. package/dist/types/buildIntlayerDictionary/writeFetchDictionary.d.ts.map +1 -1
  80. package/dist/types/buildIntlayerDictionary/writeNestedDictionary.d.ts +54 -0
  81. package/dist/types/buildIntlayerDictionary/writeNestedDictionary.d.ts.map +1 -0
  82. package/dist/types/docReview/alignBlocks.d.ts +4 -2
  83. package/dist/types/docReview/alignBlocks.d.ts.map +1 -1
  84. package/dist/types/docReview/rebuildDocument.d.ts.map +1 -1
  85. package/dist/types/docReview/segmentDocument.d.ts.map +1 -1
  86. package/dist/types/docReview/types.d.ts +9 -0
  87. package/dist/types/docReview/types.d.ts.map +1 -1
  88. package/dist/types/fetchDistantDictionaries.d.ts +26 -0
  89. package/dist/types/fetchDistantDictionaries.d.ts.map +1 -1
  90. package/dist/types/formatDictionary.d.ts +2 -0
  91. package/dist/types/formatDictionary.d.ts.map +1 -1
  92. package/dist/types/loadDictionaries/log.d.ts +16 -1
  93. package/dist/types/loadDictionaries/log.d.ts.map +1 -1
  94. package/dist/types/utils/chunkArray.d.ts +16 -0
  95. package/dist/types/utils/chunkArray.d.ts.map +1 -0
  96. package/dist/types/utils/index.d.ts +3 -2
  97. package/dist/types/utils/runOnce.d.ts +30 -6
  98. package/dist/types/utils/runOnce.d.ts.map +1 -1
  99. package/dist/types/writeContentDeclaration/transformJSFile.d.ts.map +1 -1
  100. package/package.json +11 -11
@@ -1 +1 @@
1
- {"version":3,"file":"runOnce.cjs","names":["packageJson"],"sources":["../../../src/utils/runOnce.ts"],"sourcesContent":["import { mkdir, readFile, stat, unlink, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport packageJson from '@intlayer/core/package.json' with { type: 'json' };\n\ntype RunOnceOptions = {\n /**\n * The function to execute when the sentinel is not found or is older than the cache timeout.\n */\n onIsCached?: () => void | Promise<void>;\n /**\n * The time window in milliseconds during which the sentinel is considered valid.\n *\n * @default 60000 = 1 minute\n */\n cacheTimeoutMs?: number;\n /**\n * If true, the callback will always run. If undefined, the callback will run only if the sentinel is older than the cache timeout.\n *\n * @default false\n */\n forceRun?: boolean;\n};\n\nconst DEFAULT_RUN_ONCE_OPTIONS = {\n cacheTimeoutMs: 60 * 1000, // 1 minute in milliseconds,\n} satisfies RunOnceOptions;\n\ntype SentinelData = {\n version: string;\n timestamp: number;\n};\n\nconst writeSentinelFile = async (\n sentinelFilePath: string,\n currentTimestamp: number\n) => {\n // O_EXCL ensures only the *first* process can create the file\n const data: SentinelData = {\n version: packageJson.version,\n timestamp: currentTimestamp,\n };\n\n try {\n // Ensure the directory exists before writing the file\n await mkdir(dirname(sentinelFilePath), { recursive: true });\n\n await writeFile(sentinelFilePath, JSON.stringify(data), { flag: 'wx' });\n } catch (err: any) {\n if (err.code === 'EEXIST') {\n // Another process already created it → we're done\n return;\n }\n // Optimization: If ENOENT occurs on write despite mkdir (race condition with external deletion), retry once.\n if (err.code === 'ENOENT') {\n try {\n await mkdir(dirname(sentinelFilePath), { recursive: true });\n await writeFile(sentinelFilePath, JSON.stringify(data), { flag: 'wx' });\n return;\n } catch (retryErr: any) {\n if (retryErr.code === 'EEXIST') return;\n }\n }\n throw err; // unexpected FS error\n }\n};\n\n/**\n * Ensures a callback function runs only once within a specified time window across multiple processes.\n * Uses a sentinel file to coordinate execution and prevent duplicate work.\n *\n * @param sentinelFilePath - Path to the sentinel file used for coordination\n * @param callback - The function to execute (should be async)\n * @param options - The options for the runOnce function\n *\n * @example\n * ```typescript\n * await runPrepareIntlayerOnce(\n * '/tmp/intlayer-sentinel',\n * async () => {\n * // Your initialization logic here\n * await prepareIntlayer();\n * },\n * 30 * 1000 // 30 seconds cache\n * );\n * ```\n *\n * @throws {Error} When there are unexpected filesystem errors\n */\nexport const runOnce = async (\n sentinelFilePath: string,\n callback: () => void | Promise<void>,\n options?: RunOnceOptions\n) => {\n const { onIsCached, cacheTimeoutMs, forceRun } = {\n ...DEFAULT_RUN_ONCE_OPTIONS,\n ...(options ?? {}),\n };\n const currentTimestamp = Date.now();\n\n try {\n // Check if sentinel file exists and get its stats\n const sentinelStats = await stat(sentinelFilePath);\n const sentinelAge = currentTimestamp - sentinelStats.mtime.getTime();\n\n // Determine if we should rebuild based on cache age, force flag, or version mismatch\n let shouldRebuild = Boolean(forceRun) || sentinelAge > cacheTimeoutMs!;\n\n if (!shouldRebuild) {\n try {\n const raw = await readFile(sentinelFilePath, 'utf8');\n let cachedVersion: string | undefined;\n try {\n const parsed = JSON.parse(raw) as Partial<SentinelData>;\n cachedVersion = parsed.version;\n } catch {\n // Legacy format (timestamp only). Force a rebuild once to write versioned sentinel.\n cachedVersion = undefined;\n }\n\n if (!cachedVersion || cachedVersion !== packageJson.version) {\n shouldRebuild = true;\n }\n } catch {\n // If we cannot read the file, err on the safe side and rebuild\n shouldRebuild = true;\n }\n }\n\n if (shouldRebuild) {\n try {\n await unlink(sentinelFilePath);\n } catch {}\n // Fall through to create new sentinel and rebuild\n } else {\n await onIsCached?.();\n // Sentinel is recent and versions match, no need to rebuild\n return;\n }\n } catch (err: any) {\n if (err.code === 'ENOENT') {\n // File doesn't exist, continue to create it\n } else {\n throw err; // unexpected FS error\n }\n }\n\n // Write sentinel file before to block parallel processes\n // Added await here\n await writeSentinelFile(sentinelFilePath, currentTimestamp);\n\n try {\n await callback();\n\n // Write sentinel file after to ensure the first one has not been removed with cleanOutputDir\n // Added await here\n await writeSentinelFile(sentinelFilePath, currentTimestamp);\n } catch {\n try {\n await unlink(sentinelFilePath); // Remove sentinel file if an error occurs\n } catch {}\n }\n};\n"],"mappings":";;;;;;;;AAuBA,MAAM,2BAA2B,EAC/B,gBAAgB,KAAK,IACvB;AAOA,MAAM,oBAAoB,OACxB,kBACA,qBACG;CAEH,MAAM,OAAqB;EACzB,SAASA,oCAAY;EACrB,WAAW;CACb;CAEA,IAAI;EAEF,yDAAoB,gBAAgB,GAAG,EAAE,WAAW,KAAK,CAAC;EAE1D,sCAAgB,kBAAkB,KAAK,UAAU,IAAI,GAAG,EAAE,MAAM,KAAK,CAAC;CACxE,SAAS,KAAU;EACjB,IAAI,IAAI,SAAS,UAEf;EAGF,IAAI,IAAI,SAAS,UACf,IAAI;GACF,yDAAoB,gBAAgB,GAAG,EAAE,WAAW,KAAK,CAAC;GAC1D,sCAAgB,kBAAkB,KAAK,UAAU,IAAI,GAAG,EAAE,MAAM,KAAK,CAAC;GACtE;EACF,SAAS,UAAe;GACtB,IAAI,SAAS,SAAS,UAAU;EAClC;EAEF,MAAM;CACR;AACF;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,UAAU,OACrB,kBACA,UACA,YACG;CACH,MAAM,EAAE,YAAY,gBAAgB,aAAa;EAC/C,GAAG;EACH,GAAI,WAAW,CAAC;CAClB;CACA,MAAM,mBAAmB,KAAK,IAAI;CAElC,IAAI;EAGF,MAAM,cAAc,oBAAmB,iCADN,gBAAgB,EACG,CAAC,MAAM,QAAQ;EAGnE,IAAI,gBAAgB,QAAQ,QAAQ,KAAK,cAAc;EAEvD,IAAI,CAAC,eACH,IAAI;GACF,MAAM,MAAM,qCAAe,kBAAkB,MAAM;GACnD,IAAI;GACJ,IAAI;IAEF,gBADe,KAAK,MAAM,GACL,CAAC,CAAC;GACzB,QAAQ;IAEN,gBAAgB;GAClB;GAEA,IAAI,CAAC,iBAAiB,kBAAkBA,oCAAY,SAClD,gBAAgB;EAEpB,QAAQ;GAEN,gBAAgB;EAClB;EAGF,IAAI,eACF,IAAI;GACF,mCAAa,gBAAgB;EAC/B,QAAQ,CAAC;OAEJ;GACL,MAAM,aAAa;GAEnB;EACF;CACF,SAAS,KAAU;EACjB,IAAI,IAAI,SAAS,UAAU,CAE3B,OACE,MAAM;CAEV;CAIA,MAAM,kBAAkB,kBAAkB,gBAAgB;CAE1D,IAAI;EACF,MAAM,SAAS;EAIf,MAAM,kBAAkB,kBAAkB,gBAAgB;CAC5D,QAAQ;EACN,IAAI;GACF,mCAAa,gBAAgB;EAC/B,QAAQ,CAAC;CACX;AACF"}
1
+ {"version":3,"file":"runOnce.cjs","names":["packageJson"],"sources":["../../../src/utils/runOnce.ts"],"sourcesContent":["import { rmSync } from 'node:fs';\nimport { mkdir, readFile, stat, unlink, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport packageJson from '@intlayer/core/package.json' with { type: 'json' };\n\n/**\n * Lifecycle of the run described by a sentinel file.\n *\n * - `running` — a process owns the sentinel and its callback is still in flight.\n * Other processes must wait instead of starting a concurrent run.\n * - `done` — the callback completed; the sentinel is now a plain cache marker.\n */\ntype SentinelStatus = 'running' | 'done';\n\ntype SentinelData = {\n version: string;\n timestamp: number;\n status: SentinelStatus;\n /** PID of the process that owns the sentinel, used to detect abandoned locks. */\n pid: number;\n};\n\n/** Sentinel state as read from disk, enriched with the file's modification time. */\ntype SentinelState = SentinelData & { mtimeMs: number };\n\n/**\n * Context handed to the callback so it can interact with the lock it runs under.\n */\nexport type RunOnceContext = {\n /**\n * Re-create the sentinel file after an operation that may have deleted it —\n * typically cleaning the output directory, which wipes the cache directory the\n * sentinel lives in. Without this, concurrent processes would see no lock and\n * start a competing run while this one is still writing.\n */\n renewLock: () => Promise<void>;\n};\n\ntype RunOnceOptions = {\n /**\n * The function to execute when the sentinel is not found or is older than the cache timeout.\n */\n onIsCached?: () => void | Promise<void>;\n /**\n * The time window in milliseconds during which the sentinel is considered valid.\n *\n * @default 60000 = 1 minute\n */\n cacheTimeoutMs?: number;\n /**\n * If true, the callback will always run. If undefined, the callback will run only if the sentinel is older than the cache timeout.\n *\n * @default false\n */\n forceRun?: boolean;\n /**\n * How long to wait for another process to release the sentinel before\n * considering its run abandoned and taking the lock over.\n *\n * @default 300000 = 5 minutes\n */\n lockWaitTimeoutMs?: number;\n};\n\nconst DEFAULT_RUN_ONCE_OPTIONS = {\n cacheTimeoutMs: 60 * 1000, // 1 minute in milliseconds,\n lockWaitTimeoutMs: 5 * 60 * 1000, // 5 minutes in milliseconds\n} satisfies RunOnceOptions;\n\n/** Delay between two reads of a sentinel owned by another process. */\nconst LOCK_POLL_INTERVAL_MS = 50;\n\nconst delay = (durationMs: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, durationMs));\n\n/**\n * Sentinels owned by this process, released synchronously on exit so a crash or\n * a Ctrl-C never leaves a `running` lock that would stall the next run.\n */\nconst ownedSentinelFilePaths = new Set<string>();\n\nprocess.on('exit', () => {\n for (const sentinelFilePath of ownedSentinelFilePaths) {\n try {\n rmSync(sentinelFilePath, { force: true });\n } catch {}\n }\n});\n\n/**\n * Reads the sentinel file, returning `undefined` when it does not exist or\n * cannot be parsed.\n *\n * Sentinels written by older versions carry no `status`; they always describe a\n * finished run, so they are reported as `done`.\n */\nconst readSentinelState = async (\n sentinelFilePath: string\n): Promise<SentinelState | undefined> => {\n try {\n const [sentinelStats, raw] = await Promise.all([\n stat(sentinelFilePath),\n readFile(sentinelFilePath, 'utf8'),\n ]);\n\n const parsed = JSON.parse(raw) as Partial<SentinelData>;\n\n return {\n version: parsed.version ?? '',\n timestamp: parsed.timestamp ?? 0,\n status: parsed.status ?? 'done',\n pid: parsed.pid ?? 0,\n mtimeMs: sentinelStats.mtime.getTime(),\n };\n } catch {\n return undefined;\n }\n};\n\n/**\n * Whether the process that wrote the sentinel is still alive. An unknown PID\n * (legacy sentinel) is assumed alive so the staleness timeout stays the only\n * way to reclaim it.\n */\nconst isOwnerProcessAlive = (pid: number): boolean => {\n if (!pid || pid === process.pid) return true;\n\n try {\n // Signal 0 performs an existence check without delivering a signal.\n process.kill(pid, 0);\n return true;\n } catch (error) {\n // EPERM means the process exists but belongs to another user.\n return (error as NodeJS.ErrnoException).code === 'EPERM';\n }\n};\n\nconst serializeSentinel = (timestamp: number, status: SentinelStatus): string =>\n JSON.stringify({\n version: packageJson.version,\n timestamp,\n status,\n pid: process.pid,\n } satisfies SentinelData);\n\n/**\n * Attempts to take ownership of the sentinel.\n *\n * `wx` makes the creation atomic, so exactly one process can win even when\n * several start at the same moment.\n *\n * @returns `true` when this process now owns the sentinel, `false` when another\n * process created it first.\n */\nconst acquireSentinel = async (\n sentinelFilePath: string,\n timestamp: number\n): Promise<boolean> => {\n const data = serializeSentinel(timestamp, 'running');\n\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n // Ensure the directory exists before writing the file\n await mkdir(dirname(sentinelFilePath), { recursive: true });\n\n await writeFile(sentinelFilePath, data, { flag: 'wx' });\n return true;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n\n if (code === 'EEXIST') return false;\n // The directory was removed between the mkdir and the write (e.g. a\n // concurrent output-directory clean); retry once.\n if (code === 'ENOENT' && attempt === 0) continue;\n\n throw error;\n }\n }\n\n return false;\n};\n\n/**\n * Rewrites the sentinel owned by this process, overwriting any existing file.\n */\nconst writeOwnedSentinel = async (\n sentinelFilePath: string,\n timestamp: number,\n status: SentinelStatus\n): Promise<void> => {\n try {\n await mkdir(dirname(sentinelFilePath), { recursive: true });\n await writeFile(sentinelFilePath, serializeSentinel(timestamp, status));\n } catch {}\n};\n\nconst removeSentinel = async (sentinelFilePath: string): Promise<void> => {\n try {\n await unlink(sentinelFilePath);\n } catch {}\n};\n\n/**\n * Ensures a callback function runs only once within a specified time window across multiple processes.\n * Uses a sentinel file to coordinate execution and prevent duplicate work.\n *\n * Processes that lose the race for the sentinel wait for the owner to finish\n * rather than running a competing copy of the callback — concurrent runs would\n * otherwise write to (and clean) the same output directory at the same time.\n *\n * @param sentinelFilePath - Path to the sentinel file used for coordination\n * @param callback - The function to execute (should be async)\n * @param options - The options for the runOnce function\n *\n * @example\n * ```typescript\n * await runOnce(\n * '/tmp/intlayer-sentinel',\n * async ({ renewLock }) => {\n * await cleanOutputDir(configuration); // may delete the sentinel\n * await renewLock();\n * await prepareIntlayer();\n * },\n * { cacheTimeoutMs: 30 * 1000 } // 30 seconds cache\n * );\n * ```\n *\n * @throws {Error} When there are unexpected filesystem errors\n */\nexport const runOnce = async (\n sentinelFilePath: string,\n callback: (context: RunOnceContext) => void | Promise<void>,\n options?: RunOnceOptions\n) => {\n const { onIsCached, cacheTimeoutMs, forceRun, lockWaitTimeoutMs } = {\n ...DEFAULT_RUN_ONCE_OPTIONS,\n ...(options ?? {}),\n };\n const currentTimestamp = Date.now();\n const waitDeadline = currentTimestamp + lockWaitTimeoutMs;\n\n // Acquisition loop: read the sentinel, then either return early (fresh cache),\n // wait for the current owner, or take the lock. Every branch either returns or\n // makes progress, so the loop always terminates.\n while (true) {\n const sentinelState = await readSentinelState(sentinelFilePath);\n\n if (sentinelState) {\n const sentinelAge = Date.now() - sentinelState.mtimeMs;\n\n if (sentinelState.status === 'running') {\n const isAbandoned =\n sentinelAge > lockWaitTimeoutMs ||\n !isOwnerProcessAlive(sentinelState.pid);\n\n if (!isAbandoned && Date.now() < waitDeadline) {\n await delay(LOCK_POLL_INTERVAL_MS);\n continue;\n }\n\n // The owner died or overran the timeout: reclaim the sentinel.\n await removeSentinel(sentinelFilePath);\n continue;\n }\n\n const isCacheValid =\n !forceRun &&\n sentinelAge <= cacheTimeoutMs &&\n sentinelState.version === packageJson.version;\n\n if (isCacheValid) {\n await onIsCached?.();\n return;\n }\n\n await removeSentinel(sentinelFilePath);\n }\n\n const hasAcquiredSentinel = await acquireSentinel(\n sentinelFilePath,\n currentTimestamp\n );\n\n if (hasAcquiredSentinel) break;\n\n // Another process won the race in the meantime: loop back and wait for it.\n // The delay also guarantees the loop yields, so a sentinel being repeatedly\n // created and removed can never turn into a busy wait.\n await delay(LOCK_POLL_INTERVAL_MS);\n }\n\n ownedSentinelFilePaths.add(sentinelFilePath);\n\n const renewLock = () =>\n writeOwnedSentinel(sentinelFilePath, currentTimestamp, 'running');\n\n try {\n await callback({ renewLock });\n\n // Mark the run as finished, re-creating the sentinel if the callback\n // deleted it (e.g. by cleaning the output directory).\n await writeOwnedSentinel(sentinelFilePath, currentTimestamp, 'done');\n } catch {\n await removeSentinel(sentinelFilePath); // Remove sentinel file if an error occurs\n } finally {\n ownedSentinelFilePaths.delete(sentinelFilePath);\n }\n};\n"],"mappings":"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"}
@@ -11,6 +11,81 @@ recast = require_runtime.__toESM(recast);
11
11
  const b = recast.types.builders;
12
12
  const n = recast.types.namedTypes;
13
13
  /**
14
+ * Every {@link NodeType} and how it is written back to a JS/TS declaration file.
15
+ *
16
+ * Typed as `Record<NodeType, NodeTypeStrategy>` so that adding a node type to
17
+ * `@intlayer/types` without registering it here becomes a compile-time error —
18
+ * the same guarantee `getNodeType` gives on the read side.
19
+ */
20
+ const nodeTypeStrategies = {
21
+ [_intlayer_types_nodeType.TRANSLATION]: {
22
+ kind: "helperRecord",
23
+ helper: "t"
24
+ },
25
+ [_intlayer_types_nodeType.ENUMERATION]: {
26
+ kind: "helperRecord",
27
+ helper: "enu"
28
+ },
29
+ [_intlayer_types_nodeType.PLURAL]: {
30
+ kind: "helperRecord",
31
+ helper: "plural"
32
+ },
33
+ [_intlayer_types_nodeType.CONDITION]: {
34
+ kind: "helperRecord",
35
+ helper: "cond"
36
+ },
37
+ [_intlayer_types_nodeType.GENDER]: {
38
+ kind: "helperRecord",
39
+ helper: "gender"
40
+ },
41
+ [_intlayer_types_nodeType.SELECT]: {
42
+ kind: "helperRecord",
43
+ helper: "select",
44
+ getExtraArguments: (node) => typeof node.variable === "string" && node.variable.length > 0 ? [b.literal(node.variable)] : []
45
+ },
46
+ [_intlayer_types_nodeType.INSERTION]: {
47
+ kind: "helperValue",
48
+ helper: "insert"
49
+ },
50
+ [_intlayer_types_nodeType.MARKDOWN]: {
51
+ kind: "helperValue",
52
+ helper: "md"
53
+ },
54
+ [_intlayer_types_nodeType.HTML]: {
55
+ kind: "helperValue",
56
+ helper: "html"
57
+ },
58
+ [_intlayer_types_nodeType.FILE]: {
59
+ kind: "helperValue",
60
+ helper: "file"
61
+ },
62
+ [_intlayer_types_nodeType.NESTED]: {
63
+ kind: "nesting",
64
+ helper: "nest"
65
+ },
66
+ [_intlayer_types_nodeType.TEXT]: { kind: "unwrap" },
67
+ [_intlayer_types_nodeType.NUMBER]: { kind: "unwrap" },
68
+ [_intlayer_types_nodeType.BOOLEAN]: { kind: "unwrap" },
69
+ [_intlayer_types_nodeType.NULL]: { kind: "unwrap" },
70
+ [_intlayer_types_nodeType.REACT_NODE]: { kind: "preserve" },
71
+ [_intlayer_types_nodeType.PREACT_NODE]: { kind: "preserve" },
72
+ [_intlayer_types_nodeType.SOLID_NODE]: { kind: "preserve" },
73
+ [_intlayer_types_nodeType.OBJECT]: { kind: "structural" },
74
+ [_intlayer_types_nodeType.ARRAY]: { kind: "structural" },
75
+ [_intlayer_types_nodeType.UNKNOWN]: { kind: "structural" }
76
+ };
77
+ /** Every `intlayer` helper the transform is allowed to rewrite. */
78
+ const helperNames = new Set(Object.values(nodeTypeStrategies).flatMap((strategy) => "helper" in strategy ? [strategy.helper] : []));
79
+ /** Helpers wrapping a single content, e.g. `md(t({ … }))`. */
80
+ const wrapperHelperNames = new Set(Object.values(nodeTypeStrategies).flatMap((strategy) => strategy.kind === "helperValue" ? [strategy.helper] : []));
81
+ /**
82
+ * Returned by {@link buildNodeForValue} when a value must not be written to the
83
+ * source file — framework elements and functions have no data representation,
84
+ * so the original expression (when there is one) is left untouched and no new
85
+ * property is created otherwise.
86
+ */
87
+ const SKIP_WRITE = Symbol("intlayer.skipWrite");
88
+ /**
14
89
  * Unwraps TypeScript/Babel expression wrappers (satisfies, as, !, <Type>).
15
90
  * Uses string fallbacks to bypass outdated ast-types definitions.
16
91
  */
@@ -65,39 +140,51 @@ const syncNumericSuffixAcrossLocales = (existingNode, fallbackLocaleCode, newFal
65
140
  };
66
141
  /**
67
142
  * Checks if a value represents a multilingual Intlayer node.
68
- * A node is multilingual if it is a Translation node, or if it is a specialized node
69
- * (Markdown, HTML, etc.) that contains a Translation node.
143
+ *
144
+ * A node is multilingual if it is a Translation node, if it wraps one
145
+ * (Markdown, HTML, Insertion…), or if any of its variants contains one
146
+ * (Enumeration, Plural, Condition, Gender, Select…).
70
147
  */
71
148
  const isMultilingualNode = (val) => {
72
149
  if (typeof val !== "object" || val === null || Array.isArray(val)) return false;
73
150
  const nodeType = (0, _intlayer_core_dictionaryManipulator.getNodeType)(val);
74
151
  if (nodeType === _intlayer_types_nodeType.TRANSLATION) return true;
75
- if (nodeType === _intlayer_types_nodeType.MARKDOWN || nodeType === _intlayer_types_nodeType.HTML || nodeType === _intlayer_types_nodeType.INSERTION) return isMultilingualNode(val[nodeType]);
76
- if (nodeType === _intlayer_types_nodeType.ENUMERATION || nodeType === _intlayer_types_nodeType.PLURAL || nodeType === _intlayer_types_nodeType.CONDITION || nodeType === _intlayer_types_nodeType.GENDER || nodeType === _intlayer_types_nodeType.SELECT) {
77
- const data = val[nodeType];
78
- if (data && typeof data === "object") return Object.values(data).some((v) => isMultilingualNode(v));
79
- }
152
+ const strategy = nodeTypeStrategies[nodeType];
153
+ const nodeData = val[nodeType];
154
+ if (strategy.kind === "helperValue" || strategy.kind === "unwrap") return isMultilingualNode(nodeData);
155
+ if (strategy.kind === "helperRecord" && nodeData && typeof nodeData === "object") return Object.values(nodeData).some((variant) => isMultilingualNode(variant));
80
156
  return false;
81
157
  };
82
158
  /**
159
+ * Checks whether two AST nodes are literals holding the same value.
160
+ */
161
+ const isEquivalentLiteral = (left, right) => {
162
+ const isLiteral = (node) => n.Literal.check(node) || n.StringLiteral.check(node) || n.NumericLiteral.check(node) || n.BooleanLiteral.check(node);
163
+ return isLiteral(left) && isLiteral(right) && left.value === right.value;
164
+ };
165
+ /**
166
+ * Aligns the arguments of an existing call expression with the desired ones.
167
+ *
168
+ * Mutates the call only when the arguments actually differ, so Recast keeps the
169
+ * original formatting (and quote style) of calls that did not change.
170
+ */
171
+ const syncCallArguments = (callNode, desiredArguments) => {
172
+ if (!(callNode.arguments.length === desiredArguments.length && desiredArguments.every((desired, index) => {
173
+ const current = callNode.arguments[index];
174
+ return current === desired || isEquivalentLiteral(current, desired);
175
+ }))) callNode.arguments = desiredArguments;
176
+ };
177
+ /**
83
178
  * Recursively builds or updates an AST node for a given dictionary value.
179
+ *
180
+ * Returns {@link SKIP_WRITE} when the value cannot be represented in source and
181
+ * no existing expression can be kept.
84
182
  */
85
183
  const buildNodeForValue = (val, existingNode, fallbackLocale, requiredImports) => {
184
+ if (val === void 0 || typeof val === "function") return existingNode ?? SKIP_WRITE;
86
185
  const unwrappedExisting = unwrap(existingNode);
87
186
  if (unwrappedExisting) {
88
- if (!(n.Literal.check(unwrappedExisting) || n.StringLiteral.check(unwrappedExisting) || n.NumericLiteral.check(unwrappedExisting) || n.BooleanLiteral.check(unwrappedExisting) || n.TemplateLiteral.check(unwrappedExisting) || n.ObjectExpression.check(unwrappedExisting) || n.ArrayExpression.check(unwrappedExisting) || n.CallExpression.check(unwrappedExisting) && n.Identifier.check(unwrappedExisting.callee) && [
89
- "t",
90
- "enu",
91
- "plural",
92
- "cond",
93
- "gender",
94
- "select",
95
- "insert",
96
- "md",
97
- "html",
98
- "file",
99
- "nest"
100
- ].includes(unwrappedExisting.callee.name))) return existingNode;
187
+ if (!(n.Literal.check(unwrappedExisting) || n.StringLiteral.check(unwrappedExisting) || n.NumericLiteral.check(unwrappedExisting) || n.BooleanLiteral.check(unwrappedExisting) || n.TemplateLiteral.check(unwrappedExisting) || n.ObjectExpression.check(unwrappedExisting) || n.ArrayExpression.check(unwrappedExisting) || n.CallExpression.check(unwrappedExisting) && n.Identifier.check(unwrappedExisting.callee) && helperNames.has(unwrappedExisting.callee.name))) return existingNode;
101
188
  }
102
189
  if (fallbackLocale && !existingNode && !isMultilingualNode(val)) {
103
190
  if (val === null) return b.literal(null);
@@ -119,14 +206,15 @@ const buildNodeForValue = (val, existingNode, fallbackLocale, requiredImports) =
119
206
  return existingNode;
120
207
  }
121
208
  }
122
- if ((!val || typeof val !== "object") && n.CallExpression.check(existingNode) && n.Identifier.check(existingNode.callee) && existingNode.callee.name === "md") {
209
+ if ((!val || typeof val !== "object") && n.CallExpression.check(existingNode) && n.Identifier.check(existingNode.callee) && wrapperHelperNames.has(existingNode.callee.name)) {
210
+ const wrapperName = existingNode.callee.name;
123
211
  const innerArg = existingNode.arguments[0];
124
212
  if (n.CallExpression.check(innerArg) && n.Identifier.check(innerArg.callee) && innerArg.callee.name === "t") {
125
213
  const tArg = unwrap(innerArg.arguments[0]);
126
214
  if (n.ObjectExpression.check(tArg)) {
127
215
  if (typeof val === "string") syncNumericSuffixAcrossLocales(tArg, fallbackLocale, val);
128
216
  updateObjectLiteral(tArg, { [fallbackLocale]: val }, fallbackLocale, requiredImports);
129
- requiredImports.add("md");
217
+ requiredImports.add(wrapperName);
130
218
  requiredImports.add("t");
131
219
  return existingNode;
132
220
  }
@@ -136,7 +224,7 @@ const buildNodeForValue = (val, existingNode, fallbackLocale, requiredImports) =
136
224
  if (val === null) return b.literal(null);
137
225
  if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
138
226
  if (unwrappedExisting) {
139
- if (n.TemplateLiteral.check(unwrappedExisting) && unwrappedExisting.expressions.length === 0) {
227
+ if (n.TemplateLiteral.check(unwrappedExisting) && unwrappedExisting.quasis[0] && unwrappedExisting.expressions.length === 0) {
140
228
  unwrappedExisting.quasis[0].value.raw = String(val);
141
229
  unwrappedExisting.quasis[0].value.cooked = String(val);
142
230
  return existingNode;
@@ -155,83 +243,53 @@ const buildNodeForValue = (val, existingNode, fallbackLocale, requiredImports) =
155
243
  if (Array.isArray(val)) if (unwrappedExisting && n.ArrayExpression.check(unwrappedExisting)) {
156
244
  const elements = [...unwrappedExisting.elements];
157
245
  val.forEach((item, i) => {
158
- elements[i] = buildNodeForValue(item, elements[i], fallbackLocale, requiredImports);
246
+ const elementNode = buildNodeForValue(item, elements[i], fallbackLocale, requiredImports);
247
+ elements[i] = elementNode === SKIP_WRITE ? elements[i] ?? b.literal(null) : elementNode;
159
248
  });
160
249
  if (elements.length > val.length) elements.length = val.length;
161
250
  unwrappedExisting.elements = elements;
162
251
  return existingNode;
163
- } else return b.arrayExpression(val.map((item) => buildNodeForValue(item, null, fallbackLocale, requiredImports)));
252
+ } else return b.arrayExpression(val.map((item) => {
253
+ const elementNode = buildNodeForValue(item, null, fallbackLocale, requiredImports);
254
+ return elementNode === SKIP_WRITE ? b.literal(null) : elementNode;
255
+ }));
164
256
  const nodeType = val && typeof val === "object" && !Array.isArray(val) ? (0, _intlayer_core_dictionaryManipulator.getNodeType)(val) : null;
165
- if (nodeType && [
166
- _intlayer_types_nodeType.TRANSLATION,
167
- _intlayer_types_nodeType.ENUMERATION,
168
- _intlayer_types_nodeType.PLURAL,
169
- _intlayer_types_nodeType.CONDITION,
170
- _intlayer_types_nodeType.GENDER,
171
- _intlayer_types_nodeType.SELECT,
172
- _intlayer_types_nodeType.INSERTION,
173
- _intlayer_types_nodeType.MARKDOWN,
174
- _intlayer_types_nodeType.HTML,
175
- _intlayer_types_nodeType.FILE,
176
- _intlayer_types_nodeType.NESTED,
177
- _intlayer_types_nodeType.ARRAY,
178
- _intlayer_types_nodeType.OBJECT,
179
- _intlayer_types_nodeType.REACT_NODE,
180
- _intlayer_types_nodeType.NUMBER,
181
- _intlayer_types_nodeType.BOOLEAN,
182
- _intlayer_types_nodeType.NULL,
183
- _intlayer_types_nodeType.UNKNOWN
184
- ].includes(nodeType) && nodeType !== _intlayer_types_nodeType.TEXT) {
257
+ if (nodeType) {
258
+ const strategy = nodeTypeStrategies[nodeType];
259
+ if (strategy.kind === "preserve") return existingNode ?? SKIP_WRITE;
260
+ const hasNodeData = Object.hasOwn(val, nodeType);
185
261
  const nodeData = val[nodeType];
186
- let calleeName = "";
187
- if (nodeType === _intlayer_types_nodeType.TRANSLATION) calleeName = "t";
188
- else if (nodeType === _intlayer_types_nodeType.ENUMERATION) calleeName = "enu";
189
- else if (nodeType === _intlayer_types_nodeType.PLURAL) calleeName = "plural";
190
- else if (nodeType === _intlayer_types_nodeType.CONDITION) calleeName = "cond";
191
- else if (nodeType === _intlayer_types_nodeType.GENDER) calleeName = "gender";
192
- else if (nodeType === _intlayer_types_nodeType.SELECT) calleeName = "select";
193
- else if (nodeType === _intlayer_types_nodeType.INSERTION) calleeName = "insert";
194
- else if (nodeType === _intlayer_types_nodeType.MARKDOWN) calleeName = "md";
195
- else if (nodeType === _intlayer_types_nodeType.HTML) calleeName = "html";
196
- else if (nodeType === _intlayer_types_nodeType.FILE) calleeName = "file";
197
- else if (nodeType === _intlayer_types_nodeType.NESTED) calleeName = "nest";
198
- if (calleeName) requiredImports.add(calleeName);
199
- const isMatchingCall = existingNode && n.CallExpression.check(existingNode) && n.Identifier.check(existingNode.callee) && existingNode.callee.name === calleeName;
200
- if ([
201
- "t",
202
- "enu",
203
- "plural",
204
- "cond",
205
- "gender",
206
- "select"
207
- ].includes(calleeName)) {
208
- let objArg = null;
209
- if (isMatchingCall && existingNode.arguments.length > 0 && n.ObjectExpression.check(existingNode.arguments[0])) objArg = existingNode.arguments[0];
210
- else objArg = b.objectExpression([]);
211
- updateObjectLiteral(objArg, nodeData, fallbackLocale, requiredImports);
212
- return isMatchingCall ? existingNode : b.callExpression(b.identifier(calleeName), [objArg]);
213
- }
214
- if ([
215
- "md",
216
- "html",
217
- "insert",
218
- "file"
219
- ].includes(calleeName)) {
220
- const argNode = buildNodeForValue(nodeData, isMatchingCall && existingNode.arguments.length > 0 ? existingNode.arguments[0] : null, fallbackLocale, requiredImports);
221
- if (isMatchingCall) {
222
- existingNode.arguments[0] = argNode;
223
- return existingNode;
262
+ if (strategy.kind !== "structural" && hasNodeData) {
263
+ if (strategy.kind === "unwrap") return buildNodeForValue(nodeData, existingNode, fallbackLocale, requiredImports);
264
+ const { helper } = strategy;
265
+ requiredImports.add(helper);
266
+ const isMatchingCall = existingNode && n.CallExpression.check(existingNode) && n.Identifier.check(existingNode.callee) && existingNode.callee.name === helper;
267
+ if (strategy.kind === "helperRecord") {
268
+ const recordArgument = isMatchingCall && existingNode.arguments.length > 0 && n.ObjectExpression.check(existingNode.arguments[0]) ? existingNode.arguments[0] : b.objectExpression([]);
269
+ updateObjectLiteral(recordArgument, nodeData ?? {}, fallbackLocale, requiredImports);
270
+ const callArguments = [recordArgument, ...strategy.getExtraArguments?.(val) ?? []];
271
+ if (isMatchingCall) {
272
+ syncCallArguments(existingNode, callArguments);
273
+ return existingNode;
274
+ }
275
+ return b.callExpression(b.identifier(helper), callArguments);
224
276
  }
225
- return b.callExpression(b.identifier(calleeName), [argNode]);
226
- }
227
- if (calleeName === "nest") {
228
- const args = [b.literal(nodeData.dictionaryKey)];
229
- if (nodeData.path) args.push(b.literal(nodeData.path));
277
+ if (strategy.kind === "helperValue") {
278
+ const contentArgument = buildNodeForValue(nodeData, isMatchingCall && existingNode.arguments.length > 0 ? existingNode.arguments[0] : null, fallbackLocale, requiredImports);
279
+ if (contentArgument === SKIP_WRITE) return existingNode ?? SKIP_WRITE;
280
+ if (isMatchingCall) {
281
+ syncCallArguments(existingNode, [contentArgument, ...existingNode.arguments.slice(1)]);
282
+ return existingNode;
283
+ }
284
+ return b.callExpression(b.identifier(helper), [contentArgument]);
285
+ }
286
+ const callArguments = [b.literal(nodeData?.dictionaryKey ?? "")];
287
+ if (nodeData?.path) callArguments.push(b.literal(nodeData.path));
230
288
  if (isMatchingCall) {
231
- existingNode.arguments = args;
289
+ syncCallArguments(existingNode, callArguments);
232
290
  return existingNode;
233
291
  }
234
- return b.callExpression(b.identifier("nest"), args);
292
+ return b.callExpression(b.identifier(helper), callArguments);
235
293
  }
236
294
  }
237
295
  const objNode = unwrappedExisting && n.ObjectExpression.check(unwrappedExisting) ? unwrappedExisting : b.objectExpression([]);
@@ -245,10 +303,13 @@ const updateObjectLiteral = (node, data, fallbackLocale, requiredImports) => {
245
303
  for (const [key, val] of Object.entries(data)) {
246
304
  if (val === void 0) continue;
247
305
  const existingProp = getMatchingProperty(node, key);
248
- if (existingProp) existingProp.value = buildNodeForValue(val, existingProp.value, fallbackLocale, requiredImports);
249
- else {
306
+ if (existingProp) {
307
+ const valueNode = buildNodeForValue(val, existingProp.value, fallbackLocale, requiredImports);
308
+ if (valueNode !== SKIP_WRITE) existingProp.value = valueNode;
309
+ } else {
250
310
  const keyNode = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? b.identifier(key) : b.literal(key);
251
311
  const valueNode = buildNodeForValue(val, null, fallbackLocale, requiredImports);
312
+ if (valueNode === SKIP_WRITE) continue;
252
313
  node.properties.push(b.property("init", keyNode, valueNode));
253
314
  }
254
315
  }
@@ -1 +1 @@
1
- {"version":3,"file":"transformJSFile.cjs","names":["NodeTypes","babelTsParser"],"sources":["../../../src/writeContentDeclaration/transformJSFile.ts"],"sourcesContent":["import { getNodeType } from '@intlayer/core/dictionaryManipulator';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { ContentNode, Dictionary } from '@intlayer/types/dictionary';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport * as recast from 'recast';\nimport { babelTsParser } from '../utils/babelParser';\n\nconst b = recast.types.builders;\nconst n = recast.types.namedTypes;\n\n/**\n * Unwraps TypeScript/Babel expression wrappers (satisfies, as, !, <Type>).\n * Uses string fallbacks to bypass outdated ast-types definitions.\n */\nconst unwrap = (node: any) => {\n while (\n node &&\n (n.TSSatisfiesExpression?.check(node) ||\n n.TSAsExpression?.check(node) ||\n n.TSTypeAssertion?.check(node) ||\n n.TSNonNullExpression?.check(node) ||\n [\n 'TSSatisfiesExpression',\n 'TSAsExpression',\n 'TSTypeAssertion',\n 'TSNonNullExpression',\n ].includes(node.type))\n ) {\n node = node.expression;\n }\n return node;\n};\n\n/**\n * Robustly finds a property in a recast ObjectExpression.\n * Handles quoted (\"key\") or unquoted (key) properties.\n */\nconst getMatchingProperty = (node: any, key: string) => {\n return node.properties.find((prop: any) => {\n if (n.Property.check(prop) || n.ObjectProperty.check(prop)) {\n if (n.Identifier.check(prop.key) && prop.key.name === key) return true;\n\n if (n.StringLiteral.check(prop.key) && prop.key.value === key)\n return true;\n\n if (n.Literal.check(prop.key) && prop.key.value === key) return true;\n }\n\n return false;\n });\n};\n\n/**\n * Synchronizes numeric suffixes across locales.\n * E.g. \"Hello 1\" -> \"Hello 3\" updates \"Bonjour 1\" to \"Bonjour 3\".\n */\nconst syncNumericSuffixAcrossLocales = (\n existingNode: any,\n fallbackLocaleCode: string,\n newFallbackValue: string\n) => {\n const trailingNumberMatch = newFallbackValue.match(/\\d+(?!.*\\d)/);\n if (!trailingNumberMatch) return;\n const newTrailingNumber = trailingNumberMatch[0];\n\n if (n.ObjectExpression.check(existingNode)) {\n for (const prop of existingNode.properties) {\n if (n.Property.check(prop) || n.ObjectProperty.check(prop)) {\n let propName = '';\n\n if (n.Identifier.check(prop.key)) propName = prop.key.name;\n else if (\n n.Literal.check(prop.key) &&\n typeof prop.key.value === 'string'\n )\n propName = prop.key.value;\n else if (n.StringLiteral.check(prop.key)) propName = prop.key.value;\n\n if (propName && propName !== fallbackLocaleCode) {\n if (\n n.Literal.check(prop.value) &&\n typeof prop.value.value === 'string'\n ) {\n const currentValue = prop.value.value;\n const currentTrailingNumberMatch =\n currentValue.match(/\\d+(?!.*\\d)/);\n\n if (currentTrailingNumberMatch) {\n prop.value = b.literal(\n currentValue.replace(/(\\d+)(?!.*\\d)/, newTrailingNumber)\n );\n }\n } else if (n.StringLiteral.check(prop.value)) {\n const currentValue = prop.value.value;\n const currentTrailingNumberMatch =\n currentValue.match(/\\d+(?!.*\\d)/);\n\n if (currentTrailingNumberMatch) {\n prop.value = b.stringLiteral(\n currentValue.replace(/(\\d+)(?!.*\\d)/, newTrailingNumber)\n );\n }\n }\n }\n }\n }\n }\n};\n\n/**\n * Checks if a value represents a multilingual Intlayer node.\n * A node is multilingual if it is a Translation node, or if it is a specialized node\n * (Markdown, HTML, etc.) that contains a Translation node.\n */\nconst isMultilingualNode = (val: any): boolean => {\n if (typeof val !== 'object' || val === null || Array.isArray(val)) {\n return false;\n }\n\n const nodeType = getNodeType(val as ContentNode);\n\n if (nodeType === NodeTypes.TRANSLATION) {\n return true;\n }\n\n if (\n nodeType === NodeTypes.MARKDOWN ||\n nodeType === NodeTypes.HTML ||\n nodeType === NodeTypes.INSERTION\n ) {\n return isMultilingualNode((val as any)[nodeType]);\n }\n\n if (\n nodeType === NodeTypes.ENUMERATION ||\n nodeType === NodeTypes.PLURAL ||\n nodeType === NodeTypes.CONDITION ||\n nodeType === NodeTypes.GENDER ||\n nodeType === NodeTypes.SELECT\n ) {\n const data = (val as any)[nodeType];\n\n if (data && typeof data === 'object') {\n return Object.values(data).some((v) => isMultilingualNode(v));\n }\n }\n\n return false;\n};\n\n/**\n * Recursively builds or updates an AST node for a given dictionary value.\n */\nconst buildNodeForValue = (\n val: any,\n existingNode: any,\n fallbackLocale: string | undefined, // In per-locale mode, this is the locale of the file\n requiredImports: Set<string>\n): any => {\n const unwrappedExisting = unwrap(existingNode);\n\n // --- CRITICAL GUARD: STRICT AST PRESERVATION ---\n // If the existing node is code (JSX, Variables, standard functions), leave it alone.\n // Only allow updates to literals, plain objects, arrays, and Intlayer helpers.\n if (unwrappedExisting) {\n const isUpdatableNode =\n n.Literal.check(unwrappedExisting) ||\n n.StringLiteral.check(unwrappedExisting) ||\n n.NumericLiteral.check(unwrappedExisting) ||\n n.BooleanLiteral.check(unwrappedExisting) ||\n n.TemplateLiteral.check(unwrappedExisting) ||\n n.ObjectExpression.check(unwrappedExisting) ||\n n.ArrayExpression.check(unwrappedExisting) ||\n (n.CallExpression.check(unwrappedExisting) &&\n n.Identifier.check(unwrappedExisting.callee) &&\n [\n 't',\n 'enu',\n 'plural',\n 'cond',\n 'gender',\n 'select',\n 'insert',\n 'md',\n 'html',\n 'file',\n 'nest',\n ].includes(unwrappedExisting.callee.name));\n\n if (!isUpdatableNode) {\n return existingNode;\n }\n }\n\n // If we are in per-locale mode (fallbackLocale is set)\n // and the value is not already a complex translation node,\n // we want to store it as a simple literal, NOT wrapped in t().\n if (fallbackLocale && !existingNode && !isMultilingualNode(val)) {\n if (val === null) return b.literal(null);\n if (\n typeof val === 'string' ||\n typeof val === 'number' ||\n typeof val === 'boolean'\n ) {\n if (typeof val === 'string' && val.includes('\\n')) {\n return b.templateLiteral(\n [b.templateElement({ raw: val, cooked: val }, true)],\n []\n );\n }\n return b.literal(val);\n }\n }\n\n if (fallbackLocale && existingNode && !isMultilingualNode(val)) {\n if (\n n.CallExpression.check(existingNode) &&\n n.Identifier.check(existingNode.callee) &&\n existingNode.callee.name === 't'\n ) {\n const arg = unwrap(existingNode.arguments[0]);\n\n if (n.ObjectExpression.check(arg)) {\n if (typeof val === 'string') {\n syncNumericSuffixAcrossLocales(arg, fallbackLocale, val);\n }\n updateObjectLiteral(\n arg,\n { [fallbackLocale]: val },\n fallbackLocale,\n requiredImports\n );\n\n if (!fallbackLocale) {\n requiredImports.add('t');\n }\n\n return existingNode;\n }\n }\n\n if (\n (!val || typeof val !== 'object') &&\n n.CallExpression.check(existingNode) &&\n n.Identifier.check(existingNode.callee) &&\n existingNode.callee.name === 'md'\n ) {\n const innerArg = existingNode.arguments[0];\n\n if (\n n.CallExpression.check(innerArg) &&\n n.Identifier.check(innerArg.callee) &&\n innerArg.callee.name === 't'\n ) {\n const tArg = unwrap(innerArg.arguments[0]);\n\n if (n.ObjectExpression.check(tArg)) {\n if (typeof val === 'string') {\n syncNumericSuffixAcrossLocales(tArg, fallbackLocale, val);\n }\n updateObjectLiteral(\n tArg,\n { [fallbackLocale]: val },\n fallbackLocale,\n requiredImports\n );\n requiredImports.add('md');\n requiredImports.add('t');\n\n return existingNode;\n }\n }\n }\n }\n\n // 1. Primitives\n if (val === null) return b.literal(null);\n if (\n typeof val === 'string' ||\n typeof val === 'number' ||\n typeof val === 'boolean'\n ) {\n if (unwrappedExisting) {\n // Preserve existing template literals (backticks)\n if (\n n.TemplateLiteral.check(unwrappedExisting) &&\n unwrappedExisting.expressions.length === 0\n ) {\n unwrappedExisting.quasis[0].value.raw = String(val);\n unwrappedExisting.quasis[0].value.cooked = String(val);\n return existingNode;\n }\n // Preserve existing standard literals (keeps quote styling)\n if (\n n.Literal.check(unwrappedExisting) ||\n n.StringLiteral.check(unwrappedExisting)\n ) {\n unwrappedExisting.value = val;\n return existingNode;\n }\n }\n\n // Force multiline strings to use Template Literals\n if (typeof val === 'string' && val.includes('\\n')) {\n return b.templateLiteral(\n [b.templateElement({ raw: val, cooked: val }, true)],\n []\n );\n }\n return b.literal(val);\n }\n\n // 2. Arrays\n if (Array.isArray(val)) {\n if (unwrappedExisting && n.ArrayExpression.check(unwrappedExisting)) {\n const elements = [...unwrappedExisting.elements];\n val.forEach((item, i) => {\n elements[i] = buildNodeForValue(\n item,\n elements[i],\n fallbackLocale,\n requiredImports\n );\n });\n\n if (elements.length > val.length) elements.length = val.length;\n unwrappedExisting.elements = elements as any;\n\n return existingNode;\n } else {\n return b.arrayExpression(\n val.map((item) =>\n buildNodeForValue(item, null, fallbackLocale, requiredImports)\n )\n );\n }\n }\n\n // 3. Intlayer Specialized Nodes\n const nodeType =\n val && typeof val === 'object' && !Array.isArray(val)\n ? getNodeType(val as ContentNode)\n : null;\n\n if (\n nodeType &&\n [\n NodeTypes.TRANSLATION,\n NodeTypes.ENUMERATION,\n NodeTypes.PLURAL,\n NodeTypes.CONDITION,\n NodeTypes.GENDER,\n NodeTypes.SELECT,\n NodeTypes.INSERTION,\n NodeTypes.MARKDOWN,\n NodeTypes.HTML,\n NodeTypes.FILE,\n NodeTypes.NESTED,\n NodeTypes.ARRAY,\n NodeTypes.OBJECT,\n NodeTypes.REACT_NODE,\n NodeTypes.NUMBER,\n NodeTypes.BOOLEAN,\n NodeTypes.NULL,\n NodeTypes.UNKNOWN,\n ].includes(nodeType as any) &&\n nodeType !== NodeTypes.TEXT\n ) {\n const nodeData = (val as any)[nodeType];\n let calleeName = '';\n\n if (nodeType === NodeTypes.TRANSLATION) calleeName = 't';\n else if (nodeType === NodeTypes.ENUMERATION) calleeName = 'enu';\n else if (nodeType === NodeTypes.PLURAL) calleeName = 'plural';\n else if (nodeType === NodeTypes.CONDITION) calleeName = 'cond';\n else if (nodeType === NodeTypes.GENDER) calleeName = 'gender';\n else if (nodeType === NodeTypes.SELECT) calleeName = 'select';\n else if (nodeType === NodeTypes.INSERTION) calleeName = 'insert';\n else if (nodeType === NodeTypes.MARKDOWN) calleeName = 'md';\n else if (nodeType === NodeTypes.HTML) calleeName = 'html';\n else if (nodeType === NodeTypes.FILE) calleeName = 'file';\n else if (nodeType === NodeTypes.NESTED) calleeName = 'nest';\n\n if (calleeName) requiredImports.add(calleeName);\n\n const isMatchingCall =\n existingNode &&\n n.CallExpression.check(existingNode) &&\n n.Identifier.check(existingNode.callee) &&\n existingNode.callee.name === calleeName;\n\n if (\n ['t', 'enu', 'plural', 'cond', 'gender', 'select'].includes(calleeName)\n ) {\n let objArg: any = null;\n\n if (\n isMatchingCall &&\n existingNode.arguments.length > 0 &&\n n.ObjectExpression.check(existingNode.arguments[0])\n ) {\n objArg = existingNode.arguments[0];\n } else {\n objArg = b.objectExpression([]);\n }\n updateObjectLiteral(objArg, nodeData, fallbackLocale, requiredImports);\n\n return isMatchingCall\n ? existingNode\n : b.callExpression(b.identifier(calleeName), [objArg]);\n }\n\n if (['md', 'html', 'insert', 'file'].includes(calleeName)) {\n const argNode = buildNodeForValue(\n nodeData,\n isMatchingCall && existingNode.arguments.length > 0\n ? existingNode.arguments[0]\n : null,\n fallbackLocale,\n requiredImports\n );\n\n if (isMatchingCall) {\n existingNode.arguments[0] = argNode;\n\n return existingNode;\n }\n\n return b.callExpression(b.identifier(calleeName), [argNode]);\n }\n\n if (calleeName === 'nest') {\n const args = [b.literal(nodeData.dictionaryKey)];\n\n if (nodeData.path) args.push(b.literal(nodeData.path));\n\n if (isMatchingCall) {\n existingNode.arguments = args;\n\n return existingNode;\n }\n\n return b.callExpression(b.identifier('nest'), args);\n }\n }\n\n // 4. Plain Object\n const objNode =\n unwrappedExisting && n.ObjectExpression.check(unwrappedExisting)\n ? unwrappedExisting\n : b.objectExpression([]);\n\n updateObjectLiteral(objNode, val, fallbackLocale, requiredImports);\n\n return existingNode && unwrappedExisting === existingNode\n ? objNode\n : existingNode || objNode;\n};\n\n/**\n * Recursively updates the AST object literal properties.\n */\nconst updateObjectLiteral = (\n node: recast.types.namedTypes.ObjectExpression,\n data: Record<string, any>,\n fallbackLocale: string | undefined,\n requiredImports: Set<string>\n) => {\n for (const [key, val] of Object.entries(data)) {\n if (val === undefined) continue;\n\n const existingProp = getMatchingProperty(node, key);\n\n if (existingProp) {\n existingProp.value = buildNodeForValue(\n val,\n existingProp.value,\n fallbackLocale,\n requiredImports\n );\n } else {\n const isValidId = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key);\n const keyNode = isValidId ? b.identifier(key) : b.literal(key);\n const valueNode = buildNodeForValue(\n val,\n null,\n fallbackLocale,\n requiredImports\n );\n node.properties.push(b.property('init', keyNode, valueNode));\n }\n }\n};\n\n/**\n * Modifies the AST's top-level imports to inject dynamically needed helper utilities seamlessly.\n */\nconst addImports = (ast: any, requiredImports: Set<string>, isESM: boolean) => {\n if (requiredImports.size === 0) return;\n\n const existingCoreImports = new Set<string>();\n let coreImportPath: any = null;\n\n recast.visit(ast, {\n visitImportDeclaration(path) {\n const source = path.node.source.value;\n\n if (source === 'intlayer') {\n coreImportPath = path;\n path.node.specifiers?.forEach((spec) => {\n if (\n n.ImportSpecifier.check(spec) &&\n typeof spec.imported.name === 'string'\n ) {\n existingCoreImports.add(spec.imported.name);\n }\n });\n }\n\n return false;\n },\n visitVariableDeclaration(path) {\n path.node.declarations.forEach((decl) => {\n if (\n n.VariableDeclarator.check(decl) &&\n n.CallExpression.check(decl.init) &&\n n.Identifier.check(decl.init.callee) &&\n decl.init.callee.name === 'require'\n ) {\n const arg = decl.init.arguments[0];\n\n if (n.Literal.check(arg)) {\n if (arg.value === 'intlayer') {\n if (n.ObjectPattern.check(decl.id)) {\n decl.id.properties.forEach((prop) => {\n if (\n n.Property.check(prop) &&\n (n.Identifier.check(prop.key) ||\n n.Identifier.check(prop.value))\n ) {\n const name = n.Identifier.check(prop.key)\n ? prop.key.name\n : (prop.value as any).name;\n existingCoreImports.add(name);\n }\n });\n } else if (n.Identifier.check(decl.id)) {\n // Handle const intlayer = require('intlayer')\n existingCoreImports.add(decl.id.name);\n }\n }\n }\n }\n });\n\n return false;\n },\n });\n\n const missingCore = Array.from(requiredImports).filter(\n (imp) => !existingCoreImports.has(imp)\n );\n\n if (missingCore.length === 0) return;\n\n if (isESM) {\n if (coreImportPath) {\n missingCore.forEach((imp) => {\n coreImportPath.node.specifiers.push(\n b.importSpecifier(b.identifier(imp))\n );\n });\n coreImportPath.node.specifiers.sort((a: any, b: any) =>\n a.imported.name.localeCompare(b.imported.name)\n );\n } else {\n const specifiers = missingCore\n .sort()\n .map((imp) => b.importSpecifier(b.identifier(imp)));\n const newImport = b.importDeclaration(specifiers, b.literal('intlayer'));\n ast.program.body.unshift(newImport);\n }\n } else {\n let insertIndex = 0;\n\n if (\n ast.program.body.length > 0 &&\n n.ExpressionStatement.check(ast.program.body[0]) &&\n n.Literal.check(ast.program.body[0].expression)\n ) {\n insertIndex = 1; // Insert after 'use strict'\n }\n const cjsLines: any[] = [];\n\n const properties = missingCore.sort().map((imp) => {\n const prop = b.property('init', b.identifier(imp), b.identifier(imp));\n prop.shorthand = true;\n\n return prop;\n });\n cjsLines.push(\n b.variableDeclaration('const', [\n b.variableDeclarator(\n b.objectPattern(properties),\n b.callExpression(b.identifier('require'), [b.literal('intlayer')])\n ),\n ])\n );\n\n ast.program.body.splice(insertIndex, 0, ...cjsLines);\n }\n};\n\n/**\n * Updates a JS/TS file seamlessly to map new localization keys, arrays, complex nodes and nested dictionaries gracefully using AST updates via Recast parser.\n */\nexport const transformJSFile = async (\n fileContent: string,\n dictionary: Dictionary,\n fallbackLocale?: Locale,\n noMetadata?: boolean\n): Promise<string> => {\n if (!dictionary || typeof dictionary !== 'object') return fileContent;\n\n let ast: any;\n try {\n ast = recast.parse(fileContent, {\n parser: babelTsParser,\n });\n } catch (error) {\n console.error({ error });\n return fileContent;\n }\n\n let rootObject: any = null;\n let isESM = false;\n\n recast.visit(ast, {\n visitExportDefaultDeclaration() {\n isESM = true;\n\n return false;\n },\n visitImportDeclaration() {\n isESM = true;\n\n return false;\n },\n });\n\n recast.visit(ast, {\n visitExportDefaultDeclaration(path) {\n const decl = path.node.declaration;\n const unwrappedDecl = unwrap(decl);\n\n if (n.ObjectExpression.check(unwrappedDecl)) {\n rootObject = unwrappedDecl;\n } else if (n.Identifier.check(unwrappedDecl)) {\n const varName = unwrappedDecl.name;\n recast.visit(ast, {\n visitVariableDeclarator(vp) {\n const unwrappedInit = unwrap(vp.node.init);\n\n if (\n n.Identifier.check(vp.node.id) &&\n vp.node.id.name === varName &&\n n.ObjectExpression.check(unwrappedInit)\n ) {\n rootObject = unwrappedInit;\n }\n\n return false;\n },\n });\n }\n\n return false;\n },\n visitAssignmentExpression(path) {\n const left = path.node.left;\n\n if (n.MemberExpression.check(left)) {\n if (\n n.Identifier.check(left.object) &&\n left.object.name === 'module' &&\n n.Identifier.check(left.property) &&\n left.property.name === 'exports'\n ) {\n const unwrappedRight = unwrap(path.node.right);\n\n if (n.ObjectExpression.check(unwrappedRight)) {\n rootObject = unwrappedRight;\n }\n }\n\n if (\n n.Identifier.check(left.object) &&\n left.object.name === 'exports' &&\n n.Identifier.check(left.property) &&\n left.property.name === 'default'\n ) {\n const unwrappedRight = unwrap(path.node.right);\n\n if (n.ObjectExpression.check(unwrappedRight)) {\n rootObject = unwrappedRight;\n }\n }\n }\n this.traverse(path);\n },\n });\n\n if (!rootObject) {\n recast.visit(ast, {\n visitVariableDeclarator(path) {\n const unwrappedInit = unwrap(path.node.init);\n\n if (!rootObject && n.ObjectExpression.check(unwrappedInit)) {\n rootObject = unwrappedInit;\n }\n\n return false;\n },\n });\n }\n\n if (!rootObject) return fileContent;\n\n const requiredImports = new Set<string>();\n const effectiveFallbackLocale = (fallbackLocale as string) ?? 'en';\n\n const metadataProperties = [\n 'id',\n 'locale',\n 'filled',\n 'fill',\n 'title',\n 'description',\n 'tags',\n 'version',\n 'priority',\n 'contentAutoTransformation',\n ];\n\n if (noMetadata) {\n // Remove key, content and metadata properties if they exist\n rootObject.properties = rootObject.properties.filter((prop: any) => {\n if (n.Property.check(prop) || n.ObjectProperty.check(prop)) {\n let propName = '';\n if (n.Identifier.check(prop.key)) propName = prop.key.name;\n else if (n.StringLiteral.check(prop.key)) propName = prop.key.value;\n else if (n.Literal.check(prop.key)) propName = String(prop.key.value);\n\n return !['key', 'content', ...metadataProperties].includes(propName);\n }\n return true;\n });\n\n // Update satisfies type if exists\n recast.visit(ast, {\n visitNode(path) {\n const node = path.node;\n if (\n (n.TSSatisfiesExpression?.check(node) ||\n node.type === 'TSSatisfiesExpression') &&\n (node as any).typeAnnotation &&\n n.TSTypeReference.check((node as any).typeAnnotation) &&\n n.Identifier.check((node as any).typeAnnotation.typeName) &&\n (node as any).typeAnnotation.typeName.name === 'Dictionary'\n ) {\n (node as any).typeAnnotation = b.tsIndexedAccessType(\n b.tsTypeReference(b.identifier('Dictionary')),\n b.tsLiteralType(b.stringLiteral('content'))\n );\n }\n this.traverse(path);\n },\n });\n } else {\n for (const prop of metadataProperties) {\n if ((dictionary as any)[prop] !== undefined) {\n updateObjectLiteral(\n rootObject,\n { [prop]: (dictionary as any)[prop] },\n undefined,\n requiredImports\n );\n }\n }\n }\n\n if (dictionary.content !== undefined) {\n updateObjectLiteral(\n rootObject,\n noMetadata\n ? (dictionary.content as Record<string, any>)\n : { content: dictionary.content },\n effectiveFallbackLocale,\n requiredImports\n );\n }\n\n addImports(ast, requiredImports, isESM);\n\n return recast.print(ast).code;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"transformJSFile.cjs","names":["NodeTypes","babelTsParser"],"sources":["../../../src/writeContentDeclaration/transformJSFile.ts"],"sourcesContent":["import { getNodeType } from '@intlayer/core/dictionaryManipulator';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { ContentNode, Dictionary } from '@intlayer/types/dictionary';\nimport type { NodeType } from '@intlayer/types/nodeType';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport * as recast from 'recast';\nimport { babelTsParser } from '../utils/babelParser';\n\nconst b = recast.types.builders;\nconst n = recast.types.namedTypes;\n\n/**\n * Name of an `intlayer` helper function a node type is printed as.\n *\n * Mirrors the transpiler helpers re-exported by the `intlayer` package\n * (`@intlayer/core/transpiler`).\n */\ntype HelperName =\n | 't'\n | 'enu'\n | 'plural'\n | 'cond'\n | 'gender'\n | 'select'\n | 'insert'\n | 'md'\n | 'html'\n | 'file'\n | 'nest';\n\n/**\n * How a {@link NodeType} is materialized back into source code.\n *\n * - `helperRecord` — printed as `helper({ … })`. The node data is a record of\n * variants (locales, plural categories, conditions, cases…) whose values are\n * rebuilt recursively and merged into the existing object literal.\n * - `helperValue` — printed as `helper(value)`. The node data is a single\n * content, itself possibly another node (e.g. `md(t({ … }))`).\n * - `nesting` — printed as `nest(dictionaryKey, path?)`.\n * - `unwrap` — not a source-level helper: the wrapped value replaces the node,\n * so `{ nodeType: 'text', text: 'Hello' }` is printed as `\"Hello\"`.\n * - `preserve` — framework elements have no data representation, so the\n * existing source expression is kept untouched and nothing is written when\n * there is none.\n * - `structural` — plain objects, arrays and unrecognized nodes, rebuilt by the\n * generic object/array branches.\n */\ntype NodeTypeStrategy =\n | {\n kind: 'helperRecord';\n helper: HelperName;\n /**\n * Extra arguments appended after the record argument, rebuilt from the\n * node attributes (e.g. the variable name carried by a `select` node).\n */\n getExtraArguments?: (\n node: Record<string, unknown>\n ) => recast.types.namedTypes.Literal[];\n }\n | { kind: 'helperValue'; helper: HelperName }\n | { kind: 'nesting'; helper: HelperName }\n | { kind: 'unwrap' }\n | { kind: 'preserve' }\n | { kind: 'structural' };\n\n/**\n * Every {@link NodeType} and how it is written back to a JS/TS declaration file.\n *\n * Typed as `Record<NodeType, NodeTypeStrategy>` so that adding a node type to\n * `@intlayer/types` without registering it here becomes a compile-time error —\n * the same guarantee `getNodeType` gives on the read side.\n */\nconst nodeTypeStrategies: Record<NodeType, NodeTypeStrategy> = {\n [NodeTypes.TRANSLATION]: { kind: 'helperRecord', helper: 't' },\n [NodeTypes.ENUMERATION]: { kind: 'helperRecord', helper: 'enu' },\n [NodeTypes.PLURAL]: { kind: 'helperRecord', helper: 'plural' },\n [NodeTypes.CONDITION]: { kind: 'helperRecord', helper: 'cond' },\n [NodeTypes.GENDER]: { kind: 'helperRecord', helper: 'gender' },\n [NodeTypes.SELECT]: {\n kind: 'helperRecord',\n helper: 'select',\n getExtraArguments: (node) =>\n typeof node.variable === 'string' && node.variable.length > 0\n ? [b.literal(node.variable)]\n : [],\n },\n [NodeTypes.INSERTION]: { kind: 'helperValue', helper: 'insert' },\n [NodeTypes.MARKDOWN]: { kind: 'helperValue', helper: 'md' },\n [NodeTypes.HTML]: { kind: 'helperValue', helper: 'html' },\n [NodeTypes.FILE]: { kind: 'helperValue', helper: 'file' },\n [NodeTypes.NESTED]: { kind: 'nesting', helper: 'nest' },\n [NodeTypes.TEXT]: { kind: 'unwrap' },\n [NodeTypes.NUMBER]: { kind: 'unwrap' },\n [NodeTypes.BOOLEAN]: { kind: 'unwrap' },\n [NodeTypes.NULL]: { kind: 'unwrap' },\n [NodeTypes.REACT_NODE]: { kind: 'preserve' },\n [NodeTypes.PREACT_NODE]: { kind: 'preserve' },\n [NodeTypes.SOLID_NODE]: { kind: 'preserve' },\n [NodeTypes.OBJECT]: { kind: 'structural' },\n [NodeTypes.ARRAY]: { kind: 'structural' },\n [NodeTypes.UNKNOWN]: { kind: 'structural' },\n};\n\n/** Every `intlayer` helper the transform is allowed to rewrite. */\nconst helperNames = new Set<string>(\n Object.values(nodeTypeStrategies).flatMap((strategy) =>\n 'helper' in strategy ? [strategy.helper] : []\n )\n);\n\n/** Helpers wrapping a single content, e.g. `md(t({ … }))`. */\nconst wrapperHelperNames = new Set<string>(\n Object.values(nodeTypeStrategies).flatMap((strategy) =>\n strategy.kind === 'helperValue' ? [strategy.helper] : []\n )\n);\n\n/**\n * Returned by {@link buildNodeForValue} when a value must not be written to the\n * source file — framework elements and functions have no data representation,\n * so the original expression (when there is one) is left untouched and no new\n * property is created otherwise.\n */\nconst SKIP_WRITE = Symbol('intlayer.skipWrite');\n\n/**\n * Unwraps TypeScript/Babel expression wrappers (satisfies, as, !, <Type>).\n * Uses string fallbacks to bypass outdated ast-types definitions.\n */\nconst unwrap = (node: any) => {\n while (\n node &&\n (n.TSSatisfiesExpression?.check(node) ||\n n.TSAsExpression?.check(node) ||\n n.TSTypeAssertion?.check(node) ||\n n.TSNonNullExpression?.check(node) ||\n [\n 'TSSatisfiesExpression',\n 'TSAsExpression',\n 'TSTypeAssertion',\n 'TSNonNullExpression',\n ].includes(node.type))\n ) {\n node = node.expression;\n }\n return node;\n};\n\n/**\n * Robustly finds a property in a recast ObjectExpression.\n * Handles quoted (\"key\") or unquoted (key) properties.\n */\nconst getMatchingProperty = (node: any, key: string) => {\n return node.properties.find((prop: any) => {\n if (n.Property.check(prop) || n.ObjectProperty.check(prop)) {\n if (n.Identifier.check(prop.key) && prop.key.name === key) return true;\n\n if (n.StringLiteral.check(prop.key) && prop.key.value === key)\n return true;\n\n if (n.Literal.check(prop.key) && prop.key.value === key) return true;\n }\n\n return false;\n });\n};\n\n/**\n * Synchronizes numeric suffixes across locales.\n * E.g. \"Hello 1\" -> \"Hello 3\" updates \"Bonjour 1\" to \"Bonjour 3\".\n */\nconst syncNumericSuffixAcrossLocales = (\n existingNode: any,\n fallbackLocaleCode: string,\n newFallbackValue: string\n) => {\n const trailingNumberMatch = newFallbackValue.match(/\\d+(?!.*\\d)/);\n if (!trailingNumberMatch) return;\n const newTrailingNumber = trailingNumberMatch[0];\n\n if (n.ObjectExpression.check(existingNode)) {\n for (const prop of existingNode.properties) {\n if (n.Property.check(prop) || n.ObjectProperty.check(prop)) {\n let propName = '';\n\n if (n.Identifier.check(prop.key)) propName = prop.key.name;\n else if (\n n.Literal.check(prop.key) &&\n typeof prop.key.value === 'string'\n )\n propName = prop.key.value;\n else if (n.StringLiteral.check(prop.key)) propName = prop.key.value;\n\n if (propName && propName !== fallbackLocaleCode) {\n if (\n n.Literal.check(prop.value) &&\n typeof prop.value.value === 'string'\n ) {\n const currentValue = prop.value.value;\n const currentTrailingNumberMatch =\n currentValue.match(/\\d+(?!.*\\d)/);\n\n if (currentTrailingNumberMatch) {\n prop.value = b.literal(\n currentValue.replace(/(\\d+)(?!.*\\d)/, newTrailingNumber)\n );\n }\n } else if (n.StringLiteral.check(prop.value)) {\n const currentValue = prop.value.value;\n const currentTrailingNumberMatch =\n currentValue.match(/\\d+(?!.*\\d)/);\n\n if (currentTrailingNumberMatch) {\n prop.value = b.stringLiteral(\n currentValue.replace(/(\\d+)(?!.*\\d)/, newTrailingNumber)\n );\n }\n }\n }\n }\n }\n }\n};\n\n/**\n * Checks if a value represents a multilingual Intlayer node.\n *\n * A node is multilingual if it is a Translation node, if it wraps one\n * (Markdown, HTML, Insertion…), or if any of its variants contains one\n * (Enumeration, Plural, Condition, Gender, Select…).\n */\nconst isMultilingualNode = (val: any): boolean => {\n if (typeof val !== 'object' || val === null || Array.isArray(val)) {\n return false;\n }\n\n const nodeType = getNodeType(val as ContentNode);\n\n if (nodeType === NodeTypes.TRANSLATION) {\n return true;\n }\n\n const strategy = nodeTypeStrategies[nodeType];\n const nodeData = (val as any)[nodeType];\n\n if (strategy.kind === 'helperValue' || strategy.kind === 'unwrap') {\n return isMultilingualNode(nodeData);\n }\n\n if (\n strategy.kind === 'helperRecord' &&\n nodeData &&\n typeof nodeData === 'object'\n ) {\n return Object.values(nodeData).some((variant) =>\n isMultilingualNode(variant)\n );\n }\n\n return false;\n};\n\n/**\n * Checks whether two AST nodes are literals holding the same value.\n */\nconst isEquivalentLiteral = (left: any, right: any): boolean => {\n const isLiteral = (node: any) =>\n n.Literal.check(node) ||\n n.StringLiteral.check(node) ||\n n.NumericLiteral.check(node) ||\n n.BooleanLiteral.check(node);\n\n return isLiteral(left) && isLiteral(right) && left.value === right.value;\n};\n\n/**\n * Aligns the arguments of an existing call expression with the desired ones.\n *\n * Mutates the call only when the arguments actually differ, so Recast keeps the\n * original formatting (and quote style) of calls that did not change.\n */\nconst syncCallArguments = (callNode: any, desiredArguments: any[]) => {\n const isUnchanged =\n callNode.arguments.length === desiredArguments.length &&\n desiredArguments.every((desired, index) => {\n const current = callNode.arguments[index];\n\n return current === desired || isEquivalentLiteral(current, desired);\n });\n\n if (!isUnchanged) {\n callNode.arguments = desiredArguments;\n }\n};\n\n/**\n * Recursively builds or updates an AST node for a given dictionary value.\n *\n * Returns {@link SKIP_WRITE} when the value cannot be represented in source and\n * no existing expression can be kept.\n */\nconst buildNodeForValue = (\n val: any,\n existingNode: any,\n fallbackLocale: string | undefined, // In per-locale mode, this is the locale of the file\n requiredImports: Set<string>\n): any => {\n // Values with no source representation must never overwrite existing code.\n if (val === undefined || typeof val === 'function') {\n return existingNode ?? SKIP_WRITE;\n }\n\n const unwrappedExisting = unwrap(existingNode);\n\n // --- CRITICAL GUARD: STRICT AST PRESERVATION ---\n // If the existing node is code (JSX, Variables, standard functions), leave it alone.\n // Only allow updates to literals, plain objects, arrays, and Intlayer helpers.\n if (unwrappedExisting) {\n const isUpdatableNode =\n n.Literal.check(unwrappedExisting) ||\n n.StringLiteral.check(unwrappedExisting) ||\n n.NumericLiteral.check(unwrappedExisting) ||\n n.BooleanLiteral.check(unwrappedExisting) ||\n n.TemplateLiteral.check(unwrappedExisting) ||\n n.ObjectExpression.check(unwrappedExisting) ||\n n.ArrayExpression.check(unwrappedExisting) ||\n (n.CallExpression.check(unwrappedExisting) &&\n n.Identifier.check(unwrappedExisting.callee) &&\n helperNames.has(unwrappedExisting.callee.name));\n\n if (!isUpdatableNode) {\n return existingNode;\n }\n }\n\n // If we are in per-locale mode (fallbackLocale is set)\n // and the value is not already a complex translation node,\n // we want to store it as a simple literal, NOT wrapped in t().\n if (fallbackLocale && !existingNode && !isMultilingualNode(val)) {\n if (val === null) return b.literal(null);\n if (\n typeof val === 'string' ||\n typeof val === 'number' ||\n typeof val === 'boolean'\n ) {\n if (typeof val === 'string' && val.includes('\\n')) {\n return b.templateLiteral(\n [b.templateElement({ raw: val, cooked: val }, true)],\n []\n );\n }\n return b.literal(val);\n }\n }\n\n if (fallbackLocale && existingNode && !isMultilingualNode(val)) {\n if (\n n.CallExpression.check(existingNode) &&\n n.Identifier.check(existingNode.callee) &&\n existingNode.callee.name === 't'\n ) {\n const arg = unwrap(existingNode.arguments[0]);\n\n if (n.ObjectExpression.check(arg)) {\n if (typeof val === 'string') {\n syncNumericSuffixAcrossLocales(arg, fallbackLocale, val);\n }\n updateObjectLiteral(\n arg,\n { [fallbackLocale]: val },\n fallbackLocale,\n requiredImports\n );\n\n if (!fallbackLocale) {\n requiredImports.add('t');\n }\n\n return existingNode;\n }\n }\n\n // Wrapped translations — `md(t({ … }))`, `html(t({ … }))`, `insert(t({ … }))`:\n // update the inner translation in place instead of replacing the wrapper.\n if (\n (!val || typeof val !== 'object') &&\n n.CallExpression.check(existingNode) &&\n n.Identifier.check(existingNode.callee) &&\n wrapperHelperNames.has(existingNode.callee.name)\n ) {\n const wrapperName = existingNode.callee.name;\n const innerArg = existingNode.arguments[0];\n\n if (\n n.CallExpression.check(innerArg) &&\n n.Identifier.check(innerArg.callee) &&\n innerArg.callee.name === 't'\n ) {\n const tArg = unwrap(innerArg.arguments[0]);\n\n if (n.ObjectExpression.check(tArg)) {\n if (typeof val === 'string') {\n syncNumericSuffixAcrossLocales(tArg, fallbackLocale, val);\n }\n updateObjectLiteral(\n tArg,\n { [fallbackLocale]: val },\n fallbackLocale,\n requiredImports\n );\n requiredImports.add(wrapperName);\n requiredImports.add('t');\n\n return existingNode;\n }\n }\n }\n }\n\n // 1. Primitives\n if (val === null) return b.literal(null);\n if (\n typeof val === 'string' ||\n typeof val === 'number' ||\n typeof val === 'boolean'\n ) {\n if (unwrappedExisting) {\n // Preserve existing template literals (backticks)\n if (\n n.TemplateLiteral.check(unwrappedExisting) &&\n unwrappedExisting.quasis[0] &&\n unwrappedExisting.expressions.length === 0\n ) {\n unwrappedExisting.quasis[0].value.raw = String(val);\n unwrappedExisting.quasis[0].value.cooked = String(val);\n return existingNode;\n }\n // Preserve existing standard literals (keeps quote styling)\n if (\n n.Literal.check(unwrappedExisting) ||\n n.StringLiteral.check(unwrappedExisting)\n ) {\n unwrappedExisting.value = val;\n return existingNode;\n }\n }\n\n // Force multiline strings to use Template Literals\n if (typeof val === 'string' && val.includes('\\n')) {\n return b.templateLiteral(\n [b.templateElement({ raw: val, cooked: val }, true)],\n []\n );\n }\n return b.literal(val);\n }\n\n // 2. Arrays\n if (Array.isArray(val)) {\n if (unwrappedExisting && n.ArrayExpression.check(unwrappedExisting)) {\n const elements = [...unwrappedExisting.elements];\n val.forEach((item, i) => {\n const elementNode = buildNodeForValue(\n item,\n elements[i],\n fallbackLocale,\n requiredImports\n );\n\n elements[i] =\n elementNode === SKIP_WRITE\n ? (elements[i] ?? b.literal(null))\n : elementNode;\n });\n\n if (elements.length > val.length) elements.length = val.length;\n unwrappedExisting.elements = elements as any;\n\n return existingNode;\n } else {\n return b.arrayExpression(\n val.map((item) => {\n const elementNode = buildNodeForValue(\n item,\n null,\n fallbackLocale,\n requiredImports\n );\n\n return elementNode === SKIP_WRITE ? b.literal(null) : elementNode;\n })\n );\n }\n }\n\n // 3. Intlayer Specialized Nodes — dispatched through the exhaustive registry\n const nodeType =\n val && typeof val === 'object' && !Array.isArray(val)\n ? getNodeType(val as ContentNode)\n : null;\n\n if (nodeType) {\n const strategy = nodeTypeStrategies[nodeType];\n\n // Framework elements (React/Preact/Solid) cannot be serialized back to\n // source: keep whatever the file already declares.\n if (strategy.kind === 'preserve') {\n return existingNode ?? SKIP_WRITE;\n }\n\n const hasNodeData = Object.hasOwn(val as object, nodeType);\n const nodeData = (val as any)[nodeType];\n\n // A typed node missing its payload is malformed — fall back to the generic\n // object branch rather than printing a broken helper call.\n if (strategy.kind !== 'structural' && hasNodeData) {\n if (strategy.kind === 'unwrap') {\n return buildNodeForValue(\n nodeData,\n existingNode,\n fallbackLocale,\n requiredImports\n );\n }\n\n const { helper } = strategy;\n\n requiredImports.add(helper);\n\n const isMatchingCall =\n existingNode &&\n n.CallExpression.check(existingNode) &&\n n.Identifier.check(existingNode.callee) &&\n existingNode.callee.name === helper;\n\n if (strategy.kind === 'helperRecord') {\n const recordArgument =\n isMatchingCall &&\n existingNode.arguments.length > 0 &&\n n.ObjectExpression.check(existingNode.arguments[0])\n ? existingNode.arguments[0]\n : b.objectExpression([]);\n\n updateObjectLiteral(\n recordArgument,\n nodeData ?? {},\n fallbackLocale,\n requiredImports\n );\n\n const callArguments = [\n recordArgument,\n ...(strategy.getExtraArguments?.(val as Record<string, unknown>) ??\n []),\n ];\n\n if (isMatchingCall) {\n syncCallArguments(existingNode, callArguments);\n\n return existingNode;\n }\n\n return b.callExpression(b.identifier(helper), callArguments);\n }\n\n if (strategy.kind === 'helperValue') {\n const contentArgument = buildNodeForValue(\n nodeData,\n isMatchingCall && existingNode.arguments.length > 0\n ? existingNode.arguments[0]\n : null,\n fallbackLocale,\n requiredImports\n );\n\n if (contentArgument === SKIP_WRITE) {\n return existingNode ?? SKIP_WRITE;\n }\n\n if (isMatchingCall) {\n // Keep any extra argument the source declares (custom components\n // passed to `md()` / `html()`), which the node does not carry back.\n syncCallArguments(existingNode, [\n contentArgument,\n ...existingNode.arguments.slice(1),\n ]);\n\n return existingNode;\n }\n\n return b.callExpression(b.identifier(helper), [contentArgument]);\n }\n\n // strategy.kind === 'nesting'\n const callArguments = [b.literal(nodeData?.dictionaryKey ?? '')];\n\n if (nodeData?.path) callArguments.push(b.literal(nodeData.path));\n\n if (isMatchingCall) {\n syncCallArguments(existingNode, callArguments);\n\n return existingNode;\n }\n\n return b.callExpression(b.identifier(helper), callArguments);\n }\n }\n\n // 4. Plain Object\n const objNode =\n unwrappedExisting && n.ObjectExpression.check(unwrappedExisting)\n ? unwrappedExisting\n : b.objectExpression([]);\n\n updateObjectLiteral(objNode, val, fallbackLocale, requiredImports);\n\n return existingNode && unwrappedExisting === existingNode\n ? objNode\n : existingNode || objNode;\n};\n\n/**\n * Recursively updates the AST object literal properties.\n */\nconst updateObjectLiteral = (\n node: recast.types.namedTypes.ObjectExpression,\n data: Record<string, any>,\n fallbackLocale: string | undefined,\n requiredImports: Set<string>\n) => {\n for (const [key, val] of Object.entries(data)) {\n if (val === undefined) continue;\n\n const existingProp = getMatchingProperty(node, key);\n\n if (existingProp) {\n const valueNode = buildNodeForValue(\n val,\n existingProp.value,\n fallbackLocale,\n requiredImports\n );\n\n if (valueNode !== SKIP_WRITE) {\n existingProp.value = valueNode;\n }\n } else {\n const isValidId = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key);\n const keyNode = isValidId ? b.identifier(key) : b.literal(key);\n const valueNode = buildNodeForValue(\n val,\n null,\n fallbackLocale,\n requiredImports\n );\n\n if (valueNode === SKIP_WRITE) continue;\n\n node.properties.push(b.property('init', keyNode, valueNode));\n }\n }\n};\n\n/**\n * Modifies the AST's top-level imports to inject dynamically needed helper utilities seamlessly.\n */\nconst addImports = (ast: any, requiredImports: Set<string>, isESM: boolean) => {\n if (requiredImports.size === 0) return;\n\n const existingCoreImports = new Set<string>();\n let coreImportPath: any = null;\n\n recast.visit(ast, {\n visitImportDeclaration(path) {\n const source = path.node.source.value;\n\n if (source === 'intlayer') {\n coreImportPath = path;\n path.node.specifiers?.forEach((spec) => {\n if (\n n.ImportSpecifier.check(spec) &&\n typeof spec.imported.name === 'string'\n ) {\n existingCoreImports.add(spec.imported.name);\n }\n });\n }\n\n return false;\n },\n visitVariableDeclaration(path) {\n path.node.declarations.forEach((decl) => {\n if (\n n.VariableDeclarator.check(decl) &&\n n.CallExpression.check(decl.init) &&\n n.Identifier.check(decl.init.callee) &&\n decl.init.callee.name === 'require'\n ) {\n const arg = decl.init.arguments[0];\n\n if (n.Literal.check(arg)) {\n if (arg.value === 'intlayer') {\n if (n.ObjectPattern.check(decl.id)) {\n decl.id.properties.forEach((prop) => {\n if (\n n.Property.check(prop) &&\n (n.Identifier.check(prop.key) ||\n n.Identifier.check(prop.value))\n ) {\n const name = n.Identifier.check(prop.key)\n ? prop.key.name\n : (prop.value as any).name;\n existingCoreImports.add(name);\n }\n });\n } else if (n.Identifier.check(decl.id)) {\n // Handle const intlayer = require('intlayer')\n existingCoreImports.add(decl.id.name);\n }\n }\n }\n }\n });\n\n return false;\n },\n });\n\n const missingCore = Array.from(requiredImports).filter(\n (imp) => !existingCoreImports.has(imp)\n );\n\n if (missingCore.length === 0) return;\n\n if (isESM) {\n if (coreImportPath) {\n missingCore.forEach((imp) => {\n coreImportPath.node.specifiers.push(\n b.importSpecifier(b.identifier(imp))\n );\n });\n coreImportPath.node.specifiers.sort((a: any, b: any) =>\n a.imported.name.localeCompare(b.imported.name)\n );\n } else {\n const specifiers = missingCore\n .sort()\n .map((imp) => b.importSpecifier(b.identifier(imp)));\n const newImport = b.importDeclaration(specifiers, b.literal('intlayer'));\n ast.program.body.unshift(newImport);\n }\n } else {\n let insertIndex = 0;\n\n if (\n ast.program.body.length > 0 &&\n n.ExpressionStatement.check(ast.program.body[0]) &&\n n.Literal.check(ast.program.body[0].expression)\n ) {\n insertIndex = 1; // Insert after 'use strict'\n }\n const cjsLines: any[] = [];\n\n const properties = missingCore.sort().map((imp) => {\n const prop = b.property('init', b.identifier(imp), b.identifier(imp));\n prop.shorthand = true;\n\n return prop;\n });\n cjsLines.push(\n b.variableDeclaration('const', [\n b.variableDeclarator(\n b.objectPattern(properties),\n b.callExpression(b.identifier('require'), [b.literal('intlayer')])\n ),\n ])\n );\n\n ast.program.body.splice(insertIndex, 0, ...cjsLines);\n }\n};\n\n/**\n * Updates a JS/TS file seamlessly to map new localization keys, arrays, complex nodes and nested dictionaries gracefully using AST updates via Recast parser.\n */\nexport const transformJSFile = async (\n fileContent: string,\n dictionary: Dictionary,\n fallbackLocale?: Locale,\n noMetadata?: boolean\n): Promise<string> => {\n if (!dictionary || typeof dictionary !== 'object') return fileContent;\n\n let ast: any;\n try {\n ast = recast.parse(fileContent, {\n parser: babelTsParser,\n });\n } catch (error) {\n console.error({ error });\n return fileContent;\n }\n\n let rootObject: any = null;\n let isESM = false;\n\n recast.visit(ast, {\n visitExportDefaultDeclaration() {\n isESM = true;\n\n return false;\n },\n visitImportDeclaration() {\n isESM = true;\n\n return false;\n },\n });\n\n recast.visit(ast, {\n visitExportDefaultDeclaration(path) {\n const decl = path.node.declaration;\n const unwrappedDecl = unwrap(decl);\n\n if (n.ObjectExpression.check(unwrappedDecl)) {\n rootObject = unwrappedDecl;\n } else if (n.Identifier.check(unwrappedDecl)) {\n const varName = unwrappedDecl.name;\n recast.visit(ast, {\n visitVariableDeclarator(vp) {\n const unwrappedInit = unwrap(vp.node.init);\n\n if (\n n.Identifier.check(vp.node.id) &&\n vp.node.id.name === varName &&\n n.ObjectExpression.check(unwrappedInit)\n ) {\n rootObject = unwrappedInit;\n }\n\n return false;\n },\n });\n }\n\n return false;\n },\n visitAssignmentExpression(path) {\n const left = path.node.left;\n\n if (n.MemberExpression.check(left)) {\n if (\n n.Identifier.check(left.object) &&\n left.object.name === 'module' &&\n n.Identifier.check(left.property) &&\n left.property.name === 'exports'\n ) {\n const unwrappedRight = unwrap(path.node.right);\n\n if (n.ObjectExpression.check(unwrappedRight)) {\n rootObject = unwrappedRight;\n }\n }\n\n if (\n n.Identifier.check(left.object) &&\n left.object.name === 'exports' &&\n n.Identifier.check(left.property) &&\n left.property.name === 'default'\n ) {\n const unwrappedRight = unwrap(path.node.right);\n\n if (n.ObjectExpression.check(unwrappedRight)) {\n rootObject = unwrappedRight;\n }\n }\n }\n this.traverse(path);\n },\n });\n\n if (!rootObject) {\n recast.visit(ast, {\n visitVariableDeclarator(path) {\n const unwrappedInit = unwrap(path.node.init);\n\n if (!rootObject && n.ObjectExpression.check(unwrappedInit)) {\n rootObject = unwrappedInit;\n }\n\n return false;\n },\n });\n }\n\n if (!rootObject) return fileContent;\n\n const requiredImports = new Set<string>();\n const effectiveFallbackLocale = (fallbackLocale as string) ?? 'en';\n\n const metadataProperties = [\n 'id',\n 'locale',\n 'filled',\n 'fill',\n 'title',\n 'description',\n 'tags',\n 'version',\n 'priority',\n 'contentAutoTransformation',\n ];\n\n if (noMetadata) {\n // Remove key, content and metadata properties if they exist\n rootObject.properties = rootObject.properties.filter((prop: any) => {\n if (n.Property.check(prop) || n.ObjectProperty.check(prop)) {\n let propName = '';\n if (n.Identifier.check(prop.key)) propName = prop.key.name;\n else if (n.StringLiteral.check(prop.key)) propName = prop.key.value;\n else if (n.Literal.check(prop.key)) propName = String(prop.key.value);\n\n return !['key', 'content', ...metadataProperties].includes(propName);\n }\n return true;\n });\n\n // Update satisfies type if exists\n recast.visit(ast, {\n visitNode(path) {\n const node = path.node;\n if (\n (n.TSSatisfiesExpression?.check(node) ||\n node.type === 'TSSatisfiesExpression') &&\n (node as any).typeAnnotation &&\n n.TSTypeReference.check((node as any).typeAnnotation) &&\n n.Identifier.check((node as any).typeAnnotation.typeName) &&\n (node as any).typeAnnotation.typeName.name === 'Dictionary'\n ) {\n (node as any).typeAnnotation = b.tsIndexedAccessType(\n b.tsTypeReference(b.identifier('Dictionary')),\n b.tsLiteralType(b.stringLiteral('content'))\n );\n }\n this.traverse(path);\n },\n });\n } else {\n for (const prop of metadataProperties) {\n if ((dictionary as any)[prop] !== undefined) {\n updateObjectLiteral(\n rootObject,\n { [prop]: (dictionary as any)[prop] },\n undefined,\n requiredImports\n );\n }\n }\n }\n\n if (dictionary.content !== undefined) {\n updateObjectLiteral(\n rootObject,\n noMetadata\n ? (dictionary.content as Record<string, any>)\n : { content: dictionary.content },\n effectiveFallbackLocale,\n requiredImports\n );\n }\n\n addImports(ast, requiredImports, isESM);\n\n return recast.print(ast).code;\n};\n"],"mappings":"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"}
@@ -50,14 +50,21 @@ const writeFileIfChanged = async (path, data, { encoding = "utf8", tempDir, atom
50
50
  const tempPath = tempDir ? (0, node_path.join)(tempDir, tempFileName) : `${path}.${tempFileName}`;
51
51
  activeTempFiles.add(tempPath);
52
52
  try {
53
- await (0, node_fs_promises.writeFile)(tempPath, newData);
54
- if (modeToRestore !== void 0) {
55
- if (defaultFileMode === void 0) try {
56
- defaultFileMode = (await (0, node_fs_promises.stat)(tempPath)).mode & 511;
57
- } catch {}
58
- if (modeToRestore !== defaultFileMode) await (0, node_fs_promises.chmod)(tempPath, modeToRestore);
53
+ for (let attempt = 0;; attempt++) try {
54
+ await (0, node_fs_promises.writeFile)(tempPath, newData);
55
+ if (modeToRestore !== void 0) {
56
+ if (defaultFileMode === void 0) try {
57
+ defaultFileMode = (await (0, node_fs_promises.stat)(tempPath)).mode & 511;
58
+ } catch {}
59
+ if (modeToRestore !== defaultFileMode) await (0, node_fs_promises.chmod)(tempPath, modeToRestore);
60
+ }
61
+ await (0, node_fs_promises.rename)(tempPath, path);
62
+ break;
63
+ } catch (error) {
64
+ if (!(error.code === "ENOENT") || attempt > 0) throw error;
65
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(path), { recursive: true });
66
+ if (tempDir) await (0, node_fs_promises.mkdir)(tempDir, { recursive: true });
59
67
  }
60
- await (0, node_fs_promises.rename)(tempPath, path);
61
68
  } catch (error) {
62
69
  try {
63
70
  await (0, node_fs_promises.rm)(tempPath, { force: true });