{"version":3,"file":"writeContentDeclaration.cjs","names":["processContentDeclarationContent","getFormatFromExtension","COMPILER_NO_METADATA","readDictionariesFromDisk","writeMarkdownFile","writeYamlFile","transformJSONFile","detectFormatCommand","writeJSFile"],"sources":["../../../src/writeContentDeclaration/writeContentDeclaration.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';\nimport { basename, dirname, extname, join, resolve } from 'node:path';\nimport { isDeepStrictEqual } from 'node:util';\nimport { COMPILER_NO_METADATA } from '@intlayer/config/defaultValues';\nimport {\n  getFilteredLocalesDictionary,\n  getPerLocaleDictionary,\n} from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { detectFormatCommand } from '../detectFormatCommand';\nimport {\n  type Extension,\n  getFormatFromExtension,\n} from '../utils/getFormatFromExtension';\nimport { readDictionariesFromDisk } from '../utils/readDictionariesFromDisk';\nimport type { DictionaryStatus } from './dictionaryStatus';\nimport { processContentDeclarationContent } from './processContentDeclarationContent';\nimport { transformJSONFile } from './transformJSONFile';\nimport { writeJSFile } from './writeJSFile';\nimport { writeMarkdownFile } from './writeMarkdownFile';\nimport { writeYamlFile } from './writeYamlFile';\n\nconst formatContentDeclaration = async (\n  dictionary: Dictionary,\n  configuration: IntlayerConfig,\n  localeList?: LocalesValues[]\n) => {\n  /**\n   * Clean Markdown, Insertion, File, etc. node metadata\n   */\n  const processedDictionary =\n    await processContentDeclarationContent(dictionary);\n\n  let content = processedDictionary.content;\n\n  /**\n   * Filter locales content\n   */\n\n  if (dictionary.locale) {\n    content = getPerLocaleDictionary(\n      processedDictionary,\n      dictionary.locale\n    ).content;\n  } else if (localeList) {\n    content = getFilteredLocalesDictionary(\n      processedDictionary,\n      localeList\n    ).content;\n  }\n\n  let pluginFormatResult: any = {\n    ...dictionary,\n    content,\n  } satisfies Dictionary;\n\n  /**\n   * Format the dictionary with the plugins\n   */\n\n  for await (const plugin of configuration.plugins ?? []) {\n    if (plugin.formatOutput) {\n      const formattedResult = await plugin.formatOutput?.({\n        dictionary: pluginFormatResult,\n        configuration,\n      });\n\n      if (formattedResult) {\n        pluginFormatResult = formattedResult;\n      }\n    }\n  }\n\n  const isDictionaryFormat =\n    pluginFormatResult.content && pluginFormatResult.key;\n\n  if (!isDictionaryFormat) return pluginFormatResult;\n\n  // Build result from the original dictionary so that extra user-defined fields\n  // (e.g. custom frontmatter in markdown files) are preserved.\n  // Strip internal-only fields that must never appear in persisted output.\n  const INTERNAL_FIELDS = new Set([\n    '$schema',\n    'filePath',\n    'localId',\n    'localIds',\n    'projectIds',\n  ]);\n\n  const preservedFields = Object.fromEntries(\n    Object.entries(dictionary as Record<string, unknown>).filter(\n      ([k]) => !INTERNAL_FIELDS.has(k)\n    )\n  );\n\n  let result: Dictionary = {\n    ...preservedFields,\n    content,\n  } as Dictionary;\n\n  /**\n   * Add $schema to JSON dictionaries\n   */\n  const extension = (\n    dictionary.filePath ? extname(dictionary.filePath) : '.json'\n  ) as Extension;\n  const format = getFormatFromExtension(extension);\n\n  if (\n    format === 'json' &&\n    pluginFormatResult.content &&\n    pluginFormatResult.key\n  ) {\n    result = {\n      $schema: 'https://intlayer.org/schema.json',\n      ...result,\n    };\n  }\n\n  return result;\n};\n\ntype WriteContentDeclarationOptions = {\n  newDictionariesPath?: string;\n  localeList?: LocalesValues[];\n  fallbackLocale?: Locale;\n};\n\nconst defaultOptions = {\n  newDictionariesPath: 'intlayer-dictionaries',\n} satisfies WriteContentDeclarationOptions;\n\nexport const writeContentDeclaration = async (\n  dictionary: Dictionary,\n  configuration: IntlayerConfig,\n  options?: WriteContentDeclarationOptions\n): Promise<{ status: DictionaryStatus; path: string }> => {\n  const { system, compiler } = configuration;\n  const { baseDir } = system;\n\n  const noMetadata = compiler?.noMetadata ?? COMPILER_NO_METADATA;\n  const { newDictionariesPath, localeList } = {\n    ...defaultOptions,\n    ...options,\n  };\n\n  const newDictionaryLocationPath = join(baseDir, newDictionariesPath);\n\n  const unmergedDictionariesRecord = readDictionariesFromDisk<\n    Record<string, Dictionary[]>\n  >(configuration.system.unmergedDictionariesDir);\n  const unmergedDictionaries = unmergedDictionariesRecord[\n    dictionary.key\n  ] as Dictionary[];\n\n  const existingDictionary = unmergedDictionaries?.find(\n    (el) => el.localId === dictionary.localId\n  );\n\n  const formattedContentDeclaration = await formatContentDeclaration(\n    dictionary,\n    configuration,\n    localeList\n  );\n\n  if (existingDictionary?.filePath) {\n    // Compare existing dictionary content with new dictionary content\n    const isSameContent = isDeepStrictEqual(existingDictionary, dictionary);\n\n    const filePath = resolve(\n      configuration.system.baseDir,\n      existingDictionary.filePath\n    );\n\n    // Up to date, nothing to do\n    if (isSameContent) {\n      return {\n        status: 'up-to-date',\n        path: filePath,\n      };\n    }\n\n    await writeFileWithDirectories(\n      filePath,\n      formattedContentDeclaration,\n      configuration,\n      noMetadata\n    );\n\n    return { status: 'updated', path: filePath };\n  }\n\n  if (dictionary.filePath) {\n    const filePath = resolve(configuration.system.baseDir, dictionary.filePath);\n\n    await writeFileWithDirectories(\n      filePath,\n      formattedContentDeclaration,\n      configuration,\n      noMetadata\n    );\n\n    return { status: 'created', path: filePath };\n  }\n\n  // No existing dictionary, write to new location\n  const contentDeclarationPath = join(\n    newDictionaryLocationPath,\n    `${dictionary.key}.content.json`\n  );\n\n  await writeFileWithDirectories(\n    contentDeclarationPath,\n    formattedContentDeclaration,\n    configuration,\n    noMetadata\n  );\n\n  return {\n    status: 'imported',\n    path: contentDeclarationPath,\n  };\n};\n\nconst writeFileWithDirectories = async (\n  absoluteFilePath: string,\n  dictionary: Dictionary,\n  configuration: IntlayerConfig,\n  noMetadata?: boolean\n): Promise<void> => {\n  // Extract the directory from the file path\n  const dir = dirname(absoluteFilePath);\n\n  // Create the directory recursively\n  await mkdir(dir, { recursive: true });\n\n  const extension = extname(absoluteFilePath);\n\n  if (extension === '.md' || extension === '.mdx') {\n    await writeMarkdownFile(absoluteFilePath, dictionary, configuration);\n    return;\n  }\n\n  if (extension === '.yaml' || extension === '.yml') {\n    await writeYamlFile(absoluteFilePath, dictionary, configuration);\n    return;\n  }\n\n  // Handle JSON, JSONC, and JSON5 via the AST transformer\n  if (['.json', '.jsonc', '.json5'].includes(extension)) {\n    let fileContent = '{}';\n\n    if (existsSync(absoluteFilePath)) {\n      try {\n        fileContent = await readFile(absoluteFilePath, 'utf-8');\n      } catch {\n        // ignore read errors, start with empty object\n      }\n    }\n\n    const transformedContent = transformJSONFile(\n      fileContent,\n      dictionary,\n      noMetadata\n    );\n\n    // We use standard writeFile because transformedContent is already a string\n    const tempDir = configuration.system?.tempDir;\n    if (tempDir) {\n      await mkdir(tempDir, { recursive: true });\n    }\n\n    const tempFileName = `${basename(absoluteFilePath)}.${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`;\n    const tempPath = tempDir\n      ? join(tempDir, tempFileName)\n      : `${absoluteFilePath}.${tempFileName}`;\n    try {\n      await writeFile(tempPath, transformedContent, 'utf-8');\n      await rename(tempPath, absoluteFilePath);\n    } catch (error) {\n      try {\n        await rm(tempPath, { force: true });\n      } catch {\n        // Ignore\n      }\n      throw error;\n    }\n\n    const formatCommand = detectFormatCommand(configuration);\n\n    if (formatCommand) {\n      try {\n        execSync(formatCommand.replace('{{file}}', absoluteFilePath), {\n          stdio: 'inherit',\n          cwd: configuration.system.baseDir,\n        });\n      } catch (error) {\n        console.error(error);\n      }\n    }\n\n    return;\n  }\n\n  const knownJsExtensions = new Set([\n    '.ts',\n    '.tsx',\n    '.js',\n    '.jsx',\n    '.mjs',\n    '.cjs',\n  ]);\n\n  if (!knownJsExtensions.has(extension)) {\n    // Unknown extension (e.g. .po managed by a plugin) — skip; the plugin\n    // owns this file format and writeJSFile / prettier would corrupt it.\n    return;\n  }\n\n  await writeJSFile(absoluteFilePath, dictionary, configuration, noMetadata);\n\n  // remove the cache as content has changed\n  // Will force a new preparation of the intlayer on next build\n  try {\n    const sentinelPath = join(\n      configuration.system.cacheDir,\n      'intlayer-prepared.lock'\n    );\n    await rm(sentinelPath, { recursive: true });\n  } catch {}\n};\n"],"mappings":"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"}