Skip to main content
Glama
writeContentDeclaration.ts6.43 kB
import { mkdir, rm, writeFile } from 'node:fs/promises'; import { dirname, extname, join, resolve } from 'node:path'; import { isDeepStrictEqual } from 'node:util'; import { getFilteredLocalesDictionary, getPerLocaleDictionary, } from '@intlayer/core'; import type { Dictionary, IntlayerConfig, Locale, LocalesValues, } from '@intlayer/types'; import { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry'; import { type Extension, getFormatFromExtension, } from '../utils/getFormatFromExtension'; import type { DictionaryStatus } from './dictionaryStatus'; import { processContentDeclarationContent } from './processContentDeclarationContent'; import { writeJSFile } from './writeJSFile'; const formatContentDeclaration = async ( dictionary: Dictionary, configuration: IntlayerConfig, localeList?: LocalesValues[] ) => { /** * Clean Markdown, Insertion, File, etc. node metadata */ const processedDictionary = await processContentDeclarationContent(dictionary); let content = processedDictionary.content; /** * Filter locales content */ if (dictionary.locale) { content = getPerLocaleDictionary( processedDictionary, dictionary.locale ).content; } else if (localeList) { content = getFilteredLocalesDictionary( processedDictionary, localeList ).content; } let pluginFormatResult: any = { ...dictionary, content, } satisfies Dictionary; /** * Format the dictionary with the plugins */ for await (const plugin of configuration.plugins ?? []) { if (plugin.formatOutput) { const formattedResult = await plugin.formatOutput?.({ dictionary: pluginFormatResult, configuration, }); if (formattedResult) { pluginFormatResult = formattedResult; } } } const isDictionaryFormat = pluginFormatResult.content && pluginFormatResult.key; if (!isDictionaryFormat) return pluginFormatResult; let result: Dictionary = { key: dictionary.key, id: dictionary.id, title: dictionary.title, description: dictionary.description, tags: dictionary.tags, locale: dictionary.locale, fill: dictionary.fill, filled: dictionary.filled, priority: dictionary.priority, live: dictionary.live, version: dictionary.version, content, }; /** * Add $schema to JSON dictionaries */ const extension = ( dictionary.filePath ? extname(dictionary.filePath) : '.json' ) as Extension; const format = getFormatFromExtension(extension); if ( format === 'json' && pluginFormatResult.content && pluginFormatResult.key ) { result = { $schema: 'https://intlayer.org/schema.json', ...result, }; } return result; }; type WriteContentDeclarationOptions = { newDictionariesPath?: string; localeList?: LocalesValues[]; fallbackLocale?: Locale; }; const defaultOptions = { newDictionariesPath: 'intlayer-dictionaries', } satisfies WriteContentDeclarationOptions; export const writeContentDeclaration = async ( dictionary: Dictionary, configuration: IntlayerConfig, options?: WriteContentDeclarationOptions ): Promise<{ status: DictionaryStatus; path: string }> => { const { content } = configuration; const { baseDir } = content; const { newDictionariesPath, localeList } = { ...defaultOptions, ...options, }; const newDictionaryLocationPath = join(baseDir, newDictionariesPath); const unmergedDictionariesRecord = getUnmergedDictionaries(configuration); const unmergedDictionaries = unmergedDictionariesRecord[ dictionary.key ] as Dictionary[]; const existingDictionary = unmergedDictionaries?.find( (el) => el.localId === dictionary.localId ); const formattedContentDeclaration = await formatContentDeclaration( dictionary, configuration, localeList ); if (existingDictionary?.filePath) { // Compare existing dictionary content with new dictionary content const isSameContent = isDeepStrictEqual(existingDictionary, dictionary); const filePath = resolve( configuration.content.baseDir, existingDictionary.filePath ); // Up to date, nothing to do if (isSameContent) { return { status: 'up-to-date', path: filePath, }; } await writeFileWithDirectories( filePath, formattedContentDeclaration, configuration ); return { status: 'updated', path: filePath }; } if (dictionary.filePath) { const filePath = resolve( configuration.content.baseDir, dictionary.filePath ); await writeFileWithDirectories( filePath, formattedContentDeclaration, configuration ); return { status: 'created', path: filePath }; } // No existing dictionary, write to new location const contentDeclarationPath = join( newDictionaryLocationPath, `${dictionary.key}.content.json` ); await writeFileWithDirectories( contentDeclarationPath, formattedContentDeclaration, configuration ); return { status: 'imported', path: contentDeclarationPath, }; }; const writeFileWithDirectories = async ( absoluteFilePath: string, dictionary: Dictionary, configuration: IntlayerConfig ): Promise<void> => { // Extract the directory from the file path const dir = dirname(absoluteFilePath); // Create the directory recursively await mkdir(dir, { recursive: true }); const extension = extname(absoluteFilePath); const acceptedExtensions = configuration.content.fileExtensions.map( (extension) => extname(extension) ); if (!acceptedExtensions.includes(extension)) { throw new Error( `Invalid file extension: ${extension}, file: ${absoluteFilePath}` ); } if (extension === '.json') { const jsonDictionary = JSON.stringify(dictionary, null, 2); // Write the file await writeFile(absoluteFilePath, `${jsonDictionary}\n`); // Add a new line at the end of the file to avoid formatting issues with VSCode return; } await writeJSFile(absoluteFilePath, dictionary, configuration); // remove the cache as content has changed // Will force a new preparation of the intlayer on next build try { const sentinelPath = join( configuration.content.cacheDir, 'intlayer-prepared.lock' ); await rm(sentinelPath, { recursive: true }); } catch {} };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aymericzip/intlayer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server