{"version":3,"file":"writeFetchDictionary.cjs","names":["getPathHash","OUTPUT_FORMAT","normalizePath","relative","mkdir","resolve","parallelize","writeFileIfChanged","colorizePath"],"sources":["../../../src/buildIntlayerDictionary/writeFetchDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { relative, resolve } from 'node:path';\nimport { OUTPUT_FORMAT } from '@intlayer/config/defaultValues';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin, normalizePath } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { getPathHash } from '../utils/getPathHash';\nimport { parallelize } from '../utils/parallelize';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\nimport type { LocalizedDictionaryOutput } from './writeDynamicDictionary';\n\n/**\n * Local identifier of the dynamic loader backing a nest target's fallback.\n * Hashed so any dictionary key yields a valid identifier.\n */\nconst dynamicLoaderIdentifier = (key: string): string =>\n  `_dyn${getPathHash(key)}`;\n\n/**\n * Fetches one dictionary for a locale from the live-sync server, falling back\n * to its own build-time dynamic chunk when the request fails.\n *\n * `fallbackLoader` must be the loader of `key` itself — a nest target falling\n * back to its consumer's chunk would attach the wrong content.\n */\nconst buildLiveFetchExpression = (\n  key: string,\n  locale: string,\n  fallbackLoader: string\n): string =>\n  `(async () => {\\n` +\n  `      try {\\n` +\n  `        const res = await fetch(\\`\\${editor.liveSyncURL}/dictionaries/${key}/${locale}\\`);\\n` +\n  `        return await res.json();\\n` +\n  `      } catch {\\n` +\n  `        return ${fallbackLoader}['${locale}']();\\n` +\n  `      }\\n` +\n  `    })()`;\n\n/**\n * Generates the live (fetch-mode) entry point of a dictionary.\n *\n * When the dictionary references others through `nest()`, those are fetched\n * live alongside it and attached as `nestedDictionaries`, so a remote edit to a\n * nested dictionary propagates through the reference instead of resolving\n * against the build-time snapshot.\n *\n * @param key - The dictionary key.\n * @param locales - Locales to emit a loader for.\n * @param relativePrefix - Relative path to the dynamic dictionaries directory.\n * @param format - Output module format.\n * @param nestedKeys - Keys referenced through `nest()`, transitively.\n */\nexport const generateDictionaryEntryPoint = (\n  key: string,\n  locales: string[],\n  relativePrefix: string,\n  format: 'cjs' | 'esm' = 'esm',\n  nestedKeys: string[] = []\n): string => {\n  const extension = format === 'cjs' ? 'cjs' : 'mjs';\n  const sortedNestedKeys = [...nestedKeys].sort((a, b) => a.localeCompare(b));\n\n  const localeEntries = locales\n    .sort((a, b) => String(a).localeCompare(String(b)))\n    .map((locale) => {\n      if (sortedNestedKeys.length === 0) {\n        return (\n          `  '${locale}': async () => {\\n` +\n          `    try {\\n` +\n          `      const res = await fetch(\\`\\${editor.liveSyncURL}/dictionaries/${key}/${locale}\\`);\\n` +\n          `      return await res.json();\\n` +\n          `    } catch {\\n` +\n          `      return dynContent['${locale}']();\\n` +\n          `    }\\n` +\n          `  }`\n        );\n      }\n\n      const nestedFetches = sortedNestedKeys\n        .map((nestedKey) =>\n          buildLiveFetchExpression(\n            nestedKey,\n            locale,\n            dynamicLoaderIdentifier(nestedKey)\n          )\n        )\n        .join(',\\n    ');\n\n      const attachedEntries = sortedNestedKeys\n        .map((nestedKey, index) => `'${nestedKey}': _nested[${index}]`)\n        .join(', ');\n\n      return (\n        `  '${locale}': async () => {\\n` +\n        `    const [_dictionary, ..._nested] = await Promise.all([\\n` +\n        `    ${buildLiveFetchExpression(key, locale, 'dynContent')},\\n` +\n        `    ${nestedFetches}\\n` +\n        `    ]);\\n` +\n        `    return { ..._dictionary, nestedDictionaries: { ${attachedEntries} } };\\n` +\n        `  }`\n      );\n    })\n    .join(',\\n');\n\n  // Each nest target falls back to its own dynamic chunk when the live request\n  // fails, so a failure never attaches the consumer's content under its key.\n  const nestedImports = sortedNestedKeys\n    .map((nestedKey) =>\n      format === 'esm'\n        ? `import ${dynamicLoaderIdentifier(nestedKey)} from '${relativePrefix}/${nestedKey}.${extension}';`\n        : `const ${dynamicLoaderIdentifier(nestedKey)} = require('${relativePrefix}/${nestedKey}.${extension}');`\n    )\n    .join('\\n');\n\n  if (format === 'esm') {\n    return (\n      `import { editor } from 'intlayer';\\n` +\n      `import dynContent from '${relativePrefix}/${key}.${extension}';\\n` +\n      `${nestedImports}\\n\\n` +\n      `const content = {\\n${localeEntries}\\n};\\n\\n` +\n      `export default content;\\n`\n    );\n  }\n  return (\n    `const { editor } = require('intlayer');\\n` +\n    `const dynContent = require('${relativePrefix}/${key}.${extension}');\\n` +\n    `${nestedImports}\\n\\n` +\n    `module.exports = {\\n${localeEntries}\\n};\\n`\n  );\n};\n\n/**\n * Write the localized dictionaries to the dictionariesDir\n * @param mergedDictionaries - The merged dictionaries\n * @param configuration - The configuration\n * @returns The final dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/fetch_dictionary/home.mjs\n * // .intlayer/fetch_dictionary/home.cjs\n * ```\n */\nexport const writeFetchDictionary = async (\n  dynamicDictionaries: LocalizedDictionaryOutput,\n  configuration: IntlayerConfig,\n  formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT,\n  nestedDictionaryGraph: Map<string, Set<string>> = new Map()\n): Promise<LocalizedDictionaryOutput> => {\n  const { fetchDictionariesDir, dynamicDictionariesDir } = configuration.system;\n  const { locales } = configuration.internationalization;\n\n  // Compute relative path from fetch dir to dynamic dir\n  let relativePrefix = normalizePath(\n    relative(fetchDictionariesDir, dynamicDictionariesDir)\n  );\n  if (!relativePrefix.startsWith('.')) {\n    relativePrefix = `./${relativePrefix}`;\n  }\n\n  await mkdir(resolve(fetchDictionariesDir), { recursive: true });\n\n  const resultDictionariesPaths: LocalizedDictionaryOutput = {};\n\n  // Write entry points for each dictionary in parallel\n  await parallelize(Object.entries(dynamicDictionaries), async ([key]) => {\n    if (key === 'undefined') return;\n\n    await parallelize(formats, async (format) => {\n      const extension = format === 'cjs' ? 'cjs' : 'mjs';\n      const content = generateDictionaryEntryPoint(\n        key,\n        locales,\n        relativePrefix,\n        format,\n        [...(nestedDictionaryGraph.get(key) ?? [])]\n      );\n\n      const fetchEntryPath = resolve(\n        fetchDictionariesDir,\n        `${key}.${extension}`\n      );\n      assertPathWithin(fetchEntryPath, fetchDictionariesDir);\n\n      await writeFileIfChanged(fetchEntryPath, content).catch((err) => {\n        console.error(\n          `Error creating fetch ${colorizePath(fetchEntryPath)}:`,\n          err\n        );\n      });\n    });\n  });\n\n  return resultDictionariesPaths;\n};\n"],"mappings":";;;;;;;;;;;;;;;AAeA,MAAM,2BAA2B,QAC/B,OAAOA,sCAAY,GAAG;;;;;;;;AASxB,MAAM,4BACJ,KACA,QACA,mBAEA;;wEAEyE,IAAI,GAAG,OAAO,0EAGrE,eAAe,IAAI,OAAO;;;;;;;;;;;;;;;AAkB9C,MAAa,gCACX,KACA,SACA,gBACA,SAAwB,OACxB,aAAuB,CAAC,MACb;CACX,MAAM,YAAY,WAAW,QAAQ,QAAQ;CAC7C,MAAM,mBAAmB,CAAC,GAAG,UAAU,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;CAE1E,MAAM,gBAAgB,QACnB,MAAM,GAAG,MAAM,OAAO,CAAC,CAAC,CAAC,cAAc,OAAO,CAAC,CAAC,CAAC,CAAC,CAClD,KAAK,WAAW;EACf,IAAI,iBAAiB,WAAW,GAC9B,OACE,MAAM,OAAO,mGAE0D,IAAI,GAAG,OAAO,gFAGzD,OAAO;EAMvC,MAAM,gBAAgB,iBACnB,KAAK,cACJ,yBACE,WACA,QACA,wBAAwB,SAAS,CACnC,CACF,CAAC,CACA,KAAK,SAAS;EAEjB,MAAM,kBAAkB,iBACrB,KAAK,WAAW,UAAU,IAAI,UAAU,aAAa,MAAM,EAAE,CAAC,CAC9D,KAAK,IAAI;EAEZ,OACE,MAAM,OAAO,mFAEN,yBAAyB,KAAK,QAAQ,YAAY,EAAE,SACpD,cAAc,gEAEiC,gBAAgB;CAG1E,CAAC,CAAC,CACD,KAAK,KAAK;CAIb,MAAM,gBAAgB,iBACnB,KAAK,cACJ,WAAW,QACP,UAAU,wBAAwB,SAAS,EAAE,SAAS,eAAe,GAAG,UAAU,GAAG,UAAU,MAC/F,SAAS,wBAAwB,SAAS,EAAE,cAAc,eAAe,GAAG,UAAU,GAAG,UAAU,IACzG,CAAC,CACA,KAAK,IAAI;CAEZ,IAAI,WAAW,OACb,OACE,+DAC2B,eAAe,GAAG,IAAI,GAAG,UAAU,MAC3D,cAAc,yBACK,cAAc;CAIxC,OACE,wEAC+B,eAAe,GAAG,IAAI,GAAG,UAAU,OAC/D,cAAc,0BACM,cAAc;AAEzC;;;;;;;;;;;;;;;;;AAkBA,MAAa,uBAAuB,OAClC,qBACA,eACA,UAA6BC,8CAC7B,wCAAkD,IAAI,IAAI,MACnB;CACvC,MAAM,EAAE,sBAAsB,2BAA2B,cAAc;CACvE,MAAM,EAAE,YAAY,cAAc;CAGlC,IAAI,qBAAiBC,0CACnBC,oBAAS,sBAAsB,sBAAsB,CACvD;CACA,IAAI,CAAC,eAAe,WAAW,GAAG,GAChC,iBAAiB,KAAK;CAGxB,UAAMC,4BAAMC,mBAAQ,oBAAoB,GAAG,EAAE,WAAW,KAAK,CAAC;CAE9D,MAAM,0BAAqD,CAAC;CAG5D,MAAMC,sCAAY,OAAO,QAAQ,mBAAmB,GAAG,OAAO,CAAC,SAAS;EACtE,IAAI,QAAQ,aAAa;EAEzB,MAAMA,sCAAY,SAAS,OAAO,WAAW;GAC3C,MAAM,YAAY,WAAW,QAAQ,QAAQ;GAC7C,MAAM,UAAU,6BACd,KACA,SACA,gBACA,QACA,CAAC,GAAI,sBAAsB,IAAI,GAAG,KAAK,CAAC,CAAE,CAC5C;GAEA,MAAM,qBAAiBD,mBACrB,sBACA,GAAG,IAAI,GAAG,WACZ;GACA,6CAAiB,gBAAgB,oBAAoB;GAErD,MAAME,8CAAmB,gBAAgB,OAAO,CAAC,CAAC,OAAO,QAAQ;IAC/D,QAAQ,MACN,4BAAwBC,sCAAa,cAAc,EAAE,IACrD,GACF;GACF,CAAC;EACH,CAAC;CACH,CAAC;CAED,OAAO;AACT"}