@intlayer/sync-json-plugin 7.0.3-canary.1 → 7.0.3
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.
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.mjs +2 -2
- package/dist/esm/index.mjs.map +1 -1
- package/package.json +14 -18
package/dist/cjs/index.cjs
CHANGED
|
@@ -81,7 +81,7 @@ const syncJSON = (options) => {
|
|
|
81
81
|
if (fill && !(0, node_path.isAbsolute)(fill)) fill = (0, node_path.join)(configuration.content.baseDir, fill);
|
|
82
82
|
const dictionaries = [];
|
|
83
83
|
for (const { locale, path, key } of dictionariesMap) {
|
|
84
|
-
const json = (configuration.build?.require ?? __intlayer_config.
|
|
84
|
+
const json = (configuration.build?.require ?? (0, __intlayer_config.getProjectRequire)())(path);
|
|
85
85
|
const filePath = (0, node_path.relative)(configuration.content.baseDir, path);
|
|
86
86
|
const dictionary = {
|
|
87
87
|
key,
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["locale: Locale | undefined","key: string | undefined","fg","result: MessagesRecord","messages: MessagesRecord","dictionariesMap: DictionariesMap","fill: string","dictionaries: Dictionary[]","json: JSONContent","ESMxCJSRequire","dictionary: Dictionary"],"sources":["../../src/index.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport { parallelize } from '@intlayer/chokidar';\nimport { ESMxCJSRequire } from '@intlayer/config';\nimport type {\n ContentNode,\n Dictionary,\n IntlayerConfig,\n LocalDictionaryId,\n Locale,\n LocalesValues,\n Plugin,\n} from '@intlayer/types';\nimport fg from 'fast-glob';\n\ntype JSONContent = Record<string, any>;\n\ntype Builder = ({\n key,\n locale,\n}: {\n key: string;\n locale: LocalesValues | (string & {});\n}) => string;\n\ntype MessagesRecord = Record<Locale, Record<Dictionary['key'], FilePath>>;\n\nconst escapeRegex = (str: string) => str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nconst extractKeyAndLocaleFromPath = (\n filePath: string,\n maskPattern: string,\n locales: Locale[]\n) => {\n const keyPlaceholder = '{{__KEY__}}';\n const localePlaceholder = '{{__LOCALE__}}';\n\n const escapedMask = escapeRegex(maskPattern);\n const localesAlternation = locales.join('|');\n\n // Build a regex from the mask to capture locale (and key if present)\n let regexStr = `^${escapedMask}$`;\n\n regexStr = regexStr.replace(\n escapeRegex(localePlaceholder),\n `(?<locale>${localesAlternation})`\n );\n\n if (maskPattern.includes(keyPlaceholder)) {\n regexStr = regexStr.replace(escapeRegex(keyPlaceholder), '(?<key>[^/]+)');\n }\n\n const maskRegex = new RegExp(regexStr);\n\n const match = maskRegex.exec(filePath);\n\n let locale: Locale | undefined;\n let key: string | undefined;\n\n if (match?.groups) {\n locale = match.groups.locale as Locale | undefined;\n key = (match.groups.key as string | undefined) ?? 'index';\n }\n\n return {\n key,\n locale,\n };\n};\n\nconst listMessages = (\n builder: Builder,\n configuration: IntlayerConfig\n): MessagesRecord => {\n const { content, internationalization } = configuration;\n\n const baseDir = content.baseDir;\n const locales = internationalization.locales as Locale[];\n\n const localePattern = `{${locales.map((locale) => locale).join(',')}}`;\n\n const globPattern = builder({ key: '*', locale: localePattern });\n const maskPattern = builder({ key: '{{__KEY__}}', locale: '{{__LOCALE__}}' });\n\n const files = fg.sync(globPattern, {\n cwd: baseDir,\n });\n\n const result: MessagesRecord = {} as MessagesRecord;\n\n for (const file of files) {\n const { key, locale } = extractKeyAndLocaleFromPath(\n file,\n maskPattern,\n locales\n );\n\n const absolutePath = isAbsolute(file) ? file : resolve(baseDir, file);\n\n if (!result[locale as Locale]) {\n result[locale as Locale] = {};\n }\n\n result[locale as Locale][key as Dictionary['key']] = absolutePath;\n }\n\n return result;\n};\n\ntype FilePath = string;\n\ntype DictionariesMap = { path: string; locale: Locale; key: string }[];\n\nconst loadMessagePathMap = (\n source: MessagesRecord | Builder,\n configuration: IntlayerConfig\n) => {\n const messages: MessagesRecord = listMessages(\n source as Builder,\n configuration\n );\n\n const dictionariesPathMap: DictionariesMap = Object.entries(messages).flatMap(\n ([locale, keysRecord]) =>\n Object.entries(keysRecord).map(([key, path]) => {\n const absolutePath = isAbsolute(path)\n ? path\n : resolve(configuration.content.baseDir, path);\n\n return {\n path: absolutePath,\n locale,\n key,\n } as DictionariesMap[number];\n })\n );\n\n return dictionariesPathMap;\n};\n\ntype SyncJSONPluginOptions = {\n /**\n * The source of the plugin.\n * Is a function to build the source from the key and locale.\n *\n * ```ts\n * syncJSON({\n * source: ({ key, locale }) => `./messages/${locale}/${key}.json`\n * })\n * ```\n */\n source: Builder;\n\n /**\n * Because Intlayer transform the JSON files into Dictionary, we need to identify the plugin in the dictionary.\n * Used to identify the plugin in the dictionary.\n *\n * In the case you have multiple plugins, you can use this to identify the plugin in the dictionary.\n *\n * ```ts\n * const config ={\n * plugins: [\n * syncJSON({\n * source: ({ key, locale }) => `./resources/${locale}/${key}.json`\n * location: 'plugin-i18next',\n * }),\n * syncJSON({\n * source: ({ key, locale }) => `./messages/${locale}/${key}.json`\n * location: 'plugin-next-intl',\n * }),\n * ]\n * }\n * ```\n */\n location?: string;\n\n /**\n * The priority of the dictionaries created by the plugin.\n *\n * In the case of conflicts with remote dictionaries, or .content files, the dictionary with the highest priority will override the other dictionaries.\n *\n * Default is -1. (.content file priority is 0)\n *\n */\n priority?: number;\n};\n\nexport const syncJSON = (options: SyncJSONPluginOptions): Plugin => {\n const { location, priority } = {\n location: 'plugin',\n priority: 0,\n ...options,\n };\n\n return {\n name: 'sync-json',\n\n loadDictionaries: async ({ configuration }) => {\n const dictionariesMap: DictionariesMap = loadMessagePathMap(\n options.source,\n configuration\n );\n\n let fill: string = options.source({\n key: '{{key}}',\n locale: '{{locale}}',\n });\n\n if (fill && !isAbsolute(fill)) {\n fill = join(configuration.content.baseDir, fill);\n }\n\n const dictionaries: Dictionary[] = [];\n\n for (const { locale, path, key } of dictionariesMap) {\n const requireFunction = configuration.build?.require ?? ESMxCJSRequire;\n const json: JSONContent = requireFunction(path as string);\n\n const filePath = relative(configuration.content.baseDir, path);\n\n const dictionary: Dictionary = {\n key,\n locale,\n fill,\n localId: `${key}::${location}::${filePath}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled:\n locale !== configuration.internationalization.defaultLocale\n ? true\n : undefined,\n content: json,\n filePath,\n priority,\n };\n\n dictionaries.push(dictionary);\n }\n\n return dictionaries;\n },\n afterBuild: async ({ dictionaries, configuration }) => {\n // Dynamic import to avoid circular dependency as core package import config, that load esbuild, that load the config file, that load the plugin\n const { getLocalizedContent } = await import('@intlayer/core');\n\n const locales = configuration.internationalization.locales;\n\n type RecordList = {\n key: string;\n dictionary: Dictionary;\n locale: Locale;\n };\n\n const recordList: RecordList[] = Object.entries(\n dictionaries.mergedDictionaries\n ).flatMap(([key, dictionary]) =>\n locales.map((locale) => ({\n key,\n dictionary: dictionary.dictionary as Dictionary,\n locale,\n }))\n );\n\n await parallelize(recordList, async ({ key, dictionary, locale }) => {\n const builderPath = options.source({\n key,\n locale,\n });\n\n // Remove function, Symbol, etc. as it can be written as JSON\n const flatContent = JSON.parse(JSON.stringify(dictionary.content));\n\n const localizedContent = getLocalizedContent(\n flatContent as unknown as ContentNode,\n locale,\n {\n dictionaryKey: key,\n keyPath: [],\n }\n );\n\n // Ensure directory exists before writing the file\n await mkdir(dirname(builderPath), { recursive: true });\n\n const stringContent = JSON.stringify(localizedContent, null, 2);\n\n await writeFile(\n builderPath,\n `${stringContent}\\n`, // Add a new line at the end of the file to avoid formatting issues with VSCode\n 'utf-8'\n );\n });\n },\n formatOutput: ({ dictionary }) => {\n if (!dictionary.filePath || !dictionary.locale) return dictionary;\n\n const builderPath = options.source({\n key: dictionary.key,\n locale: dictionary.locale,\n });\n\n // It's not one of the JSON that we synchronize, don't modify it\n if (resolve(builderPath) !== resolve(dictionary.filePath)) {\n return dictionary;\n }\n\n return dictionary.content;\n },\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["locale: Locale | undefined","key: string | undefined","fg","result: MessagesRecord","messages: MessagesRecord","dictionariesMap: DictionariesMap","fill: string","dictionaries: Dictionary[]","json: JSONContent","dictionary: Dictionary"],"sources":["../../src/index.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport { parallelize } from '@intlayer/chokidar';\nimport { getProjectRequire } from '@intlayer/config';\nimport type {\n ContentNode,\n Dictionary,\n IntlayerConfig,\n LocalDictionaryId,\n Locale,\n LocalesValues,\n Plugin,\n} from '@intlayer/types';\nimport fg from 'fast-glob';\n\ntype JSONContent = Record<string, any>;\n\ntype Builder = ({\n key,\n locale,\n}: {\n key: string;\n locale: LocalesValues | (string & {});\n}) => string;\n\ntype MessagesRecord = Record<Locale, Record<Dictionary['key'], FilePath>>;\n\nconst escapeRegex = (str: string) => str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nconst extractKeyAndLocaleFromPath = (\n filePath: string,\n maskPattern: string,\n locales: Locale[]\n) => {\n const keyPlaceholder = '{{__KEY__}}';\n const localePlaceholder = '{{__LOCALE__}}';\n\n const escapedMask = escapeRegex(maskPattern);\n const localesAlternation = locales.join('|');\n\n // Build a regex from the mask to capture locale (and key if present)\n let regexStr = `^${escapedMask}$`;\n\n regexStr = regexStr.replace(\n escapeRegex(localePlaceholder),\n `(?<locale>${localesAlternation})`\n );\n\n if (maskPattern.includes(keyPlaceholder)) {\n regexStr = regexStr.replace(escapeRegex(keyPlaceholder), '(?<key>[^/]+)');\n }\n\n const maskRegex = new RegExp(regexStr);\n\n const match = maskRegex.exec(filePath);\n\n let locale: Locale | undefined;\n let key: string | undefined;\n\n if (match?.groups) {\n locale = match.groups.locale as Locale | undefined;\n key = (match.groups.key as string | undefined) ?? 'index';\n }\n\n return {\n key,\n locale,\n };\n};\n\nconst listMessages = (\n builder: Builder,\n configuration: IntlayerConfig\n): MessagesRecord => {\n const { content, internationalization } = configuration;\n\n const baseDir = content.baseDir;\n const locales = internationalization.locales as Locale[];\n\n const localePattern = `{${locales.map((locale) => locale).join(',')}}`;\n\n const globPattern = builder({ key: '*', locale: localePattern });\n const maskPattern = builder({ key: '{{__KEY__}}', locale: '{{__LOCALE__}}' });\n\n const files = fg.sync(globPattern, {\n cwd: baseDir,\n });\n\n const result: MessagesRecord = {} as MessagesRecord;\n\n for (const file of files) {\n const { key, locale } = extractKeyAndLocaleFromPath(\n file,\n maskPattern,\n locales\n );\n\n const absolutePath = isAbsolute(file) ? file : resolve(baseDir, file);\n\n if (!result[locale as Locale]) {\n result[locale as Locale] = {};\n }\n\n result[locale as Locale][key as Dictionary['key']] = absolutePath;\n }\n\n return result;\n};\n\ntype FilePath = string;\n\ntype DictionariesMap = { path: string; locale: Locale; key: string }[];\n\nconst loadMessagePathMap = (\n source: MessagesRecord | Builder,\n configuration: IntlayerConfig\n) => {\n const messages: MessagesRecord = listMessages(\n source as Builder,\n configuration\n );\n\n const dictionariesPathMap: DictionariesMap = Object.entries(messages).flatMap(\n ([locale, keysRecord]) =>\n Object.entries(keysRecord).map(([key, path]) => {\n const absolutePath = isAbsolute(path)\n ? path\n : resolve(configuration.content.baseDir, path);\n\n return {\n path: absolutePath,\n locale,\n key,\n } as DictionariesMap[number];\n })\n );\n\n return dictionariesPathMap;\n};\n\ntype SyncJSONPluginOptions = {\n /**\n * The source of the plugin.\n * Is a function to build the source from the key and locale.\n *\n * ```ts\n * syncJSON({\n * source: ({ key, locale }) => `./messages/${locale}/${key}.json`\n * })\n * ```\n */\n source: Builder;\n\n /**\n * Because Intlayer transform the JSON files into Dictionary, we need to identify the plugin in the dictionary.\n * Used to identify the plugin in the dictionary.\n *\n * In the case you have multiple plugins, you can use this to identify the plugin in the dictionary.\n *\n * ```ts\n * const config ={\n * plugins: [\n * syncJSON({\n * source: ({ key, locale }) => `./resources/${locale}/${key}.json`\n * location: 'plugin-i18next',\n * }),\n * syncJSON({\n * source: ({ key, locale }) => `./messages/${locale}/${key}.json`\n * location: 'plugin-next-intl',\n * }),\n * ]\n * }\n * ```\n */\n location?: string;\n\n /**\n * The priority of the dictionaries created by the plugin.\n *\n * In the case of conflicts with remote dictionaries, or .content files, the dictionary with the highest priority will override the other dictionaries.\n *\n * Default is -1. (.content file priority is 0)\n *\n */\n priority?: number;\n};\n\nexport const syncJSON = (options: SyncJSONPluginOptions): Plugin => {\n const { location, priority } = {\n location: 'plugin',\n priority: 0,\n ...options,\n };\n\n return {\n name: 'sync-json',\n\n loadDictionaries: async ({ configuration }) => {\n const dictionariesMap: DictionariesMap = loadMessagePathMap(\n options.source,\n configuration\n );\n\n let fill: string = options.source({\n key: '{{key}}',\n locale: '{{locale}}',\n });\n\n if (fill && !isAbsolute(fill)) {\n fill = join(configuration.content.baseDir, fill);\n }\n\n const dictionaries: Dictionary[] = [];\n\n for (const { locale, path, key } of dictionariesMap) {\n const requireFunction =\n configuration.build?.require ?? getProjectRequire();\n const json: JSONContent = requireFunction(path as string);\n\n const filePath = relative(configuration.content.baseDir, path);\n\n const dictionary: Dictionary = {\n key,\n locale,\n fill,\n localId: `${key}::${location}::${filePath}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled:\n locale !== configuration.internationalization.defaultLocale\n ? true\n : undefined,\n content: json,\n filePath,\n priority,\n };\n\n dictionaries.push(dictionary);\n }\n\n return dictionaries;\n },\n afterBuild: async ({ dictionaries, configuration }) => {\n // Dynamic import to avoid circular dependency as core package import config, that load esbuild, that load the config file, that load the plugin\n const { getLocalizedContent } = await import('@intlayer/core');\n\n const locales = configuration.internationalization.locales;\n\n type RecordList = {\n key: string;\n dictionary: Dictionary;\n locale: Locale;\n };\n\n const recordList: RecordList[] = Object.entries(\n dictionaries.mergedDictionaries\n ).flatMap(([key, dictionary]) =>\n locales.map((locale) => ({\n key,\n dictionary: dictionary.dictionary as Dictionary,\n locale,\n }))\n );\n\n await parallelize(recordList, async ({ key, dictionary, locale }) => {\n const builderPath = options.source({\n key,\n locale,\n });\n\n // Remove function, Symbol, etc. as it can be written as JSON\n const flatContent = JSON.parse(JSON.stringify(dictionary.content));\n\n const localizedContent = getLocalizedContent(\n flatContent as unknown as ContentNode,\n locale,\n {\n dictionaryKey: key,\n keyPath: [],\n }\n );\n\n // Ensure directory exists before writing the file\n await mkdir(dirname(builderPath), { recursive: true });\n\n const stringContent = JSON.stringify(localizedContent, null, 2);\n\n await writeFile(\n builderPath,\n `${stringContent}\\n`, // Add a new line at the end of the file to avoid formatting issues with VSCode\n 'utf-8'\n );\n });\n },\n formatOutput: ({ dictionary }) => {\n if (!dictionary.filePath || !dictionary.locale) return dictionary;\n\n const builderPath = options.source({\n key: dictionary.key,\n locale: dictionary.locale,\n });\n\n // It's not one of the JSON that we synchronize, don't modify it\n if (resolve(builderPath) !== resolve(dictionary.filePath)) {\n return dictionary;\n }\n\n return dictionary.content;\n },\n };\n};\n"],"mappings":"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"}
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
3
|
import { parallelize } from "@intlayer/chokidar";
|
|
4
|
-
import {
|
|
4
|
+
import { getProjectRequire } from "@intlayer/config";
|
|
5
5
|
import fg from "fast-glob";
|
|
6
6
|
|
|
7
7
|
//#region src/index.ts
|
|
@@ -75,7 +75,7 @@ const syncJSON = (options) => {
|
|
|
75
75
|
if (fill && !isAbsolute(fill)) fill = join(configuration.content.baseDir, fill);
|
|
76
76
|
const dictionaries = [];
|
|
77
77
|
for (const { locale, path, key } of dictionariesMap) {
|
|
78
|
-
const json = (configuration.build?.require ??
|
|
78
|
+
const json = (configuration.build?.require ?? getProjectRequire())(path);
|
|
79
79
|
const filePath = relative(configuration.content.baseDir, path);
|
|
80
80
|
const dictionary = {
|
|
81
81
|
key,
|
package/dist/esm/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["locale: Locale | undefined","key: string | undefined","result: MessagesRecord","messages: MessagesRecord","dictionariesMap: DictionariesMap","fill: string","dictionaries: Dictionary[]","json: JSONContent","dictionary: Dictionary"],"sources":["../../src/index.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport { parallelize } from '@intlayer/chokidar';\nimport { ESMxCJSRequire } from '@intlayer/config';\nimport type {\n ContentNode,\n Dictionary,\n IntlayerConfig,\n LocalDictionaryId,\n Locale,\n LocalesValues,\n Plugin,\n} from '@intlayer/types';\nimport fg from 'fast-glob';\n\ntype JSONContent = Record<string, any>;\n\ntype Builder = ({\n key,\n locale,\n}: {\n key: string;\n locale: LocalesValues | (string & {});\n}) => string;\n\ntype MessagesRecord = Record<Locale, Record<Dictionary['key'], FilePath>>;\n\nconst escapeRegex = (str: string) => str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nconst extractKeyAndLocaleFromPath = (\n filePath: string,\n maskPattern: string,\n locales: Locale[]\n) => {\n const keyPlaceholder = '{{__KEY__}}';\n const localePlaceholder = '{{__LOCALE__}}';\n\n const escapedMask = escapeRegex(maskPattern);\n const localesAlternation = locales.join('|');\n\n // Build a regex from the mask to capture locale (and key if present)\n let regexStr = `^${escapedMask}$`;\n\n regexStr = regexStr.replace(\n escapeRegex(localePlaceholder),\n `(?<locale>${localesAlternation})`\n );\n\n if (maskPattern.includes(keyPlaceholder)) {\n regexStr = regexStr.replace(escapeRegex(keyPlaceholder), '(?<key>[^/]+)');\n }\n\n const maskRegex = new RegExp(regexStr);\n\n const match = maskRegex.exec(filePath);\n\n let locale: Locale | undefined;\n let key: string | undefined;\n\n if (match?.groups) {\n locale = match.groups.locale as Locale | undefined;\n key = (match.groups.key as string | undefined) ?? 'index';\n }\n\n return {\n key,\n locale,\n };\n};\n\nconst listMessages = (\n builder: Builder,\n configuration: IntlayerConfig\n): MessagesRecord => {\n const { content, internationalization } = configuration;\n\n const baseDir = content.baseDir;\n const locales = internationalization.locales as Locale[];\n\n const localePattern = `{${locales.map((locale) => locale).join(',')}}`;\n\n const globPattern = builder({ key: '*', locale: localePattern });\n const maskPattern = builder({ key: '{{__KEY__}}', locale: '{{__LOCALE__}}' });\n\n const files = fg.sync(globPattern, {\n cwd: baseDir,\n });\n\n const result: MessagesRecord = {} as MessagesRecord;\n\n for (const file of files) {\n const { key, locale } = extractKeyAndLocaleFromPath(\n file,\n maskPattern,\n locales\n );\n\n const absolutePath = isAbsolute(file) ? file : resolve(baseDir, file);\n\n if (!result[locale as Locale]) {\n result[locale as Locale] = {};\n }\n\n result[locale as Locale][key as Dictionary['key']] = absolutePath;\n }\n\n return result;\n};\n\ntype FilePath = string;\n\ntype DictionariesMap = { path: string; locale: Locale; key: string }[];\n\nconst loadMessagePathMap = (\n source: MessagesRecord | Builder,\n configuration: IntlayerConfig\n) => {\n const messages: MessagesRecord = listMessages(\n source as Builder,\n configuration\n );\n\n const dictionariesPathMap: DictionariesMap = Object.entries(messages).flatMap(\n ([locale, keysRecord]) =>\n Object.entries(keysRecord).map(([key, path]) => {\n const absolutePath = isAbsolute(path)\n ? path\n : resolve(configuration.content.baseDir, path);\n\n return {\n path: absolutePath,\n locale,\n key,\n } as DictionariesMap[number];\n })\n );\n\n return dictionariesPathMap;\n};\n\ntype SyncJSONPluginOptions = {\n /**\n * The source of the plugin.\n * Is a function to build the source from the key and locale.\n *\n * ```ts\n * syncJSON({\n * source: ({ key, locale }) => `./messages/${locale}/${key}.json`\n * })\n * ```\n */\n source: Builder;\n\n /**\n * Because Intlayer transform the JSON files into Dictionary, we need to identify the plugin in the dictionary.\n * Used to identify the plugin in the dictionary.\n *\n * In the case you have multiple plugins, you can use this to identify the plugin in the dictionary.\n *\n * ```ts\n * const config ={\n * plugins: [\n * syncJSON({\n * source: ({ key, locale }) => `./resources/${locale}/${key}.json`\n * location: 'plugin-i18next',\n * }),\n * syncJSON({\n * source: ({ key, locale }) => `./messages/${locale}/${key}.json`\n * location: 'plugin-next-intl',\n * }),\n * ]\n * }\n * ```\n */\n location?: string;\n\n /**\n * The priority of the dictionaries created by the plugin.\n *\n * In the case of conflicts with remote dictionaries, or .content files, the dictionary with the highest priority will override the other dictionaries.\n *\n * Default is -1. (.content file priority is 0)\n *\n */\n priority?: number;\n};\n\nexport const syncJSON = (options: SyncJSONPluginOptions): Plugin => {\n const { location, priority } = {\n location: 'plugin',\n priority: 0,\n ...options,\n };\n\n return {\n name: 'sync-json',\n\n loadDictionaries: async ({ configuration }) => {\n const dictionariesMap: DictionariesMap = loadMessagePathMap(\n options.source,\n configuration\n );\n\n let fill: string = options.source({\n key: '{{key}}',\n locale: '{{locale}}',\n });\n\n if (fill && !isAbsolute(fill)) {\n fill = join(configuration.content.baseDir, fill);\n }\n\n const dictionaries: Dictionary[] = [];\n\n for (const { locale, path, key } of dictionariesMap) {\n const requireFunction = configuration.build?.require ?? ESMxCJSRequire;\n const json: JSONContent = requireFunction(path as string);\n\n const filePath = relative(configuration.content.baseDir, path);\n\n const dictionary: Dictionary = {\n key,\n locale,\n fill,\n localId: `${key}::${location}::${filePath}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled:\n locale !== configuration.internationalization.defaultLocale\n ? true\n : undefined,\n content: json,\n filePath,\n priority,\n };\n\n dictionaries.push(dictionary);\n }\n\n return dictionaries;\n },\n afterBuild: async ({ dictionaries, configuration }) => {\n // Dynamic import to avoid circular dependency as core package import config, that load esbuild, that load the config file, that load the plugin\n const { getLocalizedContent } = await import('@intlayer/core');\n\n const locales = configuration.internationalization.locales;\n\n type RecordList = {\n key: string;\n dictionary: Dictionary;\n locale: Locale;\n };\n\n const recordList: RecordList[] = Object.entries(\n dictionaries.mergedDictionaries\n ).flatMap(([key, dictionary]) =>\n locales.map((locale) => ({\n key,\n dictionary: dictionary.dictionary as Dictionary,\n locale,\n }))\n );\n\n await parallelize(recordList, async ({ key, dictionary, locale }) => {\n const builderPath = options.source({\n key,\n locale,\n });\n\n // Remove function, Symbol, etc. as it can be written as JSON\n const flatContent = JSON.parse(JSON.stringify(dictionary.content));\n\n const localizedContent = getLocalizedContent(\n flatContent as unknown as ContentNode,\n locale,\n {\n dictionaryKey: key,\n keyPath: [],\n }\n );\n\n // Ensure directory exists before writing the file\n await mkdir(dirname(builderPath), { recursive: true });\n\n const stringContent = JSON.stringify(localizedContent, null, 2);\n\n await writeFile(\n builderPath,\n `${stringContent}\\n`, // Add a new line at the end of the file to avoid formatting issues with VSCode\n 'utf-8'\n );\n });\n },\n formatOutput: ({ dictionary }) => {\n if (!dictionary.filePath || !dictionary.locale) return dictionary;\n\n const builderPath = options.source({\n key: dictionary.key,\n locale: dictionary.locale,\n });\n\n // It's not one of the JSON that we synchronize, don't modify it\n if (resolve(builderPath) !== resolve(dictionary.filePath)) {\n return dictionary;\n }\n\n return dictionary.content;\n },\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["locale: Locale | undefined","key: string | undefined","result: MessagesRecord","messages: MessagesRecord","dictionariesMap: DictionariesMap","fill: string","dictionaries: Dictionary[]","json: JSONContent","dictionary: Dictionary"],"sources":["../../src/index.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport { parallelize } from '@intlayer/chokidar';\nimport { getProjectRequire } from '@intlayer/config';\nimport type {\n ContentNode,\n Dictionary,\n IntlayerConfig,\n LocalDictionaryId,\n Locale,\n LocalesValues,\n Plugin,\n} from '@intlayer/types';\nimport fg from 'fast-glob';\n\ntype JSONContent = Record<string, any>;\n\ntype Builder = ({\n key,\n locale,\n}: {\n key: string;\n locale: LocalesValues | (string & {});\n}) => string;\n\ntype MessagesRecord = Record<Locale, Record<Dictionary['key'], FilePath>>;\n\nconst escapeRegex = (str: string) => str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nconst extractKeyAndLocaleFromPath = (\n filePath: string,\n maskPattern: string,\n locales: Locale[]\n) => {\n const keyPlaceholder = '{{__KEY__}}';\n const localePlaceholder = '{{__LOCALE__}}';\n\n const escapedMask = escapeRegex(maskPattern);\n const localesAlternation = locales.join('|');\n\n // Build a regex from the mask to capture locale (and key if present)\n let regexStr = `^${escapedMask}$`;\n\n regexStr = regexStr.replace(\n escapeRegex(localePlaceholder),\n `(?<locale>${localesAlternation})`\n );\n\n if (maskPattern.includes(keyPlaceholder)) {\n regexStr = regexStr.replace(escapeRegex(keyPlaceholder), '(?<key>[^/]+)');\n }\n\n const maskRegex = new RegExp(regexStr);\n\n const match = maskRegex.exec(filePath);\n\n let locale: Locale | undefined;\n let key: string | undefined;\n\n if (match?.groups) {\n locale = match.groups.locale as Locale | undefined;\n key = (match.groups.key as string | undefined) ?? 'index';\n }\n\n return {\n key,\n locale,\n };\n};\n\nconst listMessages = (\n builder: Builder,\n configuration: IntlayerConfig\n): MessagesRecord => {\n const { content, internationalization } = configuration;\n\n const baseDir = content.baseDir;\n const locales = internationalization.locales as Locale[];\n\n const localePattern = `{${locales.map((locale) => locale).join(',')}}`;\n\n const globPattern = builder({ key: '*', locale: localePattern });\n const maskPattern = builder({ key: '{{__KEY__}}', locale: '{{__LOCALE__}}' });\n\n const files = fg.sync(globPattern, {\n cwd: baseDir,\n });\n\n const result: MessagesRecord = {} as MessagesRecord;\n\n for (const file of files) {\n const { key, locale } = extractKeyAndLocaleFromPath(\n file,\n maskPattern,\n locales\n );\n\n const absolutePath = isAbsolute(file) ? file : resolve(baseDir, file);\n\n if (!result[locale as Locale]) {\n result[locale as Locale] = {};\n }\n\n result[locale as Locale][key as Dictionary['key']] = absolutePath;\n }\n\n return result;\n};\n\ntype FilePath = string;\n\ntype DictionariesMap = { path: string; locale: Locale; key: string }[];\n\nconst loadMessagePathMap = (\n source: MessagesRecord | Builder,\n configuration: IntlayerConfig\n) => {\n const messages: MessagesRecord = listMessages(\n source as Builder,\n configuration\n );\n\n const dictionariesPathMap: DictionariesMap = Object.entries(messages).flatMap(\n ([locale, keysRecord]) =>\n Object.entries(keysRecord).map(([key, path]) => {\n const absolutePath = isAbsolute(path)\n ? path\n : resolve(configuration.content.baseDir, path);\n\n return {\n path: absolutePath,\n locale,\n key,\n } as DictionariesMap[number];\n })\n );\n\n return dictionariesPathMap;\n};\n\ntype SyncJSONPluginOptions = {\n /**\n * The source of the plugin.\n * Is a function to build the source from the key and locale.\n *\n * ```ts\n * syncJSON({\n * source: ({ key, locale }) => `./messages/${locale}/${key}.json`\n * })\n * ```\n */\n source: Builder;\n\n /**\n * Because Intlayer transform the JSON files into Dictionary, we need to identify the plugin in the dictionary.\n * Used to identify the plugin in the dictionary.\n *\n * In the case you have multiple plugins, you can use this to identify the plugin in the dictionary.\n *\n * ```ts\n * const config ={\n * plugins: [\n * syncJSON({\n * source: ({ key, locale }) => `./resources/${locale}/${key}.json`\n * location: 'plugin-i18next',\n * }),\n * syncJSON({\n * source: ({ key, locale }) => `./messages/${locale}/${key}.json`\n * location: 'plugin-next-intl',\n * }),\n * ]\n * }\n * ```\n */\n location?: string;\n\n /**\n * The priority of the dictionaries created by the plugin.\n *\n * In the case of conflicts with remote dictionaries, or .content files, the dictionary with the highest priority will override the other dictionaries.\n *\n * Default is -1. (.content file priority is 0)\n *\n */\n priority?: number;\n};\n\nexport const syncJSON = (options: SyncJSONPluginOptions): Plugin => {\n const { location, priority } = {\n location: 'plugin',\n priority: 0,\n ...options,\n };\n\n return {\n name: 'sync-json',\n\n loadDictionaries: async ({ configuration }) => {\n const dictionariesMap: DictionariesMap = loadMessagePathMap(\n options.source,\n configuration\n );\n\n let fill: string = options.source({\n key: '{{key}}',\n locale: '{{locale}}',\n });\n\n if (fill && !isAbsolute(fill)) {\n fill = join(configuration.content.baseDir, fill);\n }\n\n const dictionaries: Dictionary[] = [];\n\n for (const { locale, path, key } of dictionariesMap) {\n const requireFunction =\n configuration.build?.require ?? getProjectRequire();\n const json: JSONContent = requireFunction(path as string);\n\n const filePath = relative(configuration.content.baseDir, path);\n\n const dictionary: Dictionary = {\n key,\n locale,\n fill,\n localId: `${key}::${location}::${filePath}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled:\n locale !== configuration.internationalization.defaultLocale\n ? true\n : undefined,\n content: json,\n filePath,\n priority,\n };\n\n dictionaries.push(dictionary);\n }\n\n return dictionaries;\n },\n afterBuild: async ({ dictionaries, configuration }) => {\n // Dynamic import to avoid circular dependency as core package import config, that load esbuild, that load the config file, that load the plugin\n const { getLocalizedContent } = await import('@intlayer/core');\n\n const locales = configuration.internationalization.locales;\n\n type RecordList = {\n key: string;\n dictionary: Dictionary;\n locale: Locale;\n };\n\n const recordList: RecordList[] = Object.entries(\n dictionaries.mergedDictionaries\n ).flatMap(([key, dictionary]) =>\n locales.map((locale) => ({\n key,\n dictionary: dictionary.dictionary as Dictionary,\n locale,\n }))\n );\n\n await parallelize(recordList, async ({ key, dictionary, locale }) => {\n const builderPath = options.source({\n key,\n locale,\n });\n\n // Remove function, Symbol, etc. as it can be written as JSON\n const flatContent = JSON.parse(JSON.stringify(dictionary.content));\n\n const localizedContent = getLocalizedContent(\n flatContent as unknown as ContentNode,\n locale,\n {\n dictionaryKey: key,\n keyPath: [],\n }\n );\n\n // Ensure directory exists before writing the file\n await mkdir(dirname(builderPath), { recursive: true });\n\n const stringContent = JSON.stringify(localizedContent, null, 2);\n\n await writeFile(\n builderPath,\n `${stringContent}\\n`, // Add a new line at the end of the file to avoid formatting issues with VSCode\n 'utf-8'\n );\n });\n },\n formatOutput: ({ dictionary }) => {\n if (!dictionary.filePath || !dictionary.locale) return dictionary;\n\n const builderPath = options.source({\n key: dictionary.key,\n locale: dictionary.locale,\n });\n\n // It's not one of the JSON that we synchronize, don't modify it\n if (resolve(builderPath) !== resolve(dictionary.filePath)) {\n return dictionary;\n }\n\n return dictionary.content;\n },\n };\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intlayer/sync-json-plugin",
|
|
3
|
-
"version": "7.0.3
|
|
3
|
+
"version": "7.0.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A plugin for Intlayer that syncs JSON files to dictionaries.",
|
|
6
6
|
"keywords": [
|
|
@@ -68,30 +68,26 @@
|
|
|
68
68
|
"typecheck": "tsc --noEmit --project tsconfig.types.json"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@intlayer/chokidar": "7.0.3
|
|
72
|
-
"@intlayer/config": "7.0.3
|
|
73
|
-
"@intlayer/core": "7.0.3
|
|
71
|
+
"@intlayer/chokidar": "7.0.3",
|
|
72
|
+
"@intlayer/config": "7.0.3",
|
|
73
|
+
"@intlayer/core": "7.0.3",
|
|
74
|
+
"@intlayer/types": "7.0.3",
|
|
74
75
|
"fast-glob": "3.3.3"
|
|
75
76
|
},
|
|
76
77
|
"devDependencies": {
|
|
77
|
-
"@types/node": "24.9.
|
|
78
|
-
"@utils/ts-config": "7.0.3
|
|
79
|
-
"@utils/ts-config-types": "7.0.3
|
|
80
|
-
"@utils/tsdown-config": "7.0.3
|
|
78
|
+
"@types/node": "24.9.2",
|
|
79
|
+
"@utils/ts-config": "7.0.3",
|
|
80
|
+
"@utils/ts-config-types": "7.0.3",
|
|
81
|
+
"@utils/tsdown-config": "7.0.3",
|
|
81
82
|
"rimraf": "6.0.1",
|
|
82
|
-
"tsdown": "0.15.
|
|
83
|
+
"tsdown": "0.15.11",
|
|
83
84
|
"typescript": "5.9.3",
|
|
84
|
-
"vitest": "4.0.
|
|
85
|
+
"vitest": "4.0.5"
|
|
85
86
|
},
|
|
86
87
|
"peerDependencies": {
|
|
87
|
-
"@intlayer/config": "7.0.3
|
|
88
|
-
"@intlayer/core": "7.0.3
|
|
89
|
-
"intlayer": "7.0.3
|
|
90
|
-
},
|
|
91
|
-
"peerDependenciesMeta": {
|
|
92
|
-
"intlayer": {
|
|
93
|
-
"optional": true
|
|
94
|
-
}
|
|
88
|
+
"@intlayer/config": "7.0.3",
|
|
89
|
+
"@intlayer/core": "7.0.3",
|
|
90
|
+
"@intlayer/types": "7.0.3"
|
|
95
91
|
},
|
|
96
92
|
"engines": {
|
|
97
93
|
"node": ">=14.18"
|