@intlayer/sync-json-plugin 8.7.14 → 8.9.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/sync-json-plugin",
3
- "version": "8.7.14",
3
+ "version": "8.9.0",
4
4
  "private": false,
5
5
  "description": "A plugin for Intlayer that syncs JSON files to dictionaries.",
6
6
  "keywords": [
@@ -71,10 +71,10 @@
71
71
  "typecheck": "tsc --noEmit --project tsconfig.types.json"
72
72
  },
73
73
  "dependencies": {
74
- "@intlayer/chokidar": "8.7.14",
75
- "@intlayer/config": "8.7.14",
76
- "@intlayer/core": "8.7.14",
77
- "@intlayer/types": "8.7.14",
74
+ "@intlayer/chokidar": "8.9.0",
75
+ "@intlayer/config": "8.9.0",
76
+ "@intlayer/core": "8.9.0",
77
+ "@intlayer/types": "8.9.0",
78
78
  "fast-glob": "3.3.3"
79
79
  },
80
80
  "devDependencies": {
@@ -1,29 +0,0 @@
1
- //#region rolldown:runtime
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") {
10
- for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) {
13
- __defProp(to, key, {
14
- get: ((k) => from[k]).bind(null, key),
15
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
- });
17
- }
18
- }
19
- }
20
- return to;
21
- };
22
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
- value: mod,
24
- enumerable: true
25
- }) : target, mod));
26
-
27
- //#endregion
28
-
29
- exports.__toESM = __toESM;
@@ -1 +0,0 @@
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 { 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 // Ensure all declared locales are present even if the file doesn't exist yet\n // Derive the list of keys from discovered files; if no key placeholder in mask, default to 'index'\n const hasKeyInMask = maskPattern.includes('{{__KEY__}}');\n const discoveredKeys = new Set<string>();\n\n for (const locale of Object.keys(result)) {\n for (const key of Object.keys(result[locale as Locale] ?? {})) {\n discoveredKeys.add(key);\n }\n }\n\n if (!hasKeyInMask) {\n discoveredKeys.add('index');\n }\n\n // If no keys were discovered and mask expects a key, we cannot infer keys.\n // In that case, do not fabricate unknown keys.\n const keysToEnsure =\n discoveredKeys.size > 0 ? Array.from(discoveredKeys) : [];\n\n for (const locale of locales) {\n if (!result[locale]) {\n result[locale] = {} as Record<Dictionary['key'], FilePath>;\n }\n\n for (const key of keysToEnsure) {\n if (!result[locale][key as Dictionary['key']]) {\n const builtPath = builder({ key, locale });\n const absoluteBuiltPath = isAbsolute(builtPath)\n ? builtPath\n : resolve(baseDir, builtPath);\n\n result[locale][key as Dictionary['key']] = absoluteBuiltPath;\n }\n }\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 let json: JSONContent = {};\n try {\n json = requireFunction(path as string);\n } catch {\n // File does not exist yet; default to empty content so it can be filled later\n json = {};\n }\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\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 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 const { parallelize } = await import('@intlayer/chokidar');\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 // The file is empty, don't write it\n if (\n typeof localizedContent === 'undefined' ||\n (typeof localizedContent === 'object' &&\n Object.keys(localizedContent as Record<string, unknown>).length ===\n 0)\n )\n return;\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 };\n};\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`node:path`),l=require(`@intlayer/config/file`),u=require(`@intlayer/config/utils`),d=require(`fast-glob`);d=s(d);let f=require(`node:fs/promises`);const p=e=>e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),m=(e,t,n,r)=>{let i=`{{__KEY__}}`,a=p(t),o=n.join(`|`),s=`^${a}$`;s=s.replace(p(`{{__LOCALE__}}`),`(?<locale>${o})`),t.includes(i)&&(s=s.replace(p(i),`(?<key>.+)`));let c=new RegExp(s).exec(e);if(!c||!c.groups)return null;let l=c.groups.locale,u=c.groups.key??`index`;return u===void 0&&(u=`index`),l===void 0&&(l=r),{key:u,locale:l}},h=async(e,t)=>{let{system:n,internationalization:r}=t,{baseDir:i}=n,{locales:a}=r,o={};for(let t of a){let n=await(0,u.parseFilePathPattern)(e,{key:`**`,locale:t}),r=await(0,u.parseFilePathPattern)(e,{key:`{{__KEY__}}`,locale:t});if(!n||!r)continue;let s=await(0,d.default)(n,{cwd:i});for(let n of s){let s=m(n,r,a,t);if(!s)continue;let{key:l,locale:d}=s,f=await(0,u.parseFilePathPattern)(e,{key:l,locale:d}),p=(0,c.isAbsolute)(n)?n:(0,c.resolve)(i,n);if(p!==((0,c.isAbsolute)(f)?f:(0,c.resolve)(i,f)))continue;let h=d;o[h]||(o[h]={}),o[h][l]=p}}let s=new Set;for(let e of Object.keys(o))for(let t of Object.keys(o[e]??{}))s.add(t);let l=s.size>0?Array.from(s):[];for(let t of a){o[t]||(o[t]={});for(let n of l)if(!o[t][n]){let r=await(0,u.parseFilePathPattern)(e,{key:n,locale:t}),a=(0,c.isAbsolute)(r)?r:(0,c.resolve)(i,r);o[t][n]=a}}return o},g=async(e,t)=>{let n=await h(e,t);return Object.entries(n).flatMap(([e,n])=>Object.entries(n).map(([n,r])=>({path:(0,c.isAbsolute)(r)?r:(0,c.resolve)(t.system.baseDir,r),locale:e,key:n})))},_=async e=>{let{location:t,priority:n,format:r}={location:`sync-json::${await(0,u.parseFilePathPattern)(e.source,{key:`{{key}}`,locale:`{{locale}}`})}`,priority:0,...e};return{name:`sync-json`,loadDictionaries:async({configuration:i})=>{let a=await g(e.source,i),o=await(0,u.parseFilePathPattern)(e.source,{key:`{{key}}`,locale:`{{locale}}`});o&&!(0,c.isAbsolute)(o)&&(o=(0,c.join)(i.system.baseDir,o));let s=[];for(let{locale:e,path:u,key:d}of a){let a={};try{a=await(0,l.loadExternalFile)(u,{logError:!1})}catch{a={}}let f=(0,c.relative)(i.system.baseDir,u),p={key:d,locale:e,fill:o,format:r,localId:`${d}::${t}::${f}`,location:t,filled:e===i.internationalization.defaultLocale?void 0:!0,content:a,filePath:f,priority:n};s.push(p)}return s},formatOutput:async({dictionary:t})=>{let{formatDictionaryOutput:n}=await import(`@intlayer/chokidar/build`);return!t.filePath||!t.locale||(0,c.resolve)(await(0,u.parseFilePathPattern)(e.source,{key:t.key,locale:t.locale}))!==(0,c.resolve)(t.filePath)?t:n({...t,format:r}).content},afterBuild:async({dictionaries:n,configuration:i})=>{let{getPerLocaleDictionary:a}=await import(`@intlayer/core/plugins`),{parallelize:o}=await import(`@intlayer/chokidar/utils`),{formatDictionaryOutput:s}=await import(`@intlayer/chokidar/build`),{locales:l}=i.internationalization;await o(Object.entries(n.mergedDictionaries).flatMap(([e,t])=>l.map(n=>({key:e,dictionary:t.dictionary,locale:n}))),async({key:n,dictionary:i,locale:o})=>{if(i.location!==t)return;let l=await(0,u.parseFilePathPattern)(e.source,{key:n,locale:o}),d=s({...a(i,o),format:r}),p=JSON.parse(JSON.stringify(d.content));p===void 0||typeof p==`object`&&Object.keys(p).length===0||(await(0,f.mkdir)((0,c.dirname)(l),{recursive:!0}),await(0,f.writeFile)(l,`${JSON.stringify(p,null,2)}\n`,`utf-8`))})}}};Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return m}});
2
- //# sourceMappingURL=syncJSON-CTiXS5Pk.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"syncJSON-CTiXS5Pk.cjs","names":[],"sources":["../../src/syncJSON.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport { loadExternalFile } from '@intlayer/config/file';\nimport { parseFilePathPattern } from '@intlayer/config/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n DictionaryFormat,\n DictionaryLocation,\n LocalDictionaryId,\n} from '@intlayer/types/dictionary';\nimport type {\n FilePathPattern,\n FilePathPatternContext,\n} from '@intlayer/types/filePathPattern';\nimport type { Plugin } from '@intlayer/types/plugin';\nimport fg from 'fast-glob';\n\ntype JSONContent = Record<string, any>;\n\ntype FilePath = string;\n\ntype MessagesRecord = Record<Locale, Record<Dictionary['key'], FilePath>>;\n\nconst escapeRegex = (str: string) => str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nexport const extractKeyAndLocaleFromPath = (\n filePath: string,\n maskPattern: string,\n locales: Locale[],\n defaultLocale: Locale\n): { key: string; locale: Locale } | null => {\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 // FIX: Allow key to match multiple directory levels (e.g. \"nested/file\" or \"folder/index\")\n // Previous value: '(?<key>[^/]+)'\n regexStr = regexStr.replace(escapeRegex(keyPlaceholder), '(?<key>.+)');\n }\n\n const maskRegex = new RegExp(regexStr);\n const match = maskRegex.exec(filePath);\n\n if (!match || !match.groups) {\n return null;\n }\n\n let locale = match.groups.locale as Locale | undefined;\n let key = (match.groups.key as string | undefined) ?? 'index';\n\n if (typeof key === 'undefined') {\n key = 'index';\n }\n\n if (typeof locale === 'undefined') {\n locale = defaultLocale;\n }\n\n return {\n key,\n locale,\n };\n};\n\nconst listMessages = async (\n source: FilePathPattern,\n configuration: IntlayerConfig\n): Promise<MessagesRecord> => {\n const { system, internationalization } = configuration;\n\n const { baseDir } = system;\n const { locales } = internationalization;\n\n const result: MessagesRecord = {} as MessagesRecord;\n\n for (const locale of locales) {\n const globPatternLocale = await parseFilePathPattern(source, {\n key: '**',\n locale,\n } as any as FilePathPatternContext);\n\n const maskPatternLocale = await parseFilePathPattern(source, {\n key: '{{__KEY__}}',\n locale,\n } as any as FilePathPatternContext);\n\n if (!globPatternLocale || !maskPatternLocale) {\n continue;\n }\n\n const files = await fg(globPatternLocale, {\n cwd: baseDir,\n });\n\n for (const file of files) {\n const extraction = extractKeyAndLocaleFromPath(\n file,\n maskPatternLocale,\n locales,\n locale\n );\n\n if (!extraction) {\n continue;\n }\n\n const { key, locale: extractedLocale } = extraction;\n\n // Generate what the path SHOULD be for this key/locale using the current builder\n const expectedPath = await parseFilePathPattern(source, {\n key,\n locale: extractedLocale,\n } as any as FilePathPatternContext);\n\n // Resolve both to absolute paths to ensure safe comparison\n const absoluteFoundPath = isAbsolute(file)\n ? file\n : resolve(baseDir, file);\n const absoluteExpectedPath = isAbsolute(expectedPath)\n ? expectedPath\n : resolve(baseDir, expectedPath);\n\n // If the file found doesn't exactly match the file expected, it belongs to another plugin/structure\n if (absoluteFoundPath !== absoluteExpectedPath) {\n continue;\n }\n\n const usedLocale = extractedLocale as Locale;\n if (!result[usedLocale]) {\n result[usedLocale] = {};\n }\n\n result[usedLocale][key as Dictionary['key']] = absoluteFoundPath;\n }\n }\n\n // Ensure all declared locales are present even if the file doesn't exist yet\n const hasKeyInMask = true; // We check keys manually below\n const discoveredKeys = new Set<string>();\n\n for (const locale of Object.keys(result)) {\n for (const key of Object.keys(result[locale as Locale] ?? {})) {\n discoveredKeys.add(key);\n }\n }\n\n if (!hasKeyInMask) {\n discoveredKeys.add('index');\n }\n\n const keysToEnsure =\n discoveredKeys.size > 0 ? Array.from(discoveredKeys) : [];\n\n for (const locale of locales) {\n if (!result[locale]) {\n result[locale] = {} as Record<Dictionary['key'], FilePath>;\n }\n\n for (const key of keysToEnsure) {\n if (!result[locale][key as Dictionary['key']]) {\n const builtPath = await parseFilePathPattern(source, {\n key,\n locale,\n } as any as FilePathPatternContext);\n const absoluteBuiltPath = isAbsolute(builtPath)\n ? builtPath\n : resolve(baseDir, builtPath);\n\n result[locale][key as Dictionary['key']] = absoluteBuiltPath;\n }\n }\n }\n\n return result;\n};\n\ntype DictionariesMap = { path: string; locale: Locale; key: string }[];\n\nconst loadMessagePathMap = async (\n source: MessagesRecord | FilePathPattern,\n configuration: IntlayerConfig\n) => {\n const messages: MessagesRecord = await listMessages(\n source as FilePathPattern,\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.system.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: FilePathPattern;\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 * // Example usage:\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?: DictionaryLocation | (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 /**\n * The format of the dictionaries created by the plugin.\n *\n * Default: 'intlayer'\n *\n * The format of the dictionaries created by the plugin.\n */\n format?: DictionaryFormat;\n};\n\nexport const syncJSON = async (\n options: SyncJSONPluginOptions\n): Promise<Plugin> => {\n // Generate a unique default location based on the source pattern.\n // This ensures that if you have multiple plugins, they don't share the same 'plugin' ID.\n const patternMarker = await parseFilePathPattern(options.source, {\n key: '{{key}}',\n locale: '{{locale}}',\n } as any as FilePathPatternContext);\n const defaultLocation = `sync-json::${patternMarker}`;\n\n const { location, priority, format } = {\n location: defaultLocation,\n priority: 0,\n ...options,\n };\n\n return {\n name: 'sync-json',\n\n loadDictionaries: async ({ configuration }) => {\n const dictionariesMap: DictionariesMap = await loadMessagePathMap(\n options.source,\n configuration\n );\n\n let fill: string = await parseFilePathPattern(options.source, {\n key: '{{key}}',\n locale: '{{locale}}',\n } as any as FilePathPatternContext);\n\n if (fill && !isAbsolute(fill)) {\n fill = join(configuration.system.baseDir, fill);\n }\n\n const dictionaries: Dictionary[] = [];\n\n for (const { locale, path, key } of dictionariesMap) {\n let json: JSONContent = {};\n try {\n json = await loadExternalFile(path, { logError: false });\n } catch {\n json = {};\n }\n\n const filePath = relative(configuration.system.baseDir, path);\n\n const dictionary: Dictionary = {\n key,\n locale,\n fill,\n format,\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\n formatOutput: async ({ dictionary }) => {\n // Lazy import intlayer modules to avoid circular dependencies\n const { formatDictionaryOutput } = await import(\n '@intlayer/chokidar/build'\n );\n\n if (!dictionary.filePath || !dictionary.locale) return dictionary;\n\n const builderPath = await parseFilePathPattern(options.source, {\n key: dictionary.key,\n locale: dictionary.locale,\n } as any as FilePathPatternContext);\n\n // Verification to ensure we are formatting the correct file\n if (resolve(builderPath) !== resolve(dictionary.filePath)) {\n return dictionary;\n }\n\n const dictionaryWithFormat = {\n ...dictionary,\n format,\n };\n\n const formattedOutput = formatDictionaryOutput(\n dictionaryWithFormat as Dictionary\n );\n\n return formattedOutput.content;\n },\n\n afterBuild: async ({ dictionaries, configuration }) => {\n // Lazy import intlayer modules to avoid circular dependencies\n const { getPerLocaleDictionary } = await import('@intlayer/core/plugins');\n const { parallelize } = await import('@intlayer/chokidar/utils');\n const { formatDictionaryOutput } = await import(\n '@intlayer/chokidar/build'\n );\n\n const { locales } = configuration.internationalization;\n\n type RecordList = {\n key: string;\n dictionary: Dictionary;\n locale: Locale;\n };\n\n // We get all dictionaries, but we need to filter them\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 // Only process dictionaries that belong to THIS plugin instance.\n if (dictionary.location !== location) {\n return;\n }\n\n const builderPath = await parseFilePathPattern(options.source, {\n key,\n locale,\n } as any as FilePathPatternContext);\n\n const localizedDictionary = getPerLocaleDictionary(dictionary, locale);\n\n const dictionaryWithFormat = {\n ...localizedDictionary,\n format,\n };\n\n const formattedOutput = formatDictionaryOutput(dictionaryWithFormat);\n\n const content = JSON.parse(JSON.stringify(formattedOutput.content));\n\n if (\n typeof content === 'undefined' ||\n (typeof content === 'object' &&\n Object.keys(content as Record<string, unknown>).length === 0)\n ) {\n return;\n }\n\n await mkdir(dirname(builderPath), { recursive: true });\n\n const stringContent = JSON.stringify(content, null, 2);\n\n await writeFile(builderPath, `${stringContent}\\n`, 'utf-8');\n });\n },\n };\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
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 { 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 // Ensure all declared locales are present even if the file doesn't exist yet\n // Derive the list of keys from discovered files; if no key placeholder in mask, default to 'index'\n const hasKeyInMask = maskPattern.includes('{{__KEY__}}');\n const discoveredKeys = new Set<string>();\n\n for (const locale of Object.keys(result)) {\n for (const key of Object.keys(result[locale as Locale] ?? {})) {\n discoveredKeys.add(key);\n }\n }\n\n if (!hasKeyInMask) {\n discoveredKeys.add('index');\n }\n\n // If no keys were discovered and mask expects a key, we cannot infer keys.\n // In that case, do not fabricate unknown keys.\n const keysToEnsure =\n discoveredKeys.size > 0 ? Array.from(discoveredKeys) : [];\n\n for (const locale of locales) {\n if (!result[locale]) {\n result[locale] = {} as Record<Dictionary['key'], FilePath>;\n }\n\n for (const key of keysToEnsure) {\n if (!result[locale][key as Dictionary['key']]) {\n const builtPath = builder({ key, locale });\n const absoluteBuiltPath = isAbsolute(builtPath)\n ? builtPath\n : resolve(baseDir, builtPath);\n\n result[locale][key as Dictionary['key']] = absoluteBuiltPath;\n }\n }\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 let json: JSONContent = {};\n try {\n json = requireFunction(path as string);\n } catch {\n // File does not exist yet; default to empty content so it can be filled later\n json = {};\n }\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\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 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 const { parallelize } = await import('@intlayer/chokidar');\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 // The file is empty, don't write it\n if (\n typeof localizedContent === 'undefined' ||\n (typeof localizedContent === 'object' &&\n Object.keys(localizedContent as Record<string, unknown>).length ===\n 0)\n )\n return;\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 };\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/index.ts"],"sourcesContent":[],"mappings":";;;KAgBK,OAAA;;;;;EAAA,MAAA,EAKK,aALE,GAAA,CAAA,MAAA,GAAA,CAAA,CAAA,CAAA;CACV,EAAA,GAAA,MAAA;KA+JG,qBAAA,GA9JH;EAGQ;;AAAa;AA0MvB;;;;;;;UApCU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAoCG,oBAAqB,0BAAwB"}