{"version":3,"file":"writeContentDeclaration.mjs","names":[],"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  let result: Dictionary = {\n    key: dictionary.key,\n    id: dictionary.id,\n    title: dictionary.title,\n    description: dictionary.description,\n    tags: dictionary.tags,\n    locale: dictionary.locale,\n    fill: dictionary.fill,\n    filled: dictionary.filled,\n    priority: dictionary.priority,\n    importMode: dictionary.importMode,\n    version: dictionary.version,\n    content,\n  };\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  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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"}