@intlayer/sync-json-plugin 7.3.15 → 7.5.0-canary.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.
@@ -43,7 +43,7 @@ const loadMessagePathMap = (source, configuration, selectedLocale) => {
43
43
  }));
44
44
  };
45
45
  const loadJSON = (options) => {
46
- const { location, priority, locale } = {
46
+ const { location, priority, locale, format } = {
47
47
  location: "plugin",
48
48
  priority: 0,
49
49
  ...options
@@ -69,6 +69,7 @@ const loadJSON = (options) => {
69
69
  key,
70
70
  locale: usedLocale,
71
71
  fill: filePath$1,
72
+ format,
72
73
  localId: `${key}::${location}::${filePath$1}`,
73
74
  location,
74
75
  filled: usedLocale !== configuration.internationalization.defaultLocale ? true : void 0,
@@ -1 +1 @@
1
- {"version":3,"file":"loadJSON.cjs","names":["fg","result: MessagesRecord","extractKeyAndLocaleFromPath","messages: MessagesRecord","dictionariesMap: DictionariesMap","filePath: string","dictionaries: Dictionary[]","json: JSONContent","filePath","dictionary: Dictionary"],"sources":["../../src/loadJSON.ts"],"sourcesContent":["import { isAbsolute, join, relative, resolve } from 'node:path';\nimport { getProjectRequire } from '@intlayer/config';\nimport type {\n Dictionary,\n IntlayerConfig,\n LocalDictionaryId,\n Locale,\n Plugin,\n} from '@intlayer/types';\nimport fg from 'fast-glob';\nimport { extractKeyAndLocaleFromPath } from './syncJSON';\n\ntype JSONContent = Record<string, any>;\n\ntype Builder = ({\n key,\n locale,\n}: {\n key: string;\n locale?: Locale | (string & {});\n}) => string;\n\ntype MessagesRecord = Record<Locale, Record<Dictionary['key'], FilePath>>;\n\nconst listMessages = (\n builder: Builder,\n configuration: IntlayerConfig,\n selectedLocale: Locale\n): MessagesRecord => {\n const { content, internationalization } = configuration;\n\n const baseDir = content.baseDir;\n const locales = internationalization.locales;\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 selectedLocale\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 // For the load plugin we only use actual discovered files; do not fabricate\n // missing locales or keys, since we don't write outputs.\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 selectedLocale: Locale\n) => {\n const builder = source as Builder;\n const messages: MessagesRecord = listMessages(\n builder,\n configuration,\n selectedLocale\n );\n\n const maskPattern = builder({\n key: '{{__KEY__}}',\n locale: '{{__LOCALE__}}',\n });\n const hasLocaleInMask = maskPattern.includes('{{__LOCALE__}}');\n\n const entries = (\n hasLocaleInMask && selectedLocale\n ? Object.entries(messages).filter(([locale]) => locale === selectedLocale)\n : Object.entries(messages)\n ) as [Locale, Record<Dictionary['key'], FilePath>][];\n\n const dictionariesPathMap: DictionariesMap = entries.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 LoadJSONPluginOptions = {\n /**\n * The source of the plugin.\n * Is a function to build the source from the key and locale.\n *\n * @example\n * ```ts\n * loadJSON({\n * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,\n * })\n * ```\n */\n source: Builder;\n\n /**\n * Locale\n *\n * If not provided, the plugin will consider the default locale.\n *\n * @example\n * ```ts\n * loadJSON({\n * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,\n * locale: Locales.ENGLISH,\n * })\n * ```\n */\n locale?: Locale;\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 * @example\n * ```ts\n * const config = {\n * plugins: [\n * loadJSON({\n * source: ({ key }) => `./resources/${key}.json`,\n * location: 'plugin-i18next',\n * }),\n * loadJSON({\n * source: ({ key }) => `./messages/${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 loadJSON = (options: LoadJSONPluginOptions): Plugin => {\n const { location, priority, locale } = {\n location: 'plugin',\n priority: 0,\n ...options,\n };\n\n return {\n name: 'load-json',\n\n loadDictionaries: async ({ configuration }) => {\n const usedLocale = (locale ??\n configuration.internationalization.defaultLocale) as Locale;\n\n const dictionariesMap: DictionariesMap = loadMessagePathMap(\n options.source,\n configuration,\n usedLocale\n );\n\n let filePath: string = options.source({\n key: '{{key}}',\n });\n\n if (filePath && !isAbsolute(filePath)) {\n filePath = join(configuration.content.baseDir, filePath);\n }\n\n const dictionaries: Dictionary[] = [];\n\n for (const { 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: usedLocale,\n fill: filePath,\n localId: `${key}::${location}::${filePath}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled:\n usedLocale !== 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};\n"],"mappings":";;;;;;;;AAwBA,MAAM,gBACJ,SACA,eACA,mBACmB;CACnB,MAAM,EAAE,SAAS,yBAAyB;CAE1C,MAAM,UAAU,QAAQ;CACxB,MAAM,UAAU,qBAAqB;CAIrC,MAAM,cAAc,QAAQ;EAAE,KAAK;EAAK,QAFlB,IAAI,QAAQ,KAAK,WAAW,OAAO,CAAC,KAAK,IAAI,CAAC;EAEL,CAAC;CAChE,MAAM,cAAc,QAAQ;EAAE,KAAK;EAAe,QAAQ;EAAkB,CAAC;CAE7E,MAAM,QAAQA,kBAAG,KAAK,aAAa,EACjC,KAAK,SACN,CAAC;CAEF,MAAMC,SAAyB,EAAE;AAEjC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,EAAE,KAAK,WAAWC,6CACtB,MACA,aACA,SACA,eACD;EAED,MAAM,yCAA0B,KAAK,GAAG,8BAAe,SAAS,KAAK;AAErE,MAAI,CAAC,OAAO,QACV,QAAO,UAAoB,EAAE;AAG/B,SAAO,QAAkB,OAA4B;;AAKvD,QAAO;;AAOT,MAAM,sBACJ,QACA,eACA,mBACG;CACH,MAAM,UAAU;CAChB,MAAMC,WAA2B,aAC/B,SACA,eACA,eACD;AA6BD,SA3BoB,QAAQ;EAC1B,KAAK;EACL,QAAQ;EACT,CAAC,CACkC,SAAS,iBAAiB,IAGzC,iBACf,OAAO,QAAQ,SAAS,CAAC,QAAQ,CAAC,YAAY,WAAW,eAAe,GACxE,OAAO,QAAQ,SAAS,EAGuB,SAClD,CAAC,QAAQ,gBACR,OAAO,QAAQ,WAAW,CAAC,KAAK,CAAC,KAAK,UAAU;AAK9C,SAAO;GACL,gCAL8B,KAAK,GACjC,8BACQ,cAAc,QAAQ,SAAS,KAAK;GAI9C;GACA;GACD;GACD,CACL;;AAqEH,MAAa,YAAY,YAA2C;CAClE,MAAM,EAAE,UAAU,UAAU,WAAW;EACrC,UAAU;EACV,UAAU;EACV,GAAG;EACJ;AAED,QAAO;EACL,MAAM;EAEN,kBAAkB,OAAO,EAAE,oBAAoB;GAC7C,MAAM,aAAc,UAClB,cAAc,qBAAqB;GAErC,MAAMC,kBAAmC,mBACvC,QAAQ,QACR,eACA,WACD;GAED,IAAIC,WAAmB,QAAQ,OAAO,EACpC,KAAK,WACN,CAAC;AAEF,OAAI,YAAY,2BAAY,SAAS,CACnC,gCAAgB,cAAc,QAAQ,SAAS,SAAS;GAG1D,MAAMC,eAA6B,EAAE;AAErC,QAAK,MAAM,EAAE,MAAM,SAAS,iBAAiB;IAC3C,MAAM,kBACJ,cAAc,OAAO,qDAA8B;IACrD,IAAIC,OAAoB,EAAE;AAC1B,QAAI;AACF,YAAO,gBAAgB,KAAe;YAChC;AAEN,YAAO,EAAE;;IAGX,MAAMC,qCAAoB,cAAc,QAAQ,SAAS,KAAK;IAE9D,MAAMC,aAAyB;KAC7B;KACA,QAAQ;KACR,MAAMD;KACN,SAAS,GAAG,IAAI,IAAI,SAAS,IAAIA;KACvB;KACV,QACE,eAAe,cAAc,qBAAqB,gBAC9C,OACA;KACN,SAAS;KACT;KACA;KACD;AAED,iBAAa,KAAK,WAAW;;AAG/B,UAAO;;EAEV"}
1
+ {"version":3,"file":"loadJSON.cjs","names":["fg","result: MessagesRecord","extractKeyAndLocaleFromPath","messages: MessagesRecord","dictionariesMap: DictionariesMap","filePath: string","dictionaries: Dictionary[]","json: JSONContent","filePath","dictionary: Dictionary"],"sources":["../../src/loadJSON.ts"],"sourcesContent":["import { isAbsolute, join, relative, resolve } from 'node:path';\nimport { getProjectRequire } from '@intlayer/config';\nimport type {\n Dictionary,\n DictionaryFormat,\n IntlayerConfig,\n LocalDictionaryId,\n Locale,\n Plugin,\n} from '@intlayer/types';\nimport fg from 'fast-glob';\nimport { extractKeyAndLocaleFromPath } from './syncJSON';\n\ntype JSONContent = Record<string, any>;\n\ntype Builder = ({\n key,\n locale,\n}: {\n key: string;\n locale?: Locale | (string & {});\n}) => string;\n\ntype MessagesRecord = Record<Locale, Record<Dictionary['key'], FilePath>>;\n\nconst listMessages = (\n builder: Builder,\n configuration: IntlayerConfig,\n selectedLocale: Locale\n): MessagesRecord => {\n const { content, internationalization } = configuration;\n\n const baseDir = content.baseDir;\n const locales = internationalization.locales;\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 selectedLocale\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 // For the load plugin we only use actual discovered files; do not fabricate\n // missing locales or keys, since we don't write outputs.\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 selectedLocale: Locale\n) => {\n const builder = source as Builder;\n const messages: MessagesRecord = listMessages(\n builder,\n configuration,\n selectedLocale\n );\n\n const maskPattern = builder({\n key: '{{__KEY__}}',\n locale: '{{__LOCALE__}}',\n });\n const hasLocaleInMask = maskPattern.includes('{{__LOCALE__}}');\n\n const entries = (\n hasLocaleInMask && selectedLocale\n ? Object.entries(messages).filter(([locale]) => locale === selectedLocale)\n : Object.entries(messages)\n ) as [Locale, Record<Dictionary['key'], FilePath>][];\n\n const dictionariesPathMap: DictionariesMap = entries.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 LoadJSONPluginOptions = {\n /**\n * The source of the plugin.\n * Is a function to build the source from the key and locale.\n *\n * @example\n * ```ts\n * loadJSON({\n * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,\n * })\n * ```\n */\n source: Builder;\n\n /**\n * Locale\n *\n * If not provided, the plugin will consider the default locale.\n *\n * @example\n * ```ts\n * loadJSON({\n * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,\n * locale: Locales.ENGLISH,\n * })\n * ```\n */\n locale?: Locale;\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 * @example\n * ```ts\n * const config = {\n * plugins: [\n * loadJSON({\n * source: ({ key }) => `./resources/${key}.json`,\n * location: 'plugin-i18next',\n * }),\n * loadJSON({\n * source: ({ key }) => `./messages/${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 /**\n * The format of the dictionary content.\n *\n * @example\n * ```ts\n * loadJSON({\n * format: 'icu',\n * })\n * ```\n */\n format?: DictionaryFormat;\n};\n\nexport const loadJSON = (options: LoadJSONPluginOptions): Plugin => {\n const { location, priority, locale, format } = {\n location: 'plugin',\n priority: 0,\n ...options,\n } as const;\n\n return {\n name: 'load-json',\n\n loadDictionaries: async ({ configuration }) => {\n const usedLocale = (locale ??\n configuration.internationalization.defaultLocale) as Locale;\n\n const dictionariesMap: DictionariesMap = loadMessagePathMap(\n options.source,\n configuration,\n usedLocale\n );\n\n let filePath: string = options.source({\n key: '{{key}}',\n });\n\n if (filePath && !isAbsolute(filePath)) {\n filePath = join(configuration.content.baseDir, filePath);\n }\n\n const dictionaries: Dictionary[] = [];\n\n for (const { 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: usedLocale,\n fill: filePath,\n format,\n localId: `${key}::${location}::${filePath}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled:\n usedLocale !== 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};\n"],"mappings":";;;;;;;;AAyBA,MAAM,gBACJ,SACA,eACA,mBACmB;CACnB,MAAM,EAAE,SAAS,yBAAyB;CAE1C,MAAM,UAAU,QAAQ;CACxB,MAAM,UAAU,qBAAqB;CAIrC,MAAM,cAAc,QAAQ;EAAE,KAAK;EAAK,QAFlB,IAAI,QAAQ,KAAK,WAAW,OAAO,CAAC,KAAK,IAAI,CAAC;EAEL,CAAC;CAChE,MAAM,cAAc,QAAQ;EAAE,KAAK;EAAe,QAAQ;EAAkB,CAAC;CAE7E,MAAM,QAAQA,kBAAG,KAAK,aAAa,EACjC,KAAK,SACN,CAAC;CAEF,MAAMC,SAAyB,EAAE;AAEjC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,EAAE,KAAK,WAAWC,6CACtB,MACA,aACA,SACA,eACD;EAED,MAAM,yCAA0B,KAAK,GAAG,8BAAe,SAAS,KAAK;AAErE,MAAI,CAAC,OAAO,QACV,QAAO,UAAoB,EAAE;AAG/B,SAAO,QAAkB,OAA4B;;AAKvD,QAAO;;AAOT,MAAM,sBACJ,QACA,eACA,mBACG;CACH,MAAM,UAAU;CAChB,MAAMC,WAA2B,aAC/B,SACA,eACA,eACD;AA6BD,SA3BoB,QAAQ;EAC1B,KAAK;EACL,QAAQ;EACT,CAAC,CACkC,SAAS,iBAAiB,IAGzC,iBACf,OAAO,QAAQ,SAAS,CAAC,QAAQ,CAAC,YAAY,WAAW,eAAe,GACxE,OAAO,QAAQ,SAAS,EAGuB,SAClD,CAAC,QAAQ,gBACR,OAAO,QAAQ,WAAW,CAAC,KAAK,CAAC,KAAK,UAAU;AAK9C,SAAO;GACL,gCAL8B,KAAK,GACjC,8BACQ,cAAc,QAAQ,SAAS,KAAK;GAI9C;GACA;GACD;GACD,CACL;;AAiFH,MAAa,YAAY,YAA2C;CAClE,MAAM,EAAE,UAAU,UAAU,QAAQ,WAAW;EAC7C,UAAU;EACV,UAAU;EACV,GAAG;EACJ;AAED,QAAO;EACL,MAAM;EAEN,kBAAkB,OAAO,EAAE,oBAAoB;GAC7C,MAAM,aAAc,UAClB,cAAc,qBAAqB;GAErC,MAAMC,kBAAmC,mBACvC,QAAQ,QACR,eACA,WACD;GAED,IAAIC,WAAmB,QAAQ,OAAO,EACpC,KAAK,WACN,CAAC;AAEF,OAAI,YAAY,2BAAY,SAAS,CACnC,gCAAgB,cAAc,QAAQ,SAAS,SAAS;GAG1D,MAAMC,eAA6B,EAAE;AAErC,QAAK,MAAM,EAAE,MAAM,SAAS,iBAAiB;IAC3C,MAAM,kBACJ,cAAc,OAAO,qDAA8B;IACrD,IAAIC,OAAoB,EAAE;AAC1B,QAAI;AACF,YAAO,gBAAgB,KAAe;YAChC;AAEN,YAAO,EAAE;;IAGX,MAAMC,qCAAoB,cAAc,QAAQ,SAAS,KAAK;IAE9D,MAAMC,aAAyB;KAC7B;KACA,QAAQ;KACR,MAAMD;KACN;KACA,SAAS,GAAG,IAAI,IAAI,SAAS,IAAIA;KACvB;KACV,QACE,eAAe,cAAc,qBAAqB,gBAC9C,OACA;KACN,SAAS;KACT;KACA;KACD;AAED,iBAAa,KAAK,WAAW;;AAG/B,UAAO;;EAEV"}
@@ -4,6 +4,7 @@ let __intlayer_config = require("@intlayer/config");
4
4
  let fast_glob = require("fast-glob");
5
5
  fast_glob = require_rolldown_runtime.__toESM(fast_glob);
6
6
  let node_fs_promises = require("node:fs/promises");
7
+ let __intlayer_chokidar = require("@intlayer/chokidar");
7
8
 
8
9
  //#region src/syncJSON.ts
9
10
  const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -79,7 +80,7 @@ const loadMessagePathMap = (source, configuration) => {
79
80
  }));
80
81
  };
81
82
  const syncJSON = (options) => {
82
- const { location, priority } = {
83
+ const { location, priority, format } = {
83
84
  location: "plugin",
84
85
  priority: 0,
85
86
  ...options
@@ -107,6 +108,7 @@ const syncJSON = (options) => {
107
108
  key,
108
109
  locale,
109
110
  fill,
111
+ format,
110
112
  localId: `${key}::${location}::${filePath}`,
111
113
  location,
112
114
  filled: locale !== configuration.internationalization.defaultLocale ? true : void 0,
@@ -127,7 +129,7 @@ const syncJSON = (options) => {
127
129
  return dictionary.content;
128
130
  },
129
131
  afterBuild: async ({ dictionaries, configuration }) => {
130
- const { getLocalizedContent } = await import("@intlayer/core");
132
+ const { getPerLocaleDictionary } = await import("@intlayer/core");
131
133
  const { parallelize } = await import("@intlayer/chokidar");
132
134
  const locales = configuration.internationalization.locales;
133
135
  await parallelize(Object.entries(dictionaries.mergedDictionaries).flatMap(([key, dictionary]) => locales.map((locale) => ({
@@ -139,13 +141,14 @@ const syncJSON = (options) => {
139
141
  key,
140
142
  locale
141
143
  });
142
- const localizedContent = getLocalizedContent(JSON.parse(JSON.stringify(dictionary.content)), locale, {
143
- dictionaryKey: key,
144
- keyPath: []
144
+ const formattedOutput = (0, __intlayer_chokidar.formatDictionaryOutput)({
145
+ ...getPerLocaleDictionary(dictionary, locale),
146
+ format
145
147
  });
146
- if (typeof localizedContent === "undefined" || typeof localizedContent === "object" && Object.keys(localizedContent).length === 0) return;
148
+ const content = JSON.parse(JSON.stringify(formattedOutput.content));
149
+ if (typeof content === "undefined" || typeof content === "object" && Object.keys(content).length === 0) return;
147
150
  await (0, node_fs_promises.mkdir)((0, node_path.dirname)(builderPath), { recursive: true });
148
- await (0, node_fs_promises.writeFile)(builderPath, `${JSON.stringify(localizedContent, null, 2)}\n`, "utf-8");
151
+ await (0, node_fs_promises.writeFile)(builderPath, `${JSON.stringify(content, null, 2)}\n`, "utf-8");
149
152
  });
150
153
  }
151
154
  };
@@ -1 +1 @@
1
- {"version":3,"file":"syncJSON.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/syncJSON.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\nexport const extractKeyAndLocaleFromPath = (\n filePath: string,\n maskPattern: string,\n locales: Locale[],\n defaultLocale: 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 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 = (\n builder: Builder,\n configuration: IntlayerConfig\n): MessagesRecord => {\n const { content, internationalization } = configuration;\n\n const baseDir = content.baseDir;\n const locales = internationalization.locales;\n const defaultLocale = internationalization.defaultLocale;\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 defaultLocale\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
+ {"version":3,"file":"syncJSON.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/syncJSON.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport { formatDictionaryOutput } from '@intlayer/chokidar';\nimport { getProjectRequire } from '@intlayer/config';\nimport type {\n Dictionary,\n DictionaryFormat,\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\nexport const extractKeyAndLocaleFromPath = (\n filePath: string,\n maskPattern: string,\n locales: Locale[],\n defaultLocale: 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 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 = (\n builder: Builder,\n configuration: IntlayerConfig\n): MessagesRecord => {\n const { content, internationalization } = configuration;\n\n const baseDir = content.baseDir;\n const locales = internationalization.locales;\n const defaultLocale = internationalization.defaultLocale;\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 defaultLocale\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 /**\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 = (options: SyncJSONPluginOptions): Plugin => {\n const { location, priority, format } = {\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 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: ({ 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 { getPerLocaleDictionary } = 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 const localizedDictionary = getPerLocaleDictionary(dictionary, locale);\n\n // Restore the original format from plugin options for output formatting\n const dictionaryWithFormat = {\n ...localizedDictionary,\n format,\n };\n\n const formattedOutput = formatDictionaryOutput(dictionaryWithFormat);\n\n // Remove function, Symbol, etc. as it can be written as JSON\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 // Ensure directory exists before writing the file\n await mkdir(dirname(builderPath), { recursive: true });\n\n const stringContent = JSON.stringify(content, 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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"}
@@ -41,7 +41,7 @@ const loadMessagePathMap = (source, configuration, selectedLocale) => {
41
41
  }));
42
42
  };
43
43
  const loadJSON = (options) => {
44
- const { location, priority, locale } = {
44
+ const { location, priority, locale, format } = {
45
45
  location: "plugin",
46
46
  priority: 0,
47
47
  ...options
@@ -67,6 +67,7 @@ const loadJSON = (options) => {
67
67
  key,
68
68
  locale: usedLocale,
69
69
  fill: filePath$1,
70
+ format,
70
71
  localId: `${key}::${location}::${filePath$1}`,
71
72
  location,
72
73
  filled: usedLocale !== configuration.internationalization.defaultLocale ? true : void 0,
@@ -1 +1 @@
1
- {"version":3,"file":"loadJSON.mjs","names":["result: MessagesRecord","messages: MessagesRecord","dictionariesMap: DictionariesMap","filePath: string","dictionaries: Dictionary[]","json: JSONContent","filePath","dictionary: Dictionary"],"sources":["../../src/loadJSON.ts"],"sourcesContent":["import { isAbsolute, join, relative, resolve } from 'node:path';\nimport { getProjectRequire } from '@intlayer/config';\nimport type {\n Dictionary,\n IntlayerConfig,\n LocalDictionaryId,\n Locale,\n Plugin,\n} from '@intlayer/types';\nimport fg from 'fast-glob';\nimport { extractKeyAndLocaleFromPath } from './syncJSON';\n\ntype JSONContent = Record<string, any>;\n\ntype Builder = ({\n key,\n locale,\n}: {\n key: string;\n locale?: Locale | (string & {});\n}) => string;\n\ntype MessagesRecord = Record<Locale, Record<Dictionary['key'], FilePath>>;\n\nconst listMessages = (\n builder: Builder,\n configuration: IntlayerConfig,\n selectedLocale: Locale\n): MessagesRecord => {\n const { content, internationalization } = configuration;\n\n const baseDir = content.baseDir;\n const locales = internationalization.locales;\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 selectedLocale\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 // For the load plugin we only use actual discovered files; do not fabricate\n // missing locales or keys, since we don't write outputs.\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 selectedLocale: Locale\n) => {\n const builder = source as Builder;\n const messages: MessagesRecord = listMessages(\n builder,\n configuration,\n selectedLocale\n );\n\n const maskPattern = builder({\n key: '{{__KEY__}}',\n locale: '{{__LOCALE__}}',\n });\n const hasLocaleInMask = maskPattern.includes('{{__LOCALE__}}');\n\n const entries = (\n hasLocaleInMask && selectedLocale\n ? Object.entries(messages).filter(([locale]) => locale === selectedLocale)\n : Object.entries(messages)\n ) as [Locale, Record<Dictionary['key'], FilePath>][];\n\n const dictionariesPathMap: DictionariesMap = entries.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 LoadJSONPluginOptions = {\n /**\n * The source of the plugin.\n * Is a function to build the source from the key and locale.\n *\n * @example\n * ```ts\n * loadJSON({\n * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,\n * })\n * ```\n */\n source: Builder;\n\n /**\n * Locale\n *\n * If not provided, the plugin will consider the default locale.\n *\n * @example\n * ```ts\n * loadJSON({\n * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,\n * locale: Locales.ENGLISH,\n * })\n * ```\n */\n locale?: Locale;\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 * @example\n * ```ts\n * const config = {\n * plugins: [\n * loadJSON({\n * source: ({ key }) => `./resources/${key}.json`,\n * location: 'plugin-i18next',\n * }),\n * loadJSON({\n * source: ({ key }) => `./messages/${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 loadJSON = (options: LoadJSONPluginOptions): Plugin => {\n const { location, priority, locale } = {\n location: 'plugin',\n priority: 0,\n ...options,\n };\n\n return {\n name: 'load-json',\n\n loadDictionaries: async ({ configuration }) => {\n const usedLocale = (locale ??\n configuration.internationalization.defaultLocale) as Locale;\n\n const dictionariesMap: DictionariesMap = loadMessagePathMap(\n options.source,\n configuration,\n usedLocale\n );\n\n let filePath: string = options.source({\n key: '{{key}}',\n });\n\n if (filePath && !isAbsolute(filePath)) {\n filePath = join(configuration.content.baseDir, filePath);\n }\n\n const dictionaries: Dictionary[] = [];\n\n for (const { 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: usedLocale,\n fill: filePath,\n localId: `${key}::${location}::${filePath}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled:\n usedLocale !== 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};\n"],"mappings":";;;;;;AAwBA,MAAM,gBACJ,SACA,eACA,mBACmB;CACnB,MAAM,EAAE,SAAS,yBAAyB;CAE1C,MAAM,UAAU,QAAQ;CACxB,MAAM,UAAU,qBAAqB;CAIrC,MAAM,cAAc,QAAQ;EAAE,KAAK;EAAK,QAFlB,IAAI,QAAQ,KAAK,WAAW,OAAO,CAAC,KAAK,IAAI,CAAC;EAEL,CAAC;CAChE,MAAM,cAAc,QAAQ;EAAE,KAAK;EAAe,QAAQ;EAAkB,CAAC;CAE7E,MAAM,QAAQ,GAAG,KAAK,aAAa,EACjC,KAAK,SACN,CAAC;CAEF,MAAMA,SAAyB,EAAE;AAEjC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,EAAE,KAAK,WAAW,4BACtB,MACA,aACA,SACA,eACD;EAED,MAAM,eAAe,WAAW,KAAK,GAAG,OAAO,QAAQ,SAAS,KAAK;AAErE,MAAI,CAAC,OAAO,QACV,QAAO,UAAoB,EAAE;AAG/B,SAAO,QAAkB,OAA4B;;AAKvD,QAAO;;AAOT,MAAM,sBACJ,QACA,eACA,mBACG;CACH,MAAM,UAAU;CAChB,MAAMC,WAA2B,aAC/B,SACA,eACA,eACD;AA6BD,SA3BoB,QAAQ;EAC1B,KAAK;EACL,QAAQ;EACT,CAAC,CACkC,SAAS,iBAAiB,IAGzC,iBACf,OAAO,QAAQ,SAAS,CAAC,QAAQ,CAAC,YAAY,WAAW,eAAe,GACxE,OAAO,QAAQ,SAAS,EAGuB,SAClD,CAAC,QAAQ,gBACR,OAAO,QAAQ,WAAW,CAAC,KAAK,CAAC,KAAK,UAAU;AAK9C,SAAO;GACL,MALmB,WAAW,KAAK,GACjC,OACA,QAAQ,cAAc,QAAQ,SAAS,KAAK;GAI9C;GACA;GACD;GACD,CACL;;AAqEH,MAAa,YAAY,YAA2C;CAClE,MAAM,EAAE,UAAU,UAAU,WAAW;EACrC,UAAU;EACV,UAAU;EACV,GAAG;EACJ;AAED,QAAO;EACL,MAAM;EAEN,kBAAkB,OAAO,EAAE,oBAAoB;GAC7C,MAAM,aAAc,UAClB,cAAc,qBAAqB;GAErC,MAAMC,kBAAmC,mBACvC,QAAQ,QACR,eACA,WACD;GAED,IAAIC,WAAmB,QAAQ,OAAO,EACpC,KAAK,WACN,CAAC;AAEF,OAAI,YAAY,CAAC,WAAW,SAAS,CACnC,YAAW,KAAK,cAAc,QAAQ,SAAS,SAAS;GAG1D,MAAMC,eAA6B,EAAE;AAErC,QAAK,MAAM,EAAE,MAAM,SAAS,iBAAiB;IAC3C,MAAM,kBACJ,cAAc,OAAO,WAAW,mBAAmB;IACrD,IAAIC,OAAoB,EAAE;AAC1B,QAAI;AACF,YAAO,gBAAgB,KAAe;YAChC;AAEN,YAAO,EAAE;;IAGX,MAAMC,aAAW,SAAS,cAAc,QAAQ,SAAS,KAAK;IAE9D,MAAMC,aAAyB;KAC7B;KACA,QAAQ;KACR,MAAMD;KACN,SAAS,GAAG,IAAI,IAAI,SAAS,IAAIA;KACvB;KACV,QACE,eAAe,cAAc,qBAAqB,gBAC9C,OACA;KACN,SAAS;KACT;KACA;KACD;AAED,iBAAa,KAAK,WAAW;;AAG/B,UAAO;;EAEV"}
1
+ {"version":3,"file":"loadJSON.mjs","names":["result: MessagesRecord","messages: MessagesRecord","dictionariesMap: DictionariesMap","filePath: string","dictionaries: Dictionary[]","json: JSONContent","filePath","dictionary: Dictionary"],"sources":["../../src/loadJSON.ts"],"sourcesContent":["import { isAbsolute, join, relative, resolve } from 'node:path';\nimport { getProjectRequire } from '@intlayer/config';\nimport type {\n Dictionary,\n DictionaryFormat,\n IntlayerConfig,\n LocalDictionaryId,\n Locale,\n Plugin,\n} from '@intlayer/types';\nimport fg from 'fast-glob';\nimport { extractKeyAndLocaleFromPath } from './syncJSON';\n\ntype JSONContent = Record<string, any>;\n\ntype Builder = ({\n key,\n locale,\n}: {\n key: string;\n locale?: Locale | (string & {});\n}) => string;\n\ntype MessagesRecord = Record<Locale, Record<Dictionary['key'], FilePath>>;\n\nconst listMessages = (\n builder: Builder,\n configuration: IntlayerConfig,\n selectedLocale: Locale\n): MessagesRecord => {\n const { content, internationalization } = configuration;\n\n const baseDir = content.baseDir;\n const locales = internationalization.locales;\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 selectedLocale\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 // For the load plugin we only use actual discovered files; do not fabricate\n // missing locales or keys, since we don't write outputs.\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 selectedLocale: Locale\n) => {\n const builder = source as Builder;\n const messages: MessagesRecord = listMessages(\n builder,\n configuration,\n selectedLocale\n );\n\n const maskPattern = builder({\n key: '{{__KEY__}}',\n locale: '{{__LOCALE__}}',\n });\n const hasLocaleInMask = maskPattern.includes('{{__LOCALE__}}');\n\n const entries = (\n hasLocaleInMask && selectedLocale\n ? Object.entries(messages).filter(([locale]) => locale === selectedLocale)\n : Object.entries(messages)\n ) as [Locale, Record<Dictionary['key'], FilePath>][];\n\n const dictionariesPathMap: DictionariesMap = entries.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 LoadJSONPluginOptions = {\n /**\n * The source of the plugin.\n * Is a function to build the source from the key and locale.\n *\n * @example\n * ```ts\n * loadJSON({\n * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,\n * })\n * ```\n */\n source: Builder;\n\n /**\n * Locale\n *\n * If not provided, the plugin will consider the default locale.\n *\n * @example\n * ```ts\n * loadJSON({\n * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,\n * locale: Locales.ENGLISH,\n * })\n * ```\n */\n locale?: Locale;\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 * @example\n * ```ts\n * const config = {\n * plugins: [\n * loadJSON({\n * source: ({ key }) => `./resources/${key}.json`,\n * location: 'plugin-i18next',\n * }),\n * loadJSON({\n * source: ({ key }) => `./messages/${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 /**\n * The format of the dictionary content.\n *\n * @example\n * ```ts\n * loadJSON({\n * format: 'icu',\n * })\n * ```\n */\n format?: DictionaryFormat;\n};\n\nexport const loadJSON = (options: LoadJSONPluginOptions): Plugin => {\n const { location, priority, locale, format } = {\n location: 'plugin',\n priority: 0,\n ...options,\n } as const;\n\n return {\n name: 'load-json',\n\n loadDictionaries: async ({ configuration }) => {\n const usedLocale = (locale ??\n configuration.internationalization.defaultLocale) as Locale;\n\n const dictionariesMap: DictionariesMap = loadMessagePathMap(\n options.source,\n configuration,\n usedLocale\n );\n\n let filePath: string = options.source({\n key: '{{key}}',\n });\n\n if (filePath && !isAbsolute(filePath)) {\n filePath = join(configuration.content.baseDir, filePath);\n }\n\n const dictionaries: Dictionary[] = [];\n\n for (const { 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: usedLocale,\n fill: filePath,\n format,\n localId: `${key}::${location}::${filePath}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled:\n usedLocale !== 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};\n"],"mappings":";;;;;;AAyBA,MAAM,gBACJ,SACA,eACA,mBACmB;CACnB,MAAM,EAAE,SAAS,yBAAyB;CAE1C,MAAM,UAAU,QAAQ;CACxB,MAAM,UAAU,qBAAqB;CAIrC,MAAM,cAAc,QAAQ;EAAE,KAAK;EAAK,QAFlB,IAAI,QAAQ,KAAK,WAAW,OAAO,CAAC,KAAK,IAAI,CAAC;EAEL,CAAC;CAChE,MAAM,cAAc,QAAQ;EAAE,KAAK;EAAe,QAAQ;EAAkB,CAAC;CAE7E,MAAM,QAAQ,GAAG,KAAK,aAAa,EACjC,KAAK,SACN,CAAC;CAEF,MAAMA,SAAyB,EAAE;AAEjC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,EAAE,KAAK,WAAW,4BACtB,MACA,aACA,SACA,eACD;EAED,MAAM,eAAe,WAAW,KAAK,GAAG,OAAO,QAAQ,SAAS,KAAK;AAErE,MAAI,CAAC,OAAO,QACV,QAAO,UAAoB,EAAE;AAG/B,SAAO,QAAkB,OAA4B;;AAKvD,QAAO;;AAOT,MAAM,sBACJ,QACA,eACA,mBACG;CACH,MAAM,UAAU;CAChB,MAAMC,WAA2B,aAC/B,SACA,eACA,eACD;AA6BD,SA3BoB,QAAQ;EAC1B,KAAK;EACL,QAAQ;EACT,CAAC,CACkC,SAAS,iBAAiB,IAGzC,iBACf,OAAO,QAAQ,SAAS,CAAC,QAAQ,CAAC,YAAY,WAAW,eAAe,GACxE,OAAO,QAAQ,SAAS,EAGuB,SAClD,CAAC,QAAQ,gBACR,OAAO,QAAQ,WAAW,CAAC,KAAK,CAAC,KAAK,UAAU;AAK9C,SAAO;GACL,MALmB,WAAW,KAAK,GACjC,OACA,QAAQ,cAAc,QAAQ,SAAS,KAAK;GAI9C;GACA;GACD;GACD,CACL;;AAiFH,MAAa,YAAY,YAA2C;CAClE,MAAM,EAAE,UAAU,UAAU,QAAQ,WAAW;EAC7C,UAAU;EACV,UAAU;EACV,GAAG;EACJ;AAED,QAAO;EACL,MAAM;EAEN,kBAAkB,OAAO,EAAE,oBAAoB;GAC7C,MAAM,aAAc,UAClB,cAAc,qBAAqB;GAErC,MAAMC,kBAAmC,mBACvC,QAAQ,QACR,eACA,WACD;GAED,IAAIC,WAAmB,QAAQ,OAAO,EACpC,KAAK,WACN,CAAC;AAEF,OAAI,YAAY,CAAC,WAAW,SAAS,CACnC,YAAW,KAAK,cAAc,QAAQ,SAAS,SAAS;GAG1D,MAAMC,eAA6B,EAAE;AAErC,QAAK,MAAM,EAAE,MAAM,SAAS,iBAAiB;IAC3C,MAAM,kBACJ,cAAc,OAAO,WAAW,mBAAmB;IACrD,IAAIC,OAAoB,EAAE;AAC1B,QAAI;AACF,YAAO,gBAAgB,KAAe;YAChC;AAEN,YAAO,EAAE;;IAGX,MAAMC,aAAW,SAAS,cAAc,QAAQ,SAAS,KAAK;IAE9D,MAAMC,aAAyB;KAC7B;KACA,QAAQ;KACR,MAAMD;KACN;KACA,SAAS,GAAG,IAAI,IAAI,SAAS,IAAIA;KACvB;KACV,QACE,eAAe,cAAc,qBAAqB,gBAC9C,OACA;KACN,SAAS;KACT;KACA;KACD;AAED,iBAAa,KAAK,WAAW;;AAG/B,UAAO;;EAEV"}
@@ -2,6 +2,7 @@ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
2
2
  import { getProjectRequire } from "@intlayer/config";
3
3
  import fg from "fast-glob";
4
4
  import { mkdir, writeFile } from "node:fs/promises";
5
+ import { formatDictionaryOutput } from "@intlayer/chokidar";
5
6
 
6
7
  //#region src/syncJSON.ts
7
8
  const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -77,7 +78,7 @@ const loadMessagePathMap = (source, configuration) => {
77
78
  }));
78
79
  };
79
80
  const syncJSON = (options) => {
80
- const { location, priority } = {
81
+ const { location, priority, format } = {
81
82
  location: "plugin",
82
83
  priority: 0,
83
84
  ...options
@@ -105,6 +106,7 @@ const syncJSON = (options) => {
105
106
  key,
106
107
  locale,
107
108
  fill,
109
+ format,
108
110
  localId: `${key}::${location}::${filePath}`,
109
111
  location,
110
112
  filled: locale !== configuration.internationalization.defaultLocale ? true : void 0,
@@ -125,7 +127,7 @@ const syncJSON = (options) => {
125
127
  return dictionary.content;
126
128
  },
127
129
  afterBuild: async ({ dictionaries, configuration }) => {
128
- const { getLocalizedContent } = await import("@intlayer/core");
130
+ const { getPerLocaleDictionary } = await import("@intlayer/core");
129
131
  const { parallelize } = await import("@intlayer/chokidar");
130
132
  const locales = configuration.internationalization.locales;
131
133
  await parallelize(Object.entries(dictionaries.mergedDictionaries).flatMap(([key, dictionary]) => locales.map((locale) => ({
@@ -137,13 +139,14 @@ const syncJSON = (options) => {
137
139
  key,
138
140
  locale
139
141
  });
140
- const localizedContent = getLocalizedContent(JSON.parse(JSON.stringify(dictionary.content)), locale, {
141
- dictionaryKey: key,
142
- keyPath: []
142
+ const formattedOutput = formatDictionaryOutput({
143
+ ...getPerLocaleDictionary(dictionary, locale),
144
+ format
143
145
  });
144
- if (typeof localizedContent === "undefined" || typeof localizedContent === "object" && Object.keys(localizedContent).length === 0) return;
146
+ const content = JSON.parse(JSON.stringify(formattedOutput.content));
147
+ if (typeof content === "undefined" || typeof content === "object" && Object.keys(content).length === 0) return;
145
148
  await mkdir(dirname(builderPath), { recursive: true });
146
- await writeFile(builderPath, `${JSON.stringify(localizedContent, null, 2)}\n`, "utf-8");
149
+ await writeFile(builderPath, `${JSON.stringify(content, null, 2)}\n`, "utf-8");
147
150
  });
148
151
  }
149
152
  };
@@ -1 +1 @@
1
- {"version":3,"file":"syncJSON.mjs","names":["locale: Locale | undefined","key: string | undefined","result: MessagesRecord","messages: MessagesRecord","dictionariesMap: DictionariesMap","fill: string","dictionaries: Dictionary[]","json: JSONContent","dictionary: Dictionary"],"sources":["../../src/syncJSON.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\nexport const extractKeyAndLocaleFromPath = (\n filePath: string,\n maskPattern: string,\n locales: Locale[],\n defaultLocale: 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 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 = (\n builder: Builder,\n configuration: IntlayerConfig\n): MessagesRecord => {\n const { content, internationalization } = configuration;\n\n const baseDir = content.baseDir;\n const locales = internationalization.locales;\n const defaultLocale = internationalization.defaultLocale;\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 defaultLocale\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
+ {"version":3,"file":"syncJSON.mjs","names":["locale: Locale | undefined","key: string | undefined","result: MessagesRecord","messages: MessagesRecord","dictionariesMap: DictionariesMap","fill: string","dictionaries: Dictionary[]","json: JSONContent","dictionary: Dictionary"],"sources":["../../src/syncJSON.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport { formatDictionaryOutput } from '@intlayer/chokidar';\nimport { getProjectRequire } from '@intlayer/config';\nimport type {\n Dictionary,\n DictionaryFormat,\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\nexport const extractKeyAndLocaleFromPath = (\n filePath: string,\n maskPattern: string,\n locales: Locale[],\n defaultLocale: 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 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 = (\n builder: Builder,\n configuration: IntlayerConfig\n): MessagesRecord => {\n const { content, internationalization } = configuration;\n\n const baseDir = content.baseDir;\n const locales = internationalization.locales;\n const defaultLocale = internationalization.defaultLocale;\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 defaultLocale\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 /**\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 = (options: SyncJSONPluginOptions): Plugin => {\n const { location, priority, format } = {\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 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: ({ 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 { getPerLocaleDictionary } = 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 const localizedDictionary = getPerLocaleDictionary(dictionary, locale);\n\n // Restore the original format from plugin options for output formatting\n const dictionaryWithFormat = {\n ...localizedDictionary,\n format,\n };\n\n const formattedOutput = formatDictionaryOutput(dictionaryWithFormat);\n\n // Remove function, Symbol, etc. as it can be written as JSON\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 // Ensure directory exists before writing the file\n await mkdir(dirname(builderPath), { recursive: true });\n\n const stringContent = JSON.stringify(content, 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,4 +1,4 @@
1
- import { Locale, Plugin } from "@intlayer/types";
1
+ import { DictionaryFormat, Locale, Plugin } from "@intlayer/types";
2
2
 
3
3
  //#region src/loadJSON.d.ts
4
4
  type Builder = ({
@@ -67,6 +67,17 @@ type LoadJSONPluginOptions = {
67
67
  *
68
68
  */
69
69
  priority?: number;
70
+ /**
71
+ * The format of the dictionary content.
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * loadJSON({
76
+ * format: 'icu',
77
+ * })
78
+ * ```
79
+ */
80
+ format?: DictionaryFormat;
70
81
  };
71
82
  declare const loadJSON: (options: LoadJSONPluginOptions) => Plugin;
72
83
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"loadJSON.d.ts","names":[],"sources":["../../src/loadJSON.ts"],"sourcesContent":[],"mappings":";;;KAcK,OAAA;;;;;EAAA,MAAA,CAAA,EAKM,MALC,GAAA,CAAA,MAAA,GAAA,CAAA,CAAA,CAAA;CACV,EAAA,GAAA,MAAA;KAkGG,qBAAA,GAjGH;EAGS;;AAAM;AA8JjB;;;;;;;;UApDU;;;;;;;;;;;;;;WAeC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAqCE,oBAAqB,0BAAwB"}
1
+ {"version":3,"file":"loadJSON.d.ts","names":[],"sources":["../../src/loadJSON.ts"],"sourcesContent":[],"mappings":";;;KAeK,OAAA;;;;;EAAA,MAAA,CAAA,EAKM,MALC,GAAA,CAAA,MAAA,GAAA,CAAA,CAAA,CAAA;CACV,EAAA,GAAA,MAAA;KAkGG,qBAAA,GAjGH;EAGS;;AAAM;;;;;AA0KjB;;;;UAhEU;;;;;;;;;;;;;;WAeC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA8CA;;cAGE,oBAAqB,0BAAwB"}
@@ -1,4 +1,4 @@
1
- import { Locale, LocalesValues, Plugin } from "@intlayer/types";
1
+ import { DictionaryFormat, Locale, LocalesValues, Plugin } from "@intlayer/types";
2
2
 
3
3
  //#region src/syncJSON.d.ts
4
4
  type Builder = ({
@@ -55,6 +55,14 @@ type SyncJSONPluginOptions = {
55
55
  *
56
56
  */
57
57
  priority?: number;
58
+ /**
59
+ * The format of the dictionaries created by the plugin.
60
+ *
61
+ * Default: 'intlayer'
62
+ *
63
+ * The format of the dictionaries created by the plugin.
64
+ */
65
+ format?: DictionaryFormat;
58
66
  };
59
67
  declare const syncJSON: (options: SyncJSONPluginOptions) => Plugin;
60
68
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"syncJSON.d.ts","names":[],"sources":["../../src/syncJSON.ts"],"sourcesContent":[],"mappings":";;;KAgBK,OAAA;;;;;EAAA,MAAA,EAKK,aALE,GAAA,CAAA,MAAA,GAAA,CAAA,CAAA,CAAA;CACV,EAAA,GAAA,MAAA;AACA,cAUW,2BAVX,EAAA,CAAA,QAAA,EAAA,MAAA,EAAA,WAAA,EAAA,MAAA,EAAA,OAAA,EAaS,MAbT,EAAA,EAAA,aAAA,EAce,MAdf,EAAA,GAAA;EAGQ,GAAA,EAAA,MAAA;EAAa,MAAA,QAAA;AAOvB,CAAA;KA+JK,qBAAA,GA5JM;EACM;;;AA4Cf;AA8JF;;;;;;UApCU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAoCG,oBAAqB,0BAAwB"}
1
+ {"version":3,"file":"syncJSON.d.ts","names":[],"sources":["../../src/syncJSON.ts"],"sourcesContent":[],"mappings":";;;KAiBK,OAAA;;;;;EAAA,MAAA,EAKK,aALE,GAAA,CAAA,MAAA,GAAA,CAAA,CAAA,CAAA;CACV,EAAA,GAAA,MAAA;AACA,cAUW,2BAVX,EAAA,CAAA,QAAA,EAAA,MAAA,EAAA,WAAA,EAAA,MAAA,EAAA,OAAA,EAaS,MAbT,EAAA,EAAA,aAAA,EAce,MAdf,EAAA,GAAA;EAGQ,GAAA,EAAA,MAAA;EAAa,MAAA,QAAA;AAOvB,CAAA;KA+JK,qBAAA,GA5JM;EACM;;;AA4Cf;AAuKF;;;;;;UA7CU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA0CC;;cAGE,oBAAqB,0BAAwB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/sync-json-plugin",
3
- "version": "7.3.15",
3
+ "version": "7.5.0-canary.0",
4
4
  "private": false,
5
5
  "description": "A plugin for Intlayer that syncs JSON files to dictionaries.",
6
6
  "keywords": [
@@ -72,10 +72,10 @@
72
72
  "typecheck": "tsc --noEmit --project tsconfig.types.json"
73
73
  },
74
74
  "dependencies": {
75
- "@intlayer/chokidar": "7.3.15",
76
- "@intlayer/config": "7.3.15",
77
- "@intlayer/core": "7.3.15",
78
- "@intlayer/types": "7.3.15",
75
+ "@intlayer/chokidar": "7.5.0-canary.0",
76
+ "@intlayer/config": "7.5.0-canary.0",
77
+ "@intlayer/core": "7.5.0-canary.0",
78
+ "@intlayer/types": "7.5.0-canary.0",
79
79
  "fast-glob": "3.3.3"
80
80
  },
81
81
  "devDependencies": {