@intlayer/chokidar 8.9.4 → 8.9.6-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.
- package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs +6 -2
- package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs.map +1 -1
- package/dist/cjs/buildIntlayerDictionary/writeFetchDictionary.cjs +4 -2
- package/dist/cjs/buildIntlayerDictionary/writeFetchDictionary.cjs.map +1 -1
- package/dist/cjs/buildIntlayerDictionary/writeMergedDictionary.cjs +2 -0
- package/dist/cjs/buildIntlayerDictionary/writeMergedDictionary.cjs.map +1 -1
- package/dist/cjs/buildIntlayerDictionary/writeUnmergedDictionary.cjs +2 -0
- package/dist/cjs/buildIntlayerDictionary/writeUnmergedDictionary.cjs.map +1 -1
- package/dist/cjs/createType/createModuleAugmentation.cjs +17 -6
- package/dist/cjs/createType/createModuleAugmentation.cjs.map +1 -1
- package/dist/cjs/installSkills/index.cjs +5 -2
- package/dist/cjs/installSkills/index.cjs.map +1 -1
- package/dist/cjs/watcher.cjs +54 -0
- package/dist/cjs/watcher.cjs.map +1 -1
- package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs +6 -2
- package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs.map +1 -1
- package/dist/esm/buildIntlayerDictionary/writeFetchDictionary.mjs +5 -3
- package/dist/esm/buildIntlayerDictionary/writeFetchDictionary.mjs.map +1 -1
- package/dist/esm/buildIntlayerDictionary/writeMergedDictionary.mjs +2 -0
- package/dist/esm/buildIntlayerDictionary/writeMergedDictionary.mjs.map +1 -1
- package/dist/esm/buildIntlayerDictionary/writeUnmergedDictionary.mjs +2 -0
- package/dist/esm/buildIntlayerDictionary/writeUnmergedDictionary.mjs.map +1 -1
- package/dist/esm/createType/createModuleAugmentation.mjs +17 -6
- package/dist/esm/createType/createModuleAugmentation.mjs.map +1 -1
- package/dist/esm/installSkills/index.mjs +5 -2
- package/dist/esm/installSkills/index.mjs.map +1 -1
- package/dist/esm/watcher.mjs +55 -1
- package/dist/esm/watcher.mjs.map +1 -1
- package/dist/types/buildIntlayerDictionary/writeDynamicDictionary.d.ts.map +1 -1
- package/dist/types/buildIntlayerDictionary/writeMergedDictionary.d.ts.map +1 -1
- package/dist/types/buildIntlayerDictionary/writeUnmergedDictionary.d.ts.map +1 -1
- package/dist/types/createType/createModuleAugmentation.d.ts.map +1 -1
- package/dist/types/installSkills/index.d.ts.map +1 -1
- package/dist/types/watcher.d.ts.map +1 -1
- package/package.json +10 -10
|
@@ -6,6 +6,7 @@ const require_writeJsonIfChanged = require('../writeJsonIfChanged.cjs');
|
|
|
6
6
|
let node_fs_promises = require("node:fs/promises");
|
|
7
7
|
let node_path = require("node:path");
|
|
8
8
|
let _intlayer_config_logger = require("@intlayer/config/logger");
|
|
9
|
+
let _intlayer_config_utils = require("@intlayer/config/utils");
|
|
9
10
|
let _intlayer_config_defaultValues = require("@intlayer/config/defaultValues");
|
|
10
11
|
let _intlayer_core_plugins = require("@intlayer/core/plugins");
|
|
11
12
|
|
|
@@ -45,6 +46,7 @@ const writeDynamicDictionary = async (mergedDictionaries, configuration, formats
|
|
|
45
46
|
if (key === "undefined") return;
|
|
46
47
|
const localizedDictionariesPathsRecord = {};
|
|
47
48
|
const keyDir = (0, node_path.resolve)(dictDir, key);
|
|
49
|
+
(0, _intlayer_config_utils.assertPathWithin)(keyDir, dictDir);
|
|
48
50
|
await (0, node_fs_promises.mkdir)(keyDir, { recursive: true });
|
|
49
51
|
await require_utils_parallelize.parallelize(locales, async (locale) => {
|
|
50
52
|
const localizedDictionary = (0, _intlayer_core_plugins.getPerLocaleDictionary)(dictionaryEntry.dictionary, locale, defaultLocale);
|
|
@@ -61,8 +63,10 @@ const writeDynamicDictionary = async (mergedDictionaries, configuration, formats
|
|
|
61
63
|
await require_utils_parallelize.parallelize(formats, async (format) => {
|
|
62
64
|
const extension = format === "cjs" ? "cjs" : "mjs";
|
|
63
65
|
const content = generateDictionaryEntryPoint(key, locales, format);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
const dynEntryPath = (0, node_path.resolve)(dynamicDictionariesDir, `${key}.${extension}`);
|
|
67
|
+
(0, _intlayer_config_utils.assertPathWithin)(dynEntryPath, dynamicDictionariesDir);
|
|
68
|
+
await require_writeFileIfChanged.writeFileIfChanged(dynEntryPath, content).catch((err) => {
|
|
69
|
+
console.error(`Error creating dynamic ${(0, _intlayer_config_logger.colorizePath)(dynEntryPath)}:`, err);
|
|
66
70
|
});
|
|
67
71
|
});
|
|
68
72
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeDynamicDictionary.cjs","names":["OUTPUT_FORMAT","parallelize","writeJsonIfChanged","writeFileIfChanged"],"sources":["../../../src/buildIntlayerDictionary/writeDynamicDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { OUTPUT_FORMAT } from '@intlayer/config/defaultValues';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { getPerLocaleDictionary } from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\nimport type { MergedDictionaryOutput } from './writeMergedDictionary';\n\nexport type DictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary;\n};\n\nexport type LocalizedDictionaryResult = Partial<\n Record<Locale, DictionaryResult>\n>;\n\nexport type LocalizedDictionaryOutput = Record<\n string,\n LocalizedDictionaryResult\n>;\n\nconst DICTIONARIES_SUBDIR = 'json'; // Necessary to add a static first dir for Turbopack\n\n/**\n * Generates the content of a dictionary entry point file.\n */\nexport const generateDictionaryEntryPoint = (\n key: string,\n locales: string[],\n format: 'cjs' | 'esm' = 'esm'\n): string => {\n const sortedLocales = [...locales].sort((a, b) =>\n String(a).localeCompare(String(b))\n );\n\n const localeEntries = sortedLocales\n .map((locale) =>\n format === 'esm'\n ? ` '${locale}': () => import('./${DICTIONARIES_SUBDIR}/${key}/${locale}.json').then(m => m.default)`\n : ` '${locale}': () => Promise.resolve(require('./${DICTIONARIES_SUBDIR}/${key}/${locale}.json'))`\n )\n .join(',\\n');\n\n if (format === 'esm') {\n return (\n `const content = {\\n${localeEntries}\\n};\\n\\n` +\n `export default content;\\n`\n );\n }\n return `module.exports = {\\n${localeEntries}\\n};\\n`;\n};\n\n/**\n * Write the localized dictionaries to the dictionariesDir\n * @param mergedDictionaries - The merged dictionaries\n * @param configuration - The configuration\n * @returns The final dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/dynamic_dictionary/dictionaries/en_home.json\n * // .intlayer/dynamic_dictionary/dictionaries/fr_home.json\n * ```\n */\nexport const writeDynamicDictionary = async (\n mergedDictionaries: MergedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT\n): Promise<LocalizedDictionaryOutput> => {\n const { locales, defaultLocale } = configuration.internationalization;\n const { dynamicDictionariesDir } = configuration.system;\n\n const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);\n await mkdir(dictDir, { recursive: true });\n\n const resultDictionariesPaths: LocalizedDictionaryOutput = {};\n\n // Merge dictionaries with the same key and write to dictionariesDir\n await parallelize(\n Object.entries(mergedDictionaries).sort(([a], [b]) =>\n String(a).localeCompare(String(b))\n ),\n async ([key, dictionaryEntry]) => {\n if (key === 'undefined') return;\n\n const localizedDictionariesPathsRecord: LocalizedDictionaryResult = {};\n\n const keyDir = resolve(dictDir, key);\n await mkdir(keyDir, { recursive: true });\n\n await parallelize(locales, async (locale) => {\n const localizedDictionary = getPerLocaleDictionary(\n dictionaryEntry.dictionary,\n locale,\n defaultLocale\n );\n\n // Directory structure: json/key/locale.json\n const resultFilePath = resolve(keyDir, `${locale}.json`);\n\n await writeJsonIfChanged(resultFilePath, localizedDictionary).catch(\n (err) => {\n console.error(\n `Error creating localized ${key}/${locale}.json:`,\n err\n );\n }\n );\n\n localizedDictionariesPathsRecord[locale] = {\n dictionaryPath: resultFilePath,\n dictionary: localizedDictionary,\n };\n });\n\n resultDictionariesPaths[key] = localizedDictionariesPathsRecord;\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateDictionaryEntryPoint(key, locales, format);\n\n
|
|
1
|
+
{"version":3,"file":"writeDynamicDictionary.cjs","names":["OUTPUT_FORMAT","parallelize","writeJsonIfChanged","writeFileIfChanged"],"sources":["../../../src/buildIntlayerDictionary/writeDynamicDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { OUTPUT_FORMAT } from '@intlayer/config/defaultValues';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport { getPerLocaleDictionary } from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\nimport type { MergedDictionaryOutput } from './writeMergedDictionary';\n\nexport type DictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary;\n};\n\nexport type LocalizedDictionaryResult = Partial<\n Record<Locale, DictionaryResult>\n>;\n\nexport type LocalizedDictionaryOutput = Record<\n string,\n LocalizedDictionaryResult\n>;\n\nconst DICTIONARIES_SUBDIR = 'json'; // Necessary to add a static first dir for Turbopack\n\n/**\n * Generates the content of a dictionary entry point file.\n */\nexport const generateDictionaryEntryPoint = (\n key: string,\n locales: string[],\n format: 'cjs' | 'esm' = 'esm'\n): string => {\n const sortedLocales = [...locales].sort((a, b) =>\n String(a).localeCompare(String(b))\n );\n\n const localeEntries = sortedLocales\n .map((locale) =>\n format === 'esm'\n ? ` '${locale}': () => import('./${DICTIONARIES_SUBDIR}/${key}/${locale}.json').then(m => m.default)`\n : ` '${locale}': () => Promise.resolve(require('./${DICTIONARIES_SUBDIR}/${key}/${locale}.json'))`\n )\n .join(',\\n');\n\n if (format === 'esm') {\n return (\n `const content = {\\n${localeEntries}\\n};\\n\\n` +\n `export default content;\\n`\n );\n }\n return `module.exports = {\\n${localeEntries}\\n};\\n`;\n};\n\n/**\n * Write the localized dictionaries to the dictionariesDir\n * @param mergedDictionaries - The merged dictionaries\n * @param configuration - The configuration\n * @returns The final dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/dynamic_dictionary/dictionaries/en_home.json\n * // .intlayer/dynamic_dictionary/dictionaries/fr_home.json\n * ```\n */\nexport const writeDynamicDictionary = async (\n mergedDictionaries: MergedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT\n): Promise<LocalizedDictionaryOutput> => {\n const { locales, defaultLocale } = configuration.internationalization;\n const { dynamicDictionariesDir } = configuration.system;\n\n const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);\n await mkdir(dictDir, { recursive: true });\n\n const resultDictionariesPaths: LocalizedDictionaryOutput = {};\n\n // Merge dictionaries with the same key and write to dictionariesDir\n await parallelize(\n Object.entries(mergedDictionaries).sort(([a], [b]) =>\n String(a).localeCompare(String(b))\n ),\n async ([key, dictionaryEntry]) => {\n if (key === 'undefined') return;\n\n const localizedDictionariesPathsRecord: LocalizedDictionaryResult = {};\n\n const keyDir = resolve(dictDir, key);\n assertPathWithin(keyDir, dictDir);\n await mkdir(keyDir, { recursive: true });\n\n await parallelize(locales, async (locale) => {\n const localizedDictionary = getPerLocaleDictionary(\n dictionaryEntry.dictionary,\n locale,\n defaultLocale\n );\n\n // Directory structure: json/key/locale.json\n const resultFilePath = resolve(keyDir, `${locale}.json`);\n\n await writeJsonIfChanged(resultFilePath, localizedDictionary).catch(\n (err) => {\n console.error(\n `Error creating localized ${key}/${locale}.json:`,\n err\n );\n }\n );\n\n localizedDictionariesPathsRecord[locale] = {\n dictionaryPath: resultFilePath,\n dictionary: localizedDictionary,\n };\n });\n\n resultDictionariesPaths[key] = localizedDictionariesPathsRecord;\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateDictionaryEntryPoint(key, locales, format);\n\n const dynEntryPath = resolve(\n dynamicDictionariesDir,\n `${key}.${extension}`\n );\n assertPathWithin(dynEntryPath, dynamicDictionariesDir);\n\n await writeFileIfChanged(dynEntryPath, content).catch((err) => {\n console.error(\n `Error creating dynamic ${colorizePath(dynEntryPath)}:`,\n err\n );\n });\n });\n }\n );\n\n return resultDictionariesPaths;\n};\n"],"mappings":";;;;;;;;;;;;;AA4BA,MAAM,sBAAsB;;;;AAK5B,MAAa,gCACX,KACA,SACA,SAAwB,UACb;CAKX,MAAM,gBAJgB,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,MAC1C,OAAO,EAAE,CAAC,cAAc,OAAO,EAAE,CAAC,CAGD,CAChC,KAAK,WACJ,WAAW,QACP,MAAM,OAAO,qBAAqB,oBAAoB,GAAG,IAAI,GAAG,OAAO,gCACvE,MAAM,OAAO,sCAAsC,oBAAoB,GAAG,IAAI,GAAG,OAAO,UAC7F,CACA,KAAK,MAAM;CAEd,IAAI,WAAW,OACb,OACE,sBAAsB,cAAc;CAIxC,OAAO,uBAAuB,cAAc;;;;;;;;;;;;;;;;;;AAmB9C,MAAa,yBAAyB,OACpC,oBACA,eACA,UAA6BA,iDACU;CACvC,MAAM,EAAE,SAAS,kBAAkB,cAAc;CACjD,MAAM,EAAE,2BAA2B,cAAc;CAEjD,MAAM,iCAAkB,wBAAwB,oBAAoB;CACpE,kCAAY,SAAS,EAAE,WAAW,MAAM,CAAC;CAEzC,MAAM,0BAAqD,EAAE;CAG7D,MAAMC,sCACJ,OAAO,QAAQ,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,OAC7C,OAAO,EAAE,CAAC,cAAc,OAAO,EAAE,CAAC,CACnC,EACD,OAAO,CAAC,KAAK,qBAAqB;EAChC,IAAI,QAAQ,aAAa;EAEzB,MAAM,mCAA8D,EAAE;EAEtE,MAAM,gCAAiB,SAAS,IAAI;EACpC,6CAAiB,QAAQ,QAAQ;EACjC,kCAAY,QAAQ,EAAE,WAAW,MAAM,CAAC;EAExC,MAAMA,sCAAY,SAAS,OAAO,WAAW;GAC3C,MAAM,yEACJ,gBAAgB,YAChB,QACA,cACD;GAGD,MAAM,wCAAyB,QAAQ,GAAG,OAAO,OAAO;GAExD,MAAMC,8CAAmB,gBAAgB,oBAAoB,CAAC,OAC3D,QAAQ;IACP,QAAQ,MACN,4BAA4B,IAAI,GAAG,OAAO,SAC1C,IACD;KAEJ;GAED,iCAAiC,UAAU;IACzC,gBAAgB;IAChB,YAAY;IACb;IACD;EAEF,wBAAwB,OAAO;EAE/B,MAAMD,sCAAY,SAAS,OAAO,WAAW;GAC3C,MAAM,YAAY,WAAW,QAAQ,QAAQ;GAC7C,MAAM,UAAU,6BAA6B,KAAK,SAAS,OAAO;GAElE,MAAM,sCACJ,wBACA,GAAG,IAAI,GAAG,YACX;GACD,6CAAiB,cAAc,uBAAuB;GAEtD,MAAME,8CAAmB,cAAc,QAAQ,CAAC,OAAO,QAAQ;IAC7D,QAAQ,MACN,oEAAuC,aAAa,CAAC,IACrD,IACD;KACD;IACF;GAEL;CAED,OAAO"}
|
|
@@ -43,8 +43,10 @@ const writeFetchDictionary = async (dynamicDictionaries, configuration, formats
|
|
|
43
43
|
await require_utils_parallelize.parallelize(formats, async (format) => {
|
|
44
44
|
const extension = format === "cjs" ? "cjs" : "mjs";
|
|
45
45
|
const content = generateDictionaryEntryPoint(key, locales, relativePrefix, format);
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
const fetchEntryPath = (0, node_path.resolve)(fetchDictionariesDir, `${key}.${extension}`);
|
|
47
|
+
(0, _intlayer_config_utils.assertPathWithin)(fetchEntryPath, fetchDictionariesDir);
|
|
48
|
+
await require_writeFileIfChanged.writeFileIfChanged(fetchEntryPath, content).catch((err) => {
|
|
49
|
+
console.error(`Error creating fetch ${(0, _intlayer_config_logger.colorizePath)(fetchEntryPath)}:`, err);
|
|
48
50
|
});
|
|
49
51
|
});
|
|
50
52
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeFetchDictionary.cjs","names":["OUTPUT_FORMAT","parallelize","writeFileIfChanged"],"sources":["../../../src/buildIntlayerDictionary/writeFetchDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { relative, resolve } from 'node:path';\nimport { OUTPUT_FORMAT } from '@intlayer/config/defaultValues';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { normalizePath } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { parallelize } from '../utils/parallelize';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\nimport type { LocalizedDictionaryOutput } from './writeDynamicDictionary';\n\nexport const generateDictionaryEntryPoint = (\n key: string,\n locales: string[],\n relativePrefix: string,\n format: 'cjs' | 'esm' = 'esm'\n): string => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n\n const localeEntries = locales\n .sort((a, b) => String(a).localeCompare(String(b)))\n .map(\n (locale) =>\n ` '${locale}': async () => {\\n` +\n ` try {\\n` +\n ` const res = await fetch(\\`\\${editor.liveSyncURL}/dictionaries/${key}/${locale}\\`);\\n` +\n ` return await res.json();\\n` +\n ` } catch {\\n` +\n ` return dynContent['${locale}']();\\n` +\n ` }\\n` +\n ` }`\n )\n .join(',\\n');\n\n if (format === 'esm') {\n return (\n `import { editor } from 'intlayer';\\n` +\n `import dynContent from '${relativePrefix}/${key}.${extension}';\\n\\n` +\n `const content = {\\n${localeEntries}\\n};\\n\\n` +\n `export default content;\\n`\n );\n }\n return (\n `const { editor } = require('intlayer');\\n` +\n `const dynContent = require('${relativePrefix}/${key}.${extension}');\\n\\n` +\n `module.exports = {\\n${localeEntries}\\n};\\n`\n );\n};\n\n/**\n * Write the localized dictionaries to the dictionariesDir\n * @param mergedDictionaries - The merged dictionaries\n * @param configuration - The configuration\n * @returns The final dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/fetch_dictionary/home.mjs\n * // .intlayer/fetch_dictionary/home.cjs\n * ```\n */\nexport const writeFetchDictionary = async (\n dynamicDictionaries: LocalizedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT\n): Promise<LocalizedDictionaryOutput> => {\n const { fetchDictionariesDir, dynamicDictionariesDir } = configuration.system;\n const { locales } = configuration.internationalization;\n\n // Compute relative path from fetch dir to dynamic dir\n let relativePrefix = normalizePath(\n relative(fetchDictionariesDir, dynamicDictionariesDir)\n );\n if (!relativePrefix.startsWith('.')) {\n relativePrefix = `./${relativePrefix}`;\n }\n\n await mkdir(resolve(fetchDictionariesDir), { recursive: true });\n\n const resultDictionariesPaths: LocalizedDictionaryOutput = {};\n\n // Write entry points for each dictionary in parallel\n await parallelize(Object.entries(dynamicDictionaries), async ([key]) => {\n if (key === 'undefined') return;\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateDictionaryEntryPoint(\n key,\n locales,\n relativePrefix,\n format\n );\n\n
|
|
1
|
+
{"version":3,"file":"writeFetchDictionary.cjs","names":["OUTPUT_FORMAT","parallelize","writeFileIfChanged"],"sources":["../../../src/buildIntlayerDictionary/writeFetchDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { relative, resolve } from 'node:path';\nimport { OUTPUT_FORMAT } from '@intlayer/config/defaultValues';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin, normalizePath } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { parallelize } from '../utils/parallelize';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\nimport type { LocalizedDictionaryOutput } from './writeDynamicDictionary';\n\nexport const generateDictionaryEntryPoint = (\n key: string,\n locales: string[],\n relativePrefix: string,\n format: 'cjs' | 'esm' = 'esm'\n): string => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n\n const localeEntries = locales\n .sort((a, b) => String(a).localeCompare(String(b)))\n .map(\n (locale) =>\n ` '${locale}': async () => {\\n` +\n ` try {\\n` +\n ` const res = await fetch(\\`\\${editor.liveSyncURL}/dictionaries/${key}/${locale}\\`);\\n` +\n ` return await res.json();\\n` +\n ` } catch {\\n` +\n ` return dynContent['${locale}']();\\n` +\n ` }\\n` +\n ` }`\n )\n .join(',\\n');\n\n if (format === 'esm') {\n return (\n `import { editor } from 'intlayer';\\n` +\n `import dynContent from '${relativePrefix}/${key}.${extension}';\\n\\n` +\n `const content = {\\n${localeEntries}\\n};\\n\\n` +\n `export default content;\\n`\n );\n }\n return (\n `const { editor } = require('intlayer');\\n` +\n `const dynContent = require('${relativePrefix}/${key}.${extension}');\\n\\n` +\n `module.exports = {\\n${localeEntries}\\n};\\n`\n );\n};\n\n/**\n * Write the localized dictionaries to the dictionariesDir\n * @param mergedDictionaries - The merged dictionaries\n * @param configuration - The configuration\n * @returns The final dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/fetch_dictionary/home.mjs\n * // .intlayer/fetch_dictionary/home.cjs\n * ```\n */\nexport const writeFetchDictionary = async (\n dynamicDictionaries: LocalizedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT\n): Promise<LocalizedDictionaryOutput> => {\n const { fetchDictionariesDir, dynamicDictionariesDir } = configuration.system;\n const { locales } = configuration.internationalization;\n\n // Compute relative path from fetch dir to dynamic dir\n let relativePrefix = normalizePath(\n relative(fetchDictionariesDir, dynamicDictionariesDir)\n );\n if (!relativePrefix.startsWith('.')) {\n relativePrefix = `./${relativePrefix}`;\n }\n\n await mkdir(resolve(fetchDictionariesDir), { recursive: true });\n\n const resultDictionariesPaths: LocalizedDictionaryOutput = {};\n\n // Write entry points for each dictionary in parallel\n await parallelize(Object.entries(dynamicDictionaries), async ([key]) => {\n if (key === 'undefined') return;\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateDictionaryEntryPoint(\n key,\n locales,\n relativePrefix,\n format\n );\n\n const fetchEntryPath = resolve(\n fetchDictionariesDir,\n `${key}.${extension}`\n );\n assertPathWithin(fetchEntryPath, fetchDictionariesDir);\n\n await writeFileIfChanged(fetchEntryPath, content).catch((err) => {\n console.error(\n `Error creating fetch ${colorizePath(fetchEntryPath)}:`,\n err\n );\n });\n });\n });\n\n return resultDictionariesPaths;\n};\n"],"mappings":";;;;;;;;;;;AAUA,MAAa,gCACX,KACA,SACA,gBACA,SAAwB,UACb;CACX,MAAM,YAAY,WAAW,QAAQ,QAAQ;CAE7C,MAAM,gBAAgB,QACnB,MAAM,GAAG,MAAM,OAAO,EAAE,CAAC,cAAc,OAAO,EAAE,CAAC,CAAC,CAClD,KACE,WACC,MAAM,OAAO,mGAE0D,IAAI,GAAG,OAAO,gFAGzD,OAAO,mBAGtC,CACA,KAAK,MAAM;CAEd,IAAI,WAAW,OACb,OACE,+DAC2B,eAAe,GAAG,IAAI,GAAG,UAAU,2BACxC,cAAc;CAIxC,OACE,wEAC+B,eAAe,GAAG,IAAI,GAAG,UAAU,6BAC3C,cAAc;;;;;;;;;;;;;;;;;;AAoBzC,MAAa,uBAAuB,OAClC,qBACA,eACA,UAA6BA,iDACU;CACvC,MAAM,EAAE,sBAAsB,2BAA2B,cAAc;CACvE,MAAM,EAAE,YAAY,cAAc;CAGlC,IAAI,mFACO,sBAAsB,uBAAuB,CACvD;CACD,IAAI,CAAC,eAAe,WAAW,IAAI,EACjC,iBAAiB,KAAK;CAGxB,yDAAoB,qBAAqB,EAAE,EAAE,WAAW,MAAM,CAAC;CAE/D,MAAM,0BAAqD,EAAE;CAG7D,MAAMC,sCAAY,OAAO,QAAQ,oBAAoB,EAAE,OAAO,CAAC,SAAS;EACtE,IAAI,QAAQ,aAAa;EAEzB,MAAMA,sCAAY,SAAS,OAAO,WAAW;GAC3C,MAAM,YAAY,WAAW,QAAQ,QAAQ;GAC7C,MAAM,UAAU,6BACd,KACA,SACA,gBACA,OACD;GAED,MAAM,wCACJ,sBACA,GAAG,IAAI,GAAG,YACX;GACD,6CAAiB,gBAAgB,qBAAqB;GAEtD,MAAMC,8CAAmB,gBAAgB,QAAQ,CAAC,OAAO,QAAQ;IAC/D,QAAQ,MACN,kEAAqC,eAAe,CAAC,IACrD,IACD;KACD;IACF;GACF;CAEF,OAAO"}
|
|
@@ -5,6 +5,7 @@ const require_writeJsonIfChanged = require('../writeJsonIfChanged.cjs');
|
|
|
5
5
|
let node_fs_promises = require("node:fs/promises");
|
|
6
6
|
let node_path = require("node:path");
|
|
7
7
|
let _intlayer_config_logger = require("@intlayer/config/logger");
|
|
8
|
+
let _intlayer_config_utils = require("@intlayer/config/utils");
|
|
8
9
|
let _intlayer_core_dictionaryManipulator = require("@intlayer/core/dictionaryManipulator");
|
|
9
10
|
|
|
10
11
|
//#region src/buildIntlayerDictionary/writeMergedDictionary.ts
|
|
@@ -31,6 +32,7 @@ const writeMergedDictionaries = async (groupedDictionaries, configuration) => {
|
|
|
31
32
|
if (key === "undefined") return;
|
|
32
33
|
const mergedDictionary = (0, _intlayer_core_dictionaryManipulator.mergeDictionaries)((0, _intlayer_core_dictionaryManipulator.normalizeDictionaries)(dictionariesEntry.dictionaries, configuration));
|
|
33
34
|
const resultFilePath = (0, node_path.resolve)(dictionariesDir, `${key}.json`);
|
|
35
|
+
(0, _intlayer_config_utils.assertPathWithin)(resultFilePath, dictionariesDir);
|
|
34
36
|
await require_writeJsonIfChanged.writeJsonIfChanged(resultFilePath, mergedDictionary).catch((err) => {
|
|
35
37
|
console.error(`Error creating merged ${(0, _intlayer_config_logger.colorizePath)(resultFilePath)}:`, err);
|
|
36
38
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeMergedDictionary.cjs","names":["parallelize","writeJsonIfChanged"],"sources":["../../../src/buildIntlayerDictionary/writeMergedDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { colorizePath } from '@intlayer/config/logger';\nimport {\n mergeDictionaries,\n normalizeDictionaries,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\nimport type { UnmergedDictionaryOutput } from './writeUnmergedDictionary';\n\nexport type MergedDictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary;\n};\n\nexport type MergedDictionaryOutput = Record<string, MergedDictionaryResult>;\n\n/**\n * Write the merged dictionaries to the dictionariesDir\n * @param groupedDictionaries - The grouped dictionaries\n * @param configuration - The configuration\n * @returns The merged dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/dictionary/home.json\n * // { key: 'home', content: { ... } },\n * ```\n */\nexport const writeMergedDictionaries = async (\n groupedDictionaries: UnmergedDictionaryOutput,\n configuration: IntlayerConfig\n): Promise<MergedDictionaryOutput> => {\n const { dictionariesDir } = configuration.system;\n\n // Create the dictionaries folder if it doesn't exist\n await mkdir(resolve(dictionariesDir), { recursive: true });\n\n const results = await parallelize(\n Object.entries(groupedDictionaries),\n async ([key, dictionariesEntry]) => {\n if (key === 'undefined') {\n return undefined as unknown as readonly [\n string,\n MergedDictionaryResult,\n ];\n }\n\n const normalizedDictionaries = normalizeDictionaries(\n dictionariesEntry.dictionaries,\n configuration\n );\n\n const mergedDictionary = mergeDictionaries(normalizedDictionaries);\n\n const outputFileName = `${key}.json`;\n const resultFilePath = resolve(dictionariesDir, outputFileName);\n\n // Write the merged dictionary\n await writeJsonIfChanged(resultFilePath, mergedDictionary).catch(\n (err) => {\n console.error(\n `Error creating merged ${colorizePath(resultFilePath)}:`,\n err\n );\n }\n );\n\n return [\n key,\n {\n dictionaryPath: resultFilePath,\n dictionary: mergedDictionary,\n } as MergedDictionaryResult,\n ] as const;\n }\n );\n\n return Object.fromEntries(\n results.filter(Boolean) as Array<readonly [string, MergedDictionaryResult]>\n );\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"writeMergedDictionary.cjs","names":["parallelize","writeJsonIfChanged"],"sources":["../../../src/buildIntlayerDictionary/writeMergedDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport {\n mergeDictionaries,\n normalizeDictionaries,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\nimport type { UnmergedDictionaryOutput } from './writeUnmergedDictionary';\n\nexport type MergedDictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary;\n};\n\nexport type MergedDictionaryOutput = Record<string, MergedDictionaryResult>;\n\n/**\n * Write the merged dictionaries to the dictionariesDir\n * @param groupedDictionaries - The grouped dictionaries\n * @param configuration - The configuration\n * @returns The merged dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/dictionary/home.json\n * // { key: 'home', content: { ... } },\n * ```\n */\nexport const writeMergedDictionaries = async (\n groupedDictionaries: UnmergedDictionaryOutput,\n configuration: IntlayerConfig\n): Promise<MergedDictionaryOutput> => {\n const { dictionariesDir } = configuration.system;\n\n // Create the dictionaries folder if it doesn't exist\n await mkdir(resolve(dictionariesDir), { recursive: true });\n\n const results = await parallelize(\n Object.entries(groupedDictionaries),\n async ([key, dictionariesEntry]) => {\n if (key === 'undefined') {\n return undefined as unknown as readonly [\n string,\n MergedDictionaryResult,\n ];\n }\n\n const normalizedDictionaries = normalizeDictionaries(\n dictionariesEntry.dictionaries,\n configuration\n );\n\n const mergedDictionary = mergeDictionaries(normalizedDictionaries);\n\n const outputFileName = `${key}.json`;\n const resultFilePath = resolve(dictionariesDir, outputFileName);\n\n assertPathWithin(resultFilePath, dictionariesDir);\n\n // Write the merged dictionary\n await writeJsonIfChanged(resultFilePath, mergedDictionary).catch(\n (err) => {\n console.error(\n `Error creating merged ${colorizePath(resultFilePath)}:`,\n err\n );\n }\n );\n\n return [\n key,\n {\n dictionaryPath: resultFilePath,\n dictionary: mergedDictionary,\n } as MergedDictionaryResult,\n ] as const;\n }\n );\n\n return Object.fromEntries(\n results.filter(Boolean) as Array<readonly [string, MergedDictionaryResult]>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,MAAa,0BAA0B,OACrC,qBACA,kBACoC;CACpC,MAAM,EAAE,oBAAoB,cAAc;CAG1C,yDAAoB,gBAAgB,EAAE,EAAE,WAAW,MAAM,CAAC;CAE1D,MAAM,UAAU,MAAMA,sCACpB,OAAO,QAAQ,oBAAoB,EACnC,OAAO,CAAC,KAAK,uBAAuB;EAClC,IAAI,QAAQ,aACV;EAWF,MAAM,+IAJJ,kBAAkB,cAClB,cAG+D,CAAC;EAGlE,MAAM,wCAAyB,iBAAiB,GADtB,IAAI,OACiC;EAE/D,6CAAiB,gBAAgB,gBAAgB;EAGjD,MAAMC,8CAAmB,gBAAgB,iBAAiB,CAAC,OACxD,QAAQ;GACP,QAAQ,MACN,mEAAsC,eAAe,CAAC,IACtD,IACD;IAEJ;EAED,OAAO,CACL,KACA;GACE,gBAAgB;GAChB,YAAY;GACb,CACF;GAEJ;CAED,OAAO,OAAO,YACZ,QAAQ,OAAO,QAAQ,CACxB"}
|
|
@@ -6,6 +6,7 @@ const require_filterInvalidDictionaries = require('../filterInvalidDictionaries.
|
|
|
6
6
|
let node_fs_promises = require("node:fs/promises");
|
|
7
7
|
let node_path = require("node:path");
|
|
8
8
|
let _intlayer_config_logger = require("@intlayer/config/logger");
|
|
9
|
+
let _intlayer_config_utils = require("@intlayer/config/utils");
|
|
9
10
|
let _intlayer_core_dictionaryManipulator = require("@intlayer/core/dictionaryManipulator");
|
|
10
11
|
|
|
11
12
|
//#region src/buildIntlayerDictionary/writeUnmergedDictionary.ts
|
|
@@ -43,6 +44,7 @@ const writeUnmergedDictionaries = async (dictionaries, configuration, env) => {
|
|
|
43
44
|
if (key === "undefined") return;
|
|
44
45
|
const orderedDictionaries = (0, _intlayer_core_dictionaryManipulator.orderDictionaries)(dictionaries);
|
|
45
46
|
const unmergedFilePath = (0, node_path.resolve)(unmergedDictionariesDir, `${key}.json`);
|
|
47
|
+
(0, _intlayer_config_utils.assertPathWithin)(unmergedFilePath, unmergedDictionariesDir);
|
|
46
48
|
if (configuration.editor.enabled || env === "dev") await require_writeJsonIfChanged.writeJsonIfChanged(unmergedFilePath, orderedDictionaries).catch((err) => {
|
|
47
49
|
console.error(`${_intlayer_config_logger.x} Error creating unmerged ${(0, _intlayer_config_logger.colorizePath)(unmergedFilePath)}:`, err);
|
|
48
50
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeUnmergedDictionary.cjs","names":["filterInvalidDictionaries","parallelize","writeJsonIfChanged","x"],"sources":["../../../src/buildIntlayerDictionary/writeUnmergedDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { colorizePath, x } from '@intlayer/config/logger';\nimport { orderDictionaries } from '@intlayer/core/dictionaryManipulator';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary, DictionaryKey } from '@intlayer/types/dictionary';\nimport { filterInvalidDictionaries } from '../filterInvalidDictionaries';\nimport { parallelize } from '../utils/parallelize';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\n\nexport const groupDictionariesByKey = (\n dictionaries: Dictionary[]\n): Record<string, Dictionary[]> =>\n dictionaries.reduce(\n (acc, dictionary) => {\n const key = dictionary.key;\n if (!acc[key]) {\n acc[key] = [];\n }\n acc[key].push(dictionary);\n return acc;\n },\n {} as Record<string, Dictionary[]>\n );\n\nexport type UnmergedDictionaryResult = {\n dictionaryPath: string;\n dictionaries: Dictionary[];\n};\n\nexport type UnmergedDictionaryOutput = Record<\n DictionaryKey,\n UnmergedDictionaryResult\n>;\n\n/**\n * Write the unmerged dictionaries to the unmergedDictionariesDir\n * @param dictionaries - The dictionaries to write\n * @param configuration - The configuration\n * @returns The grouped dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * console.log(unmergedDictionaries);\n *\n * // .intlayer/unmerged_dictionaries/home.json\n * // {\n * // [\n * // { key: 'home', content: { ... } },\n * // { key: 'home', content: { ... } },\n * // ],\n * // }\n * ```\n */\nexport const writeUnmergedDictionaries = async (\n dictionaries: Dictionary[],\n configuration: IntlayerConfig,\n env: 'prod' | 'dev'\n): Promise<UnmergedDictionaryOutput> => {\n const { unmergedDictionariesDir } = configuration.system;\n\n // Create the dictionaries folder if it doesn't exist\n await mkdir(resolve(unmergedDictionariesDir), { recursive: true });\n\n const filteredDictionaries = filterInvalidDictionaries(\n dictionaries,\n configuration,\n { checkSchema: true }\n );\n\n // Group dictionaries by key and write to unmergedDictionariesDir\n const groupedDictionaries = groupDictionariesByKey(filteredDictionaries);\n\n const results = await parallelize(\n Object.entries(groupedDictionaries),\n async ([key, dictionaries]) => {\n if (key === 'undefined') {\n return undefined as unknown as readonly [\n string,\n UnmergedDictionaryResult,\n ];\n }\n\n const orderedDictionaries = orderDictionaries(dictionaries);\n\n const outputFileName = `${key}.json`;\n const unmergedFilePath = resolve(unmergedDictionariesDir, outputFileName);\n\n // Write the grouped dictionaries in disk if the editor is enabled\n // To make work the visual editor on the server\n // No need them if the editor is disabled\n // But in local env (env: 'dev') we write them for the vscode extension\n if (configuration.editor.enabled || env === 'dev') {\n await writeJsonIfChanged(unmergedFilePath, orderedDictionaries).catch(\n (err) => {\n console.error(\n `${x} Error creating unmerged ${colorizePath(unmergedFilePath)}:`,\n err\n );\n }\n );\n }\n\n return [\n key,\n {\n dictionaryPath: unmergedFilePath,\n dictionaries: dictionaries,\n } as UnmergedDictionaryResult,\n ] as const;\n }\n );\n\n return Object.fromEntries(\n results.filter(Boolean) as Array<\n readonly [string, UnmergedDictionaryResult]\n >\n );\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"writeUnmergedDictionary.cjs","names":["filterInvalidDictionaries","parallelize","writeJsonIfChanged","x"],"sources":["../../../src/buildIntlayerDictionary/writeUnmergedDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { colorizePath, x } from '@intlayer/config/logger';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport { orderDictionaries } from '@intlayer/core/dictionaryManipulator';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary, DictionaryKey } from '@intlayer/types/dictionary';\nimport { filterInvalidDictionaries } from '../filterInvalidDictionaries';\nimport { parallelize } from '../utils/parallelize';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\n\nexport const groupDictionariesByKey = (\n dictionaries: Dictionary[]\n): Record<string, Dictionary[]> =>\n dictionaries.reduce(\n (acc, dictionary) => {\n const key = dictionary.key;\n if (!acc[key]) {\n acc[key] = [];\n }\n acc[key].push(dictionary);\n return acc;\n },\n {} as Record<string, Dictionary[]>\n );\n\nexport type UnmergedDictionaryResult = {\n dictionaryPath: string;\n dictionaries: Dictionary[];\n};\n\nexport type UnmergedDictionaryOutput = Record<\n DictionaryKey,\n UnmergedDictionaryResult\n>;\n\n/**\n * Write the unmerged dictionaries to the unmergedDictionariesDir\n * @param dictionaries - The dictionaries to write\n * @param configuration - The configuration\n * @returns The grouped dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * console.log(unmergedDictionaries);\n *\n * // .intlayer/unmerged_dictionaries/home.json\n * // {\n * // [\n * // { key: 'home', content: { ... } },\n * // { key: 'home', content: { ... } },\n * // ],\n * // }\n * ```\n */\nexport const writeUnmergedDictionaries = async (\n dictionaries: Dictionary[],\n configuration: IntlayerConfig,\n env: 'prod' | 'dev'\n): Promise<UnmergedDictionaryOutput> => {\n const { unmergedDictionariesDir } = configuration.system;\n\n // Create the dictionaries folder if it doesn't exist\n await mkdir(resolve(unmergedDictionariesDir), { recursive: true });\n\n const filteredDictionaries = filterInvalidDictionaries(\n dictionaries,\n configuration,\n { checkSchema: true }\n );\n\n // Group dictionaries by key and write to unmergedDictionariesDir\n const groupedDictionaries = groupDictionariesByKey(filteredDictionaries);\n\n const results = await parallelize(\n Object.entries(groupedDictionaries),\n async ([key, dictionaries]) => {\n if (key === 'undefined') {\n return undefined as unknown as readonly [\n string,\n UnmergedDictionaryResult,\n ];\n }\n\n const orderedDictionaries = orderDictionaries(dictionaries);\n\n const outputFileName = `${key}.json`;\n const unmergedFilePath = resolve(unmergedDictionariesDir, outputFileName);\n\n assertPathWithin(unmergedFilePath, unmergedDictionariesDir);\n\n // Write the grouped dictionaries in disk if the editor is enabled\n // To make work the visual editor on the server\n // No need them if the editor is disabled\n // But in local env (env: 'dev') we write them for the vscode extension\n if (configuration.editor.enabled || env === 'dev') {\n await writeJsonIfChanged(unmergedFilePath, orderedDictionaries).catch(\n (err) => {\n console.error(\n `${x} Error creating unmerged ${colorizePath(unmergedFilePath)}:`,\n err\n );\n }\n );\n }\n\n return [\n key,\n {\n dictionaryPath: unmergedFilePath,\n dictionaries: dictionaries,\n } as UnmergedDictionaryResult,\n ] as const;\n }\n );\n\n return Object.fromEntries(\n results.filter(Boolean) as Array<\n readonly [string, UnmergedDictionaryResult]\n >\n );\n};\n"],"mappings":";;;;;;;;;;;;AAWA,MAAa,0BACX,iBAEA,aAAa,QACV,KAAK,eAAe;CACnB,MAAM,MAAM,WAAW;CACvB,IAAI,CAAC,IAAI,MACP,IAAI,OAAO,EAAE;CAEf,IAAI,KAAK,KAAK,WAAW;CACzB,OAAO;GAET,EAAE,CACH;;;;;;;;;;;;;;;;;;;;;AAgCH,MAAa,4BAA4B,OACvC,cACA,eACA,QACsC;CACtC,MAAM,EAAE,4BAA4B,cAAc;CAGlD,yDAAoB,wBAAwB,EAAE,EAAE,WAAW,MAAM,CAAC;CASlE,MAAM,sBAAsB,uBAPCA,4DAC3B,cACA,eACA,EAAE,aAAa,MAAM,CAIgD,CAAC;CAExE,MAAM,UAAU,MAAMC,sCACpB,OAAO,QAAQ,oBAAoB,EACnC,OAAO,CAAC,KAAK,kBAAkB;EAC7B,IAAI,QAAQ,aACV;EAMF,MAAM,kFAAwC,aAAa;EAG3D,MAAM,0CAA2B,yBAAyB,GADhC,IAAI,OAC2C;EAEzE,6CAAiB,kBAAkB,wBAAwB;EAM3D,IAAI,cAAc,OAAO,WAAW,QAAQ,OAC1C,MAAMC,8CAAmB,kBAAkB,oBAAoB,CAAC,OAC7D,QAAQ;GACP,QAAQ,MACN,GAAGC,0BAAE,qEAAwC,iBAAiB,CAAC,IAC/D,IACD;IAEJ;EAGH,OAAO,CACL,KACA;GACE,gBAAgB;GACF;GACf,CACF;GAEJ;CAED,OAAO,OAAO,YACZ,QAAQ,OAAO,QAAQ,CAGxB"}
|
|
@@ -7,7 +7,6 @@ let node_path = require("node:path");
|
|
|
7
7
|
let fast_glob = require("fast-glob");
|
|
8
8
|
fast_glob = require_runtime.__toESM(fast_glob);
|
|
9
9
|
let _intlayer_config_utils = require("@intlayer/config/utils");
|
|
10
|
-
let zod_to_ts = require("zod-to-ts");
|
|
11
10
|
|
|
12
11
|
//#region src/createType/createModuleAugmentation.ts
|
|
13
12
|
const getTypeName = (key) => `${(0, _intlayer_config_utils.kebabCaseToCamelCase)(key)}Content`;
|
|
@@ -54,7 +53,7 @@ const zodToTsString = (schema) => {
|
|
|
54
53
|
}
|
|
55
54
|
};
|
|
56
55
|
/** Generate the content of the module augmentation file */
|
|
57
|
-
const generateTypeIndexContent = (typeFiles, configuration) => {
|
|
56
|
+
const generateTypeIndexContent = (typeFiles, configuration, zodToTsFns) => {
|
|
58
57
|
const { internationalization, system, editor } = configuration;
|
|
59
58
|
const { moduleAugmentationDir } = system;
|
|
60
59
|
const { enabled } = editor;
|
|
@@ -76,9 +75,11 @@ const generateTypeIndexContent = (typeFiles, configuration) => {
|
|
|
76
75
|
const formattedSchemas = Object.entries(schemas).map(([key, schema]) => {
|
|
77
76
|
let typeStr = "any";
|
|
78
77
|
if (schema) try {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
if (zodToTsFns) {
|
|
79
|
+
const { node } = zodToTsFns.zodToTs(schema, { auxiliaryTypeStore: zodToTsFns.createAuxiliaryTypeStore() });
|
|
80
|
+
if (node.kind !== 133) typeStr = zodToTsFns.printNode(node);
|
|
81
|
+
else typeStr = zodToTsString(schema);
|
|
82
|
+
} else typeStr = zodToTsString(schema);
|
|
82
83
|
} catch (_e) {
|
|
83
84
|
typeStr = zodToTsString(schema);
|
|
84
85
|
}
|
|
@@ -103,7 +104,17 @@ const generateTypeIndexContent = (typeFiles, configuration) => {
|
|
|
103
104
|
const createModuleAugmentation = async (configuration) => {
|
|
104
105
|
const { moduleAugmentationDir, typesDir } = configuration.system;
|
|
105
106
|
await (0, node_fs_promises.mkdir)(moduleAugmentationDir, { recursive: true });
|
|
106
|
-
const
|
|
107
|
+
const dictionariesTypesDefinitions = await (0, fast_glob.default)((0, _intlayer_config_utils.normalizePath)(`${typesDir}/*.ts`), { ignore: ["**/*.d.ts"] });
|
|
108
|
+
let zodToTsFns = null;
|
|
109
|
+
try {
|
|
110
|
+
const mod = await import("zod-to-ts");
|
|
111
|
+
zodToTsFns = {
|
|
112
|
+
zodToTs: mod.zodToTs,
|
|
113
|
+
printNode: mod.printNode,
|
|
114
|
+
createAuxiliaryTypeStore: mod.createAuxiliaryTypeStore
|
|
115
|
+
};
|
|
116
|
+
} catch {}
|
|
117
|
+
const tsContent = generateTypeIndexContent(dictionariesTypesDefinitions, configuration, zodToTsFns);
|
|
107
118
|
await require_writeFileIfChanged.writeFileIfChanged((0, node_path.join)(moduleAugmentationDir, "intlayer.d.ts"), tsContent);
|
|
108
119
|
};
|
|
109
120
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createModuleAugmentation.cjs","names":["getPathHash","writeFileIfChanged"],"sources":["../../../src/createType/createModuleAugmentation.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { basename, extname, join, relative } from 'node:path';\nimport { kebabCaseToCamelCase, normalizePath } from '@intlayer/config/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport fg from 'fast-glob';\nimport { createAuxiliaryTypeStore, printNode, zodToTs } from 'zod-to-ts';\nimport { getPathHash } from '../utils';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\n\nexport const getTypeName = (key: string): string =>\n `${kebabCaseToCamelCase(key)}Content`;\n\n/** Returns lines like: [Locales.FRENCH]: 1; */\nconst formatLocales = (locales: Locale[]) =>\n locales.map((locale) => ` \"${locale}\": 1;`).join('\\n');\n\nconst zodToTsString = (schema: any): string => {\n if (!schema) return 'any';\n\n // Support both real Zod objects (_def) and serialized versions (def or nested)\n const def = schema._def ?? schema.def ?? schema;\n\n // Handle serialized type names (sometimes 'type' instead of 'typeName')\n const typeName = def.typeName ?? def.type;\n\n switch (typeName) {\n case 'ZodString':\n case 'string':\n return 'string';\n case 'ZodNumber':\n case 'number':\n return 'number';\n case 'ZodBoolean':\n case 'boolean':\n return 'boolean';\n case 'ZodNull':\n case 'null':\n return 'null';\n case 'ZodUndefined':\n case 'undefined':\n return 'undefined';\n case 'ZodArray':\n case 'array':\n return `${zodToTsString(def.type ?? def.element)}[]`;\n case 'ZodObject':\n case 'object': {\n const shape = typeof def.shape === 'function' ? def.shape() : def.shape;\n if (!shape) return 'Record<string, any>';\n\n const entries = Object.entries(shape)\n .map(([k, v]) => ` \"${k}\": ${zodToTsString(v)};`)\n .join('\\n');\n return `{\\n${entries}\\n }`;\n }\n case 'ZodOptional':\n case 'optional':\n return `${zodToTsString(def.innerType ?? def.wrapped)} | undefined`;\n case 'ZodNullable':\n case 'nullable':\n return `${zodToTsString(def.innerType ?? def.wrapped)} | null`;\n case 'ZodUnion':\n case 'union': {\n const options = def.options ?? [];\n return options.map(zodToTsString).join(' | ');\n }\n case 'ZodIntersection':\n case 'intersection':\n return `${zodToTsString(def.left)} & ${zodToTsString(def.right)}`;\n case 'ZodEnum':\n case 'enum': {\n const values = def.values ?? [];\n return values.map((v: string) => `\"${v}\"`).join(' | ');\n }\n case 'ZodLiteral':\n case 'literal': {\n const value = def.value;\n return typeof value === 'string' ? `\"${value}\"` : String(value);\n }\n default:\n return 'any';\n }\n};\n\n/** Generate the content of the module augmentation file */\nconst generateTypeIndexContent = (\n typeFiles: string[],\n configuration: IntlayerConfig\n): string => {\n const { internationalization, system, editor } = configuration;\n const { moduleAugmentationDir } = system;\n const { enabled } = editor;\n const { locales, requiredLocales, strictMode } = internationalization;\n\n let fileContent = 'import \"intlayer\";\\n';\n\n // Build dictionary refs\n const dictionariesRef = typeFiles.map((dictionaryPath) => ({\n relativePath: `./${relative(moduleAugmentationDir, dictionaryPath)}`,\n id: basename(dictionaryPath, extname(dictionaryPath)),\n hash: `_${getPathHash(dictionaryPath)}`,\n }));\n\n // Import all dictionaries\n for (const dictionary of dictionariesRef) {\n fileContent += `import ${dictionary.hash} from '${dictionary.relativePath}';\\n`;\n }\n fileContent += '\\n';\n\n // Dictionary map entries (id: typeof <hash>)\n const formattedDictionaryMap: string = dictionariesRef\n .map((dictionary) => ` \"${dictionary.id}\": typeof ${dictionary.hash};`)\n .join('\\n');\n\n // Ensure required ⊆ declared; if empty, default required = declared\n const declared = locales;\n const requiredSanitized = requiredLocales?.length\n ? requiredLocales.filter((requiredLocales) =>\n declared.includes(requiredLocales)\n )\n : declared;\n\n const formattedDeclaredLocales = formatLocales(declared);\n const formattedRequiredLocales = formatLocales(requiredSanitized);\n\n // Build schema registry\n const schemas = configuration.schemas ?? {};\n const formattedSchemas = Object.entries(schemas)\n .map(([key, schema]) => {\n let typeStr = 'any';\n\n if (schema) {\n try {\n const { node } = zodToTs(schema, {\n auxiliaryTypeStore: createAuxiliaryTypeStore(),\n });\n // 133 is the kind for AnyKeyword in TypeScript\n if ((node as any).kind !== 133) {\n typeStr = printNode(node);\n } else {\n // Fallback to custom string generator if zodToTs returns any\n typeStr = zodToTsString(schema);\n }\n } catch (_e) {\n // Fallback to custom string generator\n typeStr = zodToTsString(schema);\n }\n }\n return ` \"${key}\": ${typeStr};`;\n })\n .join('\\n');\n\n // Choose strict mode registry key\n const strictKey =\n strictMode === 'strict'\n ? 'strict'\n : strictMode === 'inclusive'\n ? 'inclusive'\n : 'loose';\n\n /**\n * Module augmentation that ONLY adds keys to registries.\n * No types/aliases redefined here—avoids merge conflicts.\n */\n fileContent += `declare module 'intlayer' {\\n`;\n // Dictionaries registry\n fileContent += ` interface __DictionaryRegistry {\\n${formattedDictionaryMap}\\n }\\n\\n`;\n // Locales registries\n fileContent += ` interface __DeclaredLocalesRegistry {\\n${formattedDeclaredLocales}\\n }\\n\\n`;\n fileContent += ` interface __RequiredLocalesRegistry {\\n${formattedRequiredLocales}\\n }\\n\\n`;\n // Schema registry\n fileContent += ` interface __SchemaRegistry {\\n${formattedSchemas}\\n }\\n\\n`;\n // Resolved strict mode (narrow the literal at build time)\n fileContent += ` interface __StrictModeRegistry { mode: '${strictKey}' }\\n\\n`;\n // Editor registry\n fileContent += ` interface __EditorRegistry { enabled : ${enabled} } \\n`;\n fileContent += `}\\n`;\n\n return fileContent;\n};\n\n/** Generate the index file merging all the types */\nexport const createModuleAugmentation = async (\n configuration: IntlayerConfig\n) => {\n const { moduleAugmentationDir, typesDir } = configuration.system;\n\n await mkdir(moduleAugmentationDir, { recursive: true });\n\n const dictionariesTypesDefinitions: string[] = await fg(\n normalizePath(`${typesDir}/*.ts`),\n { ignore: ['**/*.d.ts'] }\n );\n\n const tsContent = generateTypeIndexContent(\n dictionariesTypesDefinitions,\n configuration\n );\n\n const tsFilePath = join(moduleAugmentationDir, 'intlayer.d.ts');\n await writeFileIfChanged(tsFilePath, tsContent);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createModuleAugmentation.cjs","names":["getPathHash","writeFileIfChanged"],"sources":["../../../src/createType/createModuleAugmentation.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { basename, extname, join, relative } from 'node:path';\nimport { kebabCaseToCamelCase, normalizePath } from '@intlayer/config/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport fg from 'fast-glob';\nimport { getPathHash } from '../utils';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\n\nexport const getTypeName = (key: string): string =>\n `${kebabCaseToCamelCase(key)}Content`;\n\n/** Returns lines like: [Locales.FRENCH]: 1; */\nconst formatLocales = (locales: Locale[]) =>\n locales.map((locale) => ` \"${locale}\": 1;`).join('\\n');\n\nconst zodToTsString = (schema: any): string => {\n if (!schema) return 'any';\n\n // Support both real Zod objects (_def) and serialized versions (def or nested)\n const def = schema._def ?? schema.def ?? schema;\n\n // Handle serialized type names (sometimes 'type' instead of 'typeName')\n const typeName = def.typeName ?? def.type;\n\n switch (typeName) {\n case 'ZodString':\n case 'string':\n return 'string';\n case 'ZodNumber':\n case 'number':\n return 'number';\n case 'ZodBoolean':\n case 'boolean':\n return 'boolean';\n case 'ZodNull':\n case 'null':\n return 'null';\n case 'ZodUndefined':\n case 'undefined':\n return 'undefined';\n case 'ZodArray':\n case 'array':\n return `${zodToTsString(def.type ?? def.element)}[]`;\n case 'ZodObject':\n case 'object': {\n const shape = typeof def.shape === 'function' ? def.shape() : def.shape;\n if (!shape) return 'Record<string, any>';\n\n const entries = Object.entries(shape)\n .map(([k, v]) => ` \"${k}\": ${zodToTsString(v)};`)\n .join('\\n');\n return `{\\n${entries}\\n }`;\n }\n case 'ZodOptional':\n case 'optional':\n return `${zodToTsString(def.innerType ?? def.wrapped)} | undefined`;\n case 'ZodNullable':\n case 'nullable':\n return `${zodToTsString(def.innerType ?? def.wrapped)} | null`;\n case 'ZodUnion':\n case 'union': {\n const options = def.options ?? [];\n return options.map(zodToTsString).join(' | ');\n }\n case 'ZodIntersection':\n case 'intersection':\n return `${zodToTsString(def.left)} & ${zodToTsString(def.right)}`;\n case 'ZodEnum':\n case 'enum': {\n const values = def.values ?? [];\n return values.map((v: string) => `\"${v}\"`).join(' | ');\n }\n case 'ZodLiteral':\n case 'literal': {\n const value = def.value;\n return typeof value === 'string' ? `\"${value}\"` : String(value);\n }\n default:\n return 'any';\n }\n};\n\ntype ZodToTsFns = {\n zodToTs: (schema: any, opts?: any) => { node: any };\n printNode: (node: any) => string;\n createAuxiliaryTypeStore: () => any;\n};\n\n/** Generate the content of the module augmentation file */\nconst generateTypeIndexContent = (\n typeFiles: string[],\n configuration: IntlayerConfig,\n zodToTsFns: ZodToTsFns | null\n): string => {\n const { internationalization, system, editor } = configuration;\n const { moduleAugmentationDir } = system;\n const { enabled } = editor;\n const { locales, requiredLocales, strictMode } = internationalization;\n\n let fileContent = 'import \"intlayer\";\\n';\n\n // Build dictionary refs\n const dictionariesRef = typeFiles.map((dictionaryPath) => ({\n relativePath: `./${relative(moduleAugmentationDir, dictionaryPath)}`,\n id: basename(dictionaryPath, extname(dictionaryPath)),\n hash: `_${getPathHash(dictionaryPath)}`,\n }));\n\n // Import all dictionaries\n for (const dictionary of dictionariesRef) {\n fileContent += `import ${dictionary.hash} from '${dictionary.relativePath}';\\n`;\n }\n fileContent += '\\n';\n\n // Dictionary map entries (id: typeof <hash>)\n const formattedDictionaryMap: string = dictionariesRef\n .map((dictionary) => ` \"${dictionary.id}\": typeof ${dictionary.hash};`)\n .join('\\n');\n\n // Ensure required ⊆ declared; if empty, default required = declared\n const declared = locales;\n const requiredSanitized = requiredLocales?.length\n ? requiredLocales.filter((requiredLocales) =>\n declared.includes(requiredLocales)\n )\n : declared;\n\n const formattedDeclaredLocales = formatLocales(declared);\n const formattedRequiredLocales = formatLocales(requiredSanitized);\n\n // Build schema registry\n const schemas = configuration.schemas ?? {};\n const formattedSchemas = Object.entries(schemas)\n .map(([key, schema]) => {\n let typeStr = 'any';\n\n if (schema) {\n try {\n if (zodToTsFns) {\n const { node } = zodToTsFns.zodToTs(schema, {\n auxiliaryTypeStore: zodToTsFns.createAuxiliaryTypeStore(),\n });\n // 133 is the kind for AnyKeyword in TypeScript\n if ((node as any).kind !== 133) {\n typeStr = zodToTsFns.printNode(node);\n } else {\n typeStr = zodToTsString(schema);\n }\n } else {\n typeStr = zodToTsString(schema);\n }\n } catch (_e) {\n // Fallback to custom string generator\n typeStr = zodToTsString(schema);\n }\n }\n return ` \"${key}\": ${typeStr};`;\n })\n .join('\\n');\n\n // Choose strict mode registry key\n const strictKey =\n strictMode === 'strict'\n ? 'strict'\n : strictMode === 'inclusive'\n ? 'inclusive'\n : 'loose';\n\n /**\n * Module augmentation that ONLY adds keys to registries.\n * No types/aliases redefined here—avoids merge conflicts.\n */\n fileContent += `declare module 'intlayer' {\\n`;\n // Dictionaries registry\n fileContent += ` interface __DictionaryRegistry {\\n${formattedDictionaryMap}\\n }\\n\\n`;\n // Locales registries\n fileContent += ` interface __DeclaredLocalesRegistry {\\n${formattedDeclaredLocales}\\n }\\n\\n`;\n fileContent += ` interface __RequiredLocalesRegistry {\\n${formattedRequiredLocales}\\n }\\n\\n`;\n // Schema registry\n fileContent += ` interface __SchemaRegistry {\\n${formattedSchemas}\\n }\\n\\n`;\n // Resolved strict mode (narrow the literal at build time)\n fileContent += ` interface __StrictModeRegistry { mode: '${strictKey}' }\\n\\n`;\n // Editor registry\n fileContent += ` interface __EditorRegistry { enabled : ${enabled} } \\n`;\n fileContent += `}\\n`;\n\n return fileContent;\n};\n\n/** Generate the index file merging all the types */\nexport const createModuleAugmentation = async (\n configuration: IntlayerConfig\n) => {\n const { moduleAugmentationDir, typesDir } = configuration.system;\n\n await mkdir(moduleAugmentationDir, { recursive: true });\n\n const dictionariesTypesDefinitions: string[] = await fg(\n normalizePath(`${typesDir}/*.ts`),\n { ignore: ['**/*.d.ts'] }\n );\n\n let zodToTsFns: ZodToTsFns | null = null;\n try {\n const mod = await import('zod-to-ts');\n zodToTsFns = {\n zodToTs: mod.zodToTs,\n printNode: mod.printNode,\n createAuxiliaryTypeStore: mod.createAuxiliaryTypeStore,\n };\n } catch {\n // typescript peer dep not installed (plain JS project), use fallback\n }\n\n const tsContent = generateTypeIndexContent(\n dictionariesTypesDefinitions,\n configuration,\n zodToTsFns\n );\n\n const tsFilePath = join(moduleAugmentationDir, 'intlayer.d.ts');\n await writeFileIfChanged(tsFilePath, tsContent);\n};\n"],"mappings":"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"}
|
|
@@ -230,11 +230,14 @@ const getLicenceContent = () => {
|
|
|
230
230
|
return "";
|
|
231
231
|
}
|
|
232
232
|
};
|
|
233
|
+
const ALLOWED_FETCH_HOSTS = new Set(["intlayer.org"]);
|
|
233
234
|
/**
|
|
234
235
|
* Fetches the content of a URL using native fetch.
|
|
235
236
|
*/
|
|
236
237
|
const fetchUrlContent = async (url) => {
|
|
237
|
-
const
|
|
238
|
+
const parsed = new URL(url);
|
|
239
|
+
if (parsed.protocol !== "https:" || !ALLOWED_FETCH_HOSTS.has(parsed.hostname)) throw new Error(`Blocked fetch to disallowed host: ${parsed.hostname}`);
|
|
240
|
+
const response = await fetch(url, { redirect: "error" });
|
|
238
241
|
if (!response.ok) throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
239
242
|
return response.text();
|
|
240
243
|
};
|
|
@@ -252,7 +255,7 @@ const installSkills = async (projectRoot, platform, skills) => {
|
|
|
252
255
|
const skillName = `intlayer-${SKILL_FILENAME_MAP[skill] ?? camelToKebabCase(skill)}`;
|
|
253
256
|
const skillContent = getSkillContent(skill);
|
|
254
257
|
if (!skillContent) continue;
|
|
255
|
-
const urls = Array.from(new Set(skillContent.match(/https:\/\/intlayer\.org\/
|
|
258
|
+
const urls = Array.from(new Set(skillContent.match(/https:\/\/intlayer\.org\/[^\s)]+\.md/g) || []));
|
|
256
259
|
const skillDir = node_path.default.join(skillsBaseDir, skillName);
|
|
257
260
|
const referenceDir = node_path.default.join(skillDir, "references");
|
|
258
261
|
await node_fs.promises.mkdir(referenceDir, { recursive: true });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["readAsset","path","fs","v"],"sources":["../../../src/installSkills/index.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { readAsset } from 'utils:asset';\nimport { v } from '@intlayer/config/logger';\nimport { getMarkdownMetadata } from '@intlayer/core/markdown';\n\n/**\n * Metadata for each available documentation skill.\n */\nexport const SKILLS_METADATA = {\n Config: 'Intlayer configuration documentation',\n Content: 'Reference for all Intlayer content node types (t, enu, etc.)',\n Usage: 'How to use Intlayer in your project',\n CLI: 'Intlayer CLI commands and usage',\n Compiler:\n 'Intlayer Compiler setup and usage for automatic content extraction without .content files',\n RemoteContent: 'How to use Intlayer with Remote/CMS/Server-side content',\n NextJS: 'Next.js-specific usage (Server & Client components)',\n React: 'React-specific syntax and hooks usage',\n Vue: 'Vue-specific composables and syntax',\n Svelte: 'Svelte-specific stores and syntax',\n Angular: 'Angular-specific syntax and Injectable Function usage',\n Preact: 'Preact-specific syntax and hooks usage',\n Solid:\n 'Integrates Intlayer internationalization with SolidJS components. Use when the user asks to \"setup SolidJS i18n\", use the \"useIntlayer\" hook in Solid, or manage locales in a SolidJS application.',\n Astro: 'Astro-specific usage and getIntlayer',\n} as const;\n\nexport type Skill = keyof typeof SKILLS_METADATA;\n\nexport const SKILLS = Object.keys(SKILLS_METADATA) as Skill[];\n\nexport const getInitialSkills = (\n deps: Record<string, string>\n): (keyof typeof SKILLS_METADATA)[] => {\n const skills: (keyof typeof SKILLS_METADATA)[] = [\n 'Usage',\n 'Content',\n 'Config',\n 'CLI',\n 'Compiler',\n ];\n\n if (deps.next) skills.push('NextJS');\n if (deps.react || !deps.next) skills.push('React');\n if (deps.preact) skills.push('Preact');\n if (deps['solid-js']) skills.push('Solid');\n if (deps.vue || deps.nuxt) skills.push('Vue');\n if (deps.svelte || deps['@sveltejs/kit']) skills.push('Svelte');\n if (deps.astro) skills.push('Astro');\n\n return skills;\n};\n\nexport interface PlatformMetadata {\n label: string;\n dir: string;\n check?: () => boolean;\n}\n\n/**\n * Metadata and configuration for each supported platform.\n */\nexport const PLATFORMS_METADATA: Record<string, PlatformMetadata> = {\n Cursor: {\n label: 'Cursor',\n dir: '.cursor/skills',\n check: () =>\n process.env.CURSOR === 'true' || process.env.TERM_PROGRAM === 'cursor',\n },\n Windsurf: {\n label: 'Windsurf',\n dir: '.windsurf/skills',\n check: () =>\n process.env.WINDSURF === 'true' ||\n process.env.TERM_PROGRAM === 'windsurf',\n },\n Trae: {\n label: 'Trae',\n dir: '.trae/skills',\n check: () =>\n process.env.TRAE === 'true' || process.env.TERM_PROGRAM === 'trae',\n },\n TraeCN: {\n label: 'Trae CN',\n dir: '.trae/skills',\n check: () => process.env.TRAE_CN === 'true',\n },\n VSCode: {\n label: 'VS Code',\n dir: '.vscode/skills',\n check: () =>\n process.env.VSCODE === 'true' || process.env.TERM_PROGRAM === 'vscode',\n },\n OpenCode: {\n label: 'OpenCode',\n dir: '.opencode/skills',\n check: () => process.env.OPENCODE === 'true',\n },\n Claude: {\n label: 'Claude Code',\n dir: '.claude/skills',\n check: () => process.env.CLAUDE === 'true',\n },\n GitHub: {\n label: 'GitHub Copilot Workspace',\n dir: '.github/skills',\n check: () =>\n process.env.GITHUB_ACTIONS === 'true' || !!process.env.GITHUB_WORKSPACE,\n },\n Antigravity: {\n label: 'Antigravity',\n dir: '.agent/skills',\n },\n Augment: {\n label: 'Augment',\n dir: '.augment/skills',\n },\n OpenClaw: {\n label: 'OpenClaw',\n dir: 'skills',\n },\n Cline: {\n label: 'Cline',\n dir: '.cline/skills',\n },\n CodeBuddy: {\n label: 'CodeBuddy',\n dir: '.codebuddy/skills',\n },\n CommandCode: {\n label: 'Command Code',\n dir: '.commandcode/skills',\n },\n Continue: {\n label: 'Continue',\n dir: '.continue/skills',\n },\n Crush: {\n label: 'Crush',\n dir: '.crush/skills',\n },\n Droid: {\n label: 'Droid',\n dir: '.factory/skills',\n },\n Goose: {\n label: 'Goose',\n dir: '.goose/skills',\n },\n IFlow: {\n label: 'iFlow CLI',\n dir: '.iflow/skills',\n },\n Junie: {\n label: 'Junie',\n dir: '.junie/skills',\n },\n KiloCode: {\n label: 'Kilo Code',\n dir: '.kilocode/skills',\n },\n Kiro: {\n label: 'Kiro CLI',\n dir: '.kiro/skills',\n },\n Kode: {\n label: 'Kode',\n dir: '.kode/skills',\n },\n MCPJam: {\n label: 'MCPJam',\n dir: '.mcpjam/skills',\n },\n MistralVibe: {\n label: 'Mistral Vibe',\n dir: '.vibe/skills',\n },\n Mux: {\n label: 'Mux',\n dir: '.mux/skills',\n },\n OpenHands: {\n label: 'OpenHands',\n dir: '.openhands/skills',\n },\n Pi: {\n label: 'Pi',\n dir: '.pi/skills',\n },\n Qoder: {\n label: 'Qoder',\n dir: '.qoder/skills',\n },\n Qwen: {\n label: 'Qwen Code',\n dir: '.qwen/skills',\n },\n RooCode: {\n label: 'Roo Code',\n dir: '.roo/skills',\n },\n Zencoder: {\n label: 'Zencoder',\n dir: '.zencoder/skills',\n },\n Neovate: {\n label: 'Neovate',\n dir: '.neovate/skills',\n },\n Pochi: {\n label: 'Pochi',\n dir: '.pochi/skills',\n },\n Other: {\n label: 'Other',\n dir: 'skills',\n },\n} as const;\n\nexport type Platform = keyof typeof PLATFORMS_METADATA;\n\nexport const PLATFORMS = Object.keys(PLATFORMS_METADATA) as Platform[];\n\n/**\n * Maps specific skill keys to special filenames if they differ from standard snake_case.\n */\nconst SKILL_FILENAME_MAP: Partial<Record<Skill, string>> = {};\n\n/**\n * Helper to convert CamelCase to kebab-case for directory naming\n */\nconst camelToKebabCase = (str: string) =>\n str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();\n\n/**\n * Reads the raw markdown content for a specific skill from the assets folder.\n */\nconst getSkillContent = (skill: Skill): string => {\n const baseName = SKILL_FILENAME_MAP[skill] ?? camelToKebabCase(skill);\n const fileName = `./skills/${baseName}.md`;\n\n try {\n return readAsset(fileName);\n } catch {\n console.warn(\n `Warning: Could not read asset for skill: ${skill} at ${fileName}`\n );\n return '';\n }\n};\n\n/**\n * Reads the licence content from the assets folder.\n */\nconst getLicenceContent = (): string => {\n try {\n return readAsset('./LICENCE.md');\n } catch {\n console.warn('Warning: Could not read LICENCE.md asset');\n return '';\n }\n};\n\n/**\n * Fetches the content of a URL using native fetch.\n */\nconst fetchUrlContent = async (url: string): Promise<string> => {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}: ${response.statusText}`);\n }\n return response.text();\n};\n\n/**\n * Installs skills using the \"Agent Skills\" directory standard.\n * Standard: <PROJECT_ROOT>/<CONFIG_DIR>/skills/<SKILL_NAME>/SKILL.md\n */\nexport const installSkills = async (\n projectRoot: string,\n platform: Platform,\n skills: Skill[]\n): Promise<string> => {\n // Determine destination directory\n const relativeDir = PLATFORMS_METADATA[platform].dir ?? 'skills';\n const skillsBaseDir = path.join(projectRoot, relativeDir);\n\n // Ensure the base directory exists\n await fs.mkdir(skillsBaseDir, { recursive: true });\n\n const createdSkills: string[] = [];\n const licenceContent = getLicenceContent();\n\n for (const skill of skills) {\n const baseName = SKILL_FILENAME_MAP[skill] ?? camelToKebabCase(skill);\n const skillName = `intlayer-${baseName}`;\n const skillContent = getSkillContent(skill);\n\n if (!skillContent) continue;\n\n // Extract unique URLs\n const urls = Array.from(\n new Set(\n skillContent.match(/https:\\/\\/intlayer\\.org\\/doc\\/[^\\s)]+\\.md/g) || []\n )\n );\n\n // Agent Standard: .../skills/<skill-name>/SKILL.md\n const skillDir = path.join(skillsBaseDir, skillName);\n const referenceDir = path.join(skillDir, 'references');\n\n // Ensure directories exist\n await fs.mkdir(referenceDir, { recursive: true });\n\n // Write License\n if (licenceContent) {\n await fs.writeFile(\n path.join(skillDir, 'LICENCE.md'),\n licenceContent,\n 'utf-8'\n );\n }\n\n let updatedSkillContent = skillContent;\n\n // Parallel download of references\n const downloadPromises = urls.map(async (url) => {\n try {\n const content = await fetchUrlContent(url);\n const metadata = getMarkdownMetadata<{ slugs?: string[] }>(content);\n\n let fileName = '';\n\n // Determine filename from slugs or URL path\n if (Array.isArray(metadata.slugs)) {\n fileName = metadata.slugs.filter((slug) => slug !== 'doc').join('_');\n } else {\n const urlPath = new URL(url).pathname;\n fileName = urlPath\n .split('/')\n .filter((part) => part && part !== 'doc')\n .map((part) => part.replace('.md', '')) // Clean extension for joining\n .join('_');\n }\n\n // Ensure it ends with .md\n fileName = fileName ? `${fileName}.md` : 'index.md';\n const localRefPath = `references/${fileName}`;\n\n return {\n url,\n localRefPath,\n fileName,\n content,\n success: true,\n };\n } catch (error) {\n console.warn(\n `Warning: Failed to download ref ${url} for skill ${skill}`,\n error\n );\n return { url, success: false };\n }\n });\n\n const results = await Promise.all(downloadPromises);\n\n // Process results: Write files and update content string\n for (const res of results) {\n if (res.success && res.fileName && res.content && res.localRefPath) {\n // Write the reference file\n await fs.writeFile(\n path.join(referenceDir, res.fileName),\n res.content,\n 'utf-8'\n );\n\n // Update the main content to point to local file\n updatedSkillContent = updatedSkillContent.replaceAll(\n res.url,\n res.localRefPath\n );\n }\n }\n\n // Write the main SKILL.md\n const filePath = path.join(skillDir, 'SKILL.md');\n await fs.writeFile(filePath, updatedSkillContent, 'utf-8');\n createdSkills.push(`${skillName}/SKILL.md`);\n }\n\n if (createdSkills.length === 0) {\n return `No skill files were created. Check your asset paths.`;\n }\n\n return `${v} Created ${createdSkills.length} skills in ${skillsBaseDir}`;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["readAsset","path","fs","v"],"sources":["../../../src/installSkills/index.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { readAsset } from 'utils:asset';\nimport { v } from '@intlayer/config/logger';\nimport { getMarkdownMetadata } from '@intlayer/core/markdown';\n\n/**\n * Metadata for each available documentation skill.\n */\nexport const SKILLS_METADATA = {\n Config: 'Intlayer configuration documentation',\n Content: 'Reference for all Intlayer content node types (t, enu, etc.)',\n Usage: 'How to use Intlayer in your project',\n CLI: 'Intlayer CLI commands and usage',\n Compiler:\n 'Intlayer Compiler setup and usage for automatic content extraction without .content files',\n RemoteContent: 'How to use Intlayer with Remote/CMS/Server-side content',\n NextJS: 'Next.js-specific usage (Server & Client components)',\n React: 'React-specific syntax and hooks usage',\n Vue: 'Vue-specific composables and syntax',\n Svelte: 'Svelte-specific stores and syntax',\n Angular: 'Angular-specific syntax and Injectable Function usage',\n Preact: 'Preact-specific syntax and hooks usage',\n Solid:\n 'Integrates Intlayer internationalization with SolidJS components. Use when the user asks to \"setup SolidJS i18n\", use the \"useIntlayer\" hook in Solid, or manage locales in a SolidJS application.',\n Astro: 'Astro-specific usage and getIntlayer',\n} as const;\n\nexport type Skill = keyof typeof SKILLS_METADATA;\n\nexport const SKILLS = Object.keys(SKILLS_METADATA) as Skill[];\n\nexport const getInitialSkills = (\n deps: Record<string, string>\n): (keyof typeof SKILLS_METADATA)[] => {\n const skills: (keyof typeof SKILLS_METADATA)[] = [\n 'Usage',\n 'Content',\n 'Config',\n 'CLI',\n 'Compiler',\n ];\n\n if (deps.next) skills.push('NextJS');\n if (deps.react || !deps.next) skills.push('React');\n if (deps.preact) skills.push('Preact');\n if (deps['solid-js']) skills.push('Solid');\n if (deps.vue || deps.nuxt) skills.push('Vue');\n if (deps.svelte || deps['@sveltejs/kit']) skills.push('Svelte');\n if (deps.astro) skills.push('Astro');\n\n return skills;\n};\n\nexport interface PlatformMetadata {\n label: string;\n dir: string;\n check?: () => boolean;\n}\n\n/**\n * Metadata and configuration for each supported platform.\n */\nexport const PLATFORMS_METADATA: Record<string, PlatformMetadata> = {\n Cursor: {\n label: 'Cursor',\n dir: '.cursor/skills',\n check: () =>\n process.env.CURSOR === 'true' || process.env.TERM_PROGRAM === 'cursor',\n },\n Windsurf: {\n label: 'Windsurf',\n dir: '.windsurf/skills',\n check: () =>\n process.env.WINDSURF === 'true' ||\n process.env.TERM_PROGRAM === 'windsurf',\n },\n Trae: {\n label: 'Trae',\n dir: '.trae/skills',\n check: () =>\n process.env.TRAE === 'true' || process.env.TERM_PROGRAM === 'trae',\n },\n TraeCN: {\n label: 'Trae CN',\n dir: '.trae/skills',\n check: () => process.env.TRAE_CN === 'true',\n },\n VSCode: {\n label: 'VS Code',\n dir: '.vscode/skills',\n check: () =>\n process.env.VSCODE === 'true' || process.env.TERM_PROGRAM === 'vscode',\n },\n OpenCode: {\n label: 'OpenCode',\n dir: '.opencode/skills',\n check: () => process.env.OPENCODE === 'true',\n },\n Claude: {\n label: 'Claude Code',\n dir: '.claude/skills',\n check: () => process.env.CLAUDE === 'true',\n },\n GitHub: {\n label: 'GitHub Copilot Workspace',\n dir: '.github/skills',\n check: () =>\n process.env.GITHUB_ACTIONS === 'true' || !!process.env.GITHUB_WORKSPACE,\n },\n Antigravity: {\n label: 'Antigravity',\n dir: '.agent/skills',\n },\n Augment: {\n label: 'Augment',\n dir: '.augment/skills',\n },\n OpenClaw: {\n label: 'OpenClaw',\n dir: 'skills',\n },\n Cline: {\n label: 'Cline',\n dir: '.cline/skills',\n },\n CodeBuddy: {\n label: 'CodeBuddy',\n dir: '.codebuddy/skills',\n },\n CommandCode: {\n label: 'Command Code',\n dir: '.commandcode/skills',\n },\n Continue: {\n label: 'Continue',\n dir: '.continue/skills',\n },\n Crush: {\n label: 'Crush',\n dir: '.crush/skills',\n },\n Droid: {\n label: 'Droid',\n dir: '.factory/skills',\n },\n Goose: {\n label: 'Goose',\n dir: '.goose/skills',\n },\n IFlow: {\n label: 'iFlow CLI',\n dir: '.iflow/skills',\n },\n Junie: {\n label: 'Junie',\n dir: '.junie/skills',\n },\n KiloCode: {\n label: 'Kilo Code',\n dir: '.kilocode/skills',\n },\n Kiro: {\n label: 'Kiro CLI',\n dir: '.kiro/skills',\n },\n Kode: {\n label: 'Kode',\n dir: '.kode/skills',\n },\n MCPJam: {\n label: 'MCPJam',\n dir: '.mcpjam/skills',\n },\n MistralVibe: {\n label: 'Mistral Vibe',\n dir: '.vibe/skills',\n },\n Mux: {\n label: 'Mux',\n dir: '.mux/skills',\n },\n OpenHands: {\n label: 'OpenHands',\n dir: '.openhands/skills',\n },\n Pi: {\n label: 'Pi',\n dir: '.pi/skills',\n },\n Qoder: {\n label: 'Qoder',\n dir: '.qoder/skills',\n },\n Qwen: {\n label: 'Qwen Code',\n dir: '.qwen/skills',\n },\n RooCode: {\n label: 'Roo Code',\n dir: '.roo/skills',\n },\n Zencoder: {\n label: 'Zencoder',\n dir: '.zencoder/skills',\n },\n Neovate: {\n label: 'Neovate',\n dir: '.neovate/skills',\n },\n Pochi: {\n label: 'Pochi',\n dir: '.pochi/skills',\n },\n Other: {\n label: 'Other',\n dir: 'skills',\n },\n} as const;\n\nexport type Platform = keyof typeof PLATFORMS_METADATA;\n\nexport const PLATFORMS = Object.keys(PLATFORMS_METADATA) as Platform[];\n\n/**\n * Maps specific skill keys to special filenames if they differ from standard snake_case.\n */\nconst SKILL_FILENAME_MAP: Partial<Record<Skill, string>> = {};\n\n/**\n * Helper to convert CamelCase to kebab-case for directory naming\n */\nconst camelToKebabCase = (str: string) =>\n str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();\n\n/**\n * Reads the raw markdown content for a specific skill from the assets folder.\n */\nconst getSkillContent = (skill: Skill): string => {\n const baseName = SKILL_FILENAME_MAP[skill] ?? camelToKebabCase(skill);\n const fileName = `./skills/${baseName}.md`;\n\n try {\n return readAsset(fileName);\n } catch {\n console.warn(\n `Warning: Could not read asset for skill: ${skill} at ${fileName}`\n );\n return '';\n }\n};\n\n/**\n * Reads the licence content from the assets folder.\n */\nconst getLicenceContent = (): string => {\n try {\n return readAsset('./LICENCE.md');\n } catch {\n console.warn('Warning: Could not read LICENCE.md asset');\n return '';\n }\n};\n\nconst ALLOWED_FETCH_HOSTS = new Set(['intlayer.org']);\n\n/**\n * Fetches the content of a URL using native fetch.\n */\nconst fetchUrlContent = async (url: string): Promise<string> => {\n const parsed = new URL(url);\n if (\n parsed.protocol !== 'https:' ||\n !ALLOWED_FETCH_HOSTS.has(parsed.hostname)\n ) {\n throw new Error(`Blocked fetch to disallowed host: ${parsed.hostname}`);\n }\n const response = await fetch(url, { redirect: 'error' });\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}: ${response.statusText}`);\n }\n return response.text();\n};\n\n/**\n * Installs skills using the \"Agent Skills\" directory standard.\n * Standard: <PROJECT_ROOT>/<CONFIG_DIR>/skills/<SKILL_NAME>/SKILL.md\n */\nexport const installSkills = async (\n projectRoot: string,\n platform: Platform,\n skills: Skill[]\n): Promise<string> => {\n // Determine destination directory\n const relativeDir = PLATFORMS_METADATA[platform].dir ?? 'skills';\n const skillsBaseDir = path.join(projectRoot, relativeDir);\n\n // Ensure the base directory exists\n await fs.mkdir(skillsBaseDir, { recursive: true });\n\n const createdSkills: string[] = [];\n const licenceContent = getLicenceContent();\n\n for (const skill of skills) {\n const baseName = SKILL_FILENAME_MAP[skill] ?? camelToKebabCase(skill);\n const skillName = `intlayer-${baseName}`;\n const skillContent = getSkillContent(skill);\n\n if (!skillContent) continue;\n\n // Extract unique URLs\n const urls = Array.from(\n new Set(skillContent.match(/https:\\/\\/intlayer\\.org\\/[^\\s)]+\\.md/g) || [])\n );\n\n // Agent Standard: .../skills/<skill-name>/SKILL.md\n const skillDir = path.join(skillsBaseDir, skillName);\n const referenceDir = path.join(skillDir, 'references');\n\n // Ensure directories exist\n await fs.mkdir(referenceDir, { recursive: true });\n\n // Write License\n if (licenceContent) {\n await fs.writeFile(\n path.join(skillDir, 'LICENCE.md'),\n licenceContent,\n 'utf-8'\n );\n }\n\n let updatedSkillContent = skillContent;\n\n // Parallel download of references\n const downloadPromises = urls.map(async (url) => {\n try {\n const content = await fetchUrlContent(url);\n const metadata = getMarkdownMetadata<{ slugs?: string[] }>(content);\n\n let fileName = '';\n\n // Determine filename from slugs or URL path\n if (Array.isArray(metadata.slugs)) {\n fileName = metadata.slugs.filter((slug) => slug !== 'doc').join('_');\n } else {\n const urlPath = new URL(url).pathname;\n fileName = urlPath\n .split('/')\n .filter((part) => part && part !== 'doc')\n .map((part) => part.replace('.md', '')) // Clean extension for joining\n .join('_');\n }\n\n // Ensure it ends with .md\n fileName = fileName ? `${fileName}.md` : 'index.md';\n const localRefPath = `references/${fileName}`;\n\n return {\n url,\n localRefPath,\n fileName,\n content,\n success: true,\n };\n } catch (error) {\n console.warn(\n `Warning: Failed to download ref ${url} for skill ${skill}`,\n error\n );\n return { url, success: false };\n }\n });\n\n const results = await Promise.all(downloadPromises);\n\n // Process results: Write files and update content string\n for (const res of results) {\n if (res.success && res.fileName && res.content && res.localRefPath) {\n // Write the reference file\n await fs.writeFile(\n path.join(referenceDir, res.fileName),\n res.content,\n 'utf-8'\n );\n\n // Update the main content to point to local file\n updatedSkillContent = updatedSkillContent.replaceAll(\n res.url,\n res.localRefPath\n );\n }\n }\n\n // Write the main SKILL.md\n const filePath = path.join(skillDir, 'SKILL.md');\n await fs.writeFile(filePath, updatedSkillContent, 'utf-8');\n createdSkills.push(`${skillName}/SKILL.md`);\n }\n\n if (createdSkills.length === 0) {\n return `No skill files were created. Check your asset paths.`;\n }\n\n return `${v} Created ${createdSkills.length} skills in ${skillsBaseDir}`;\n};\n"],"mappings":"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"}
|
package/dist/cjs/watcher.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
const require_utils_formatter = require('./utils/formatter.cjs');
|
|
3
4
|
const require_handleContentDeclarationFileChange = require('./handleContentDeclarationFileChange.cjs');
|
|
4
5
|
const require_handleContentDeclarationFileMoved = require('./handleContentDeclarationFileMoved.cjs');
|
|
5
6
|
const require_handleAdditionalContentDeclarationFile = require('./handleAdditionalContentDeclarationFile.cjs');
|
|
@@ -14,6 +15,7 @@ let node_fs = require("node:fs");
|
|
|
14
15
|
let _intlayer_config_utils = require("@intlayer/config/utils");
|
|
15
16
|
let _intlayer_config_colors = require("@intlayer/config/colors");
|
|
16
17
|
_intlayer_config_colors = require_runtime.__toESM(_intlayer_config_colors);
|
|
18
|
+
let node_url = require("node:url");
|
|
17
19
|
|
|
18
20
|
//#region src/watcher.ts
|
|
19
21
|
const pendingUnlinks = /* @__PURE__ */ new Map();
|
|
@@ -57,6 +59,58 @@ const watch = async (options) => {
|
|
|
57
59
|
], { level: "warn" });
|
|
58
60
|
const excludedSegments = excludedPath.map((segment) => segment.replace(/^\*\*\//, "").replace(/\/\*\*$/, ""));
|
|
59
61
|
const normalizedConfigPath = configurationFilePath ? (0, _intlayer_config_utils.normalizePath)(configurationFilePath) : null;
|
|
62
|
+
const { mainDir, baseDir } = configuration.system;
|
|
63
|
+
const normalizedIntlayerDir = (0, _intlayer_config_utils.normalizePath)((0, node_path.dirname)(mainDir));
|
|
64
|
+
if ((0, node_fs.existsSync)(mainDir)) chokidarWatch(mainDir, {
|
|
65
|
+
persistent: isWatchMode,
|
|
66
|
+
ignoreInitial: true,
|
|
67
|
+
depth: 0
|
|
68
|
+
}).on("change", async (filePath) => {
|
|
69
|
+
if (isProcessing) return;
|
|
70
|
+
processEvent(async () => {
|
|
71
|
+
(0, _intlayer_config_utils.clearModuleCache)(filePath);
|
|
72
|
+
try {
|
|
73
|
+
await import(`${(0, node_url.pathToFileURL)(filePath).href}?update=${Date.now()}`);
|
|
74
|
+
} catch {
|
|
75
|
+
appLogger(`Entry point ${(0, node_path.basename)(filePath)} failed to load, running clean rebuild...`, { level: "warn" });
|
|
76
|
+
await require_prepareIntlayer.prepareIntlayer(configuration, {
|
|
77
|
+
clean: true,
|
|
78
|
+
forceRun: true
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}).on("unlink", async (filePath) => {
|
|
83
|
+
if (isProcessing) return;
|
|
84
|
+
processEvent(async () => {
|
|
85
|
+
appLogger([
|
|
86
|
+
"Entry point",
|
|
87
|
+
require_utils_formatter.formatPath((0, node_path.basename)(filePath)),
|
|
88
|
+
"was removed, running clean rebuild..."
|
|
89
|
+
], { level: "warn" });
|
|
90
|
+
await require_prepareIntlayer.prepareIntlayer(configuration, {
|
|
91
|
+
clean: true,
|
|
92
|
+
forceRun: true
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
chokidarWatch(baseDir, {
|
|
97
|
+
persistent: isWatchMode,
|
|
98
|
+
ignoreInitial: true,
|
|
99
|
+
depth: 0,
|
|
100
|
+
ignored: (filePath) => {
|
|
101
|
+
const path = (0, _intlayer_config_utils.normalizePath)(filePath);
|
|
102
|
+
return path !== (0, _intlayer_config_utils.normalizePath)(baseDir) && path !== normalizedIntlayerDir;
|
|
103
|
+
}
|
|
104
|
+
}).on("unlinkDir", async (dirPath) => {
|
|
105
|
+
if (isProcessing) return;
|
|
106
|
+
if ((0, _intlayer_config_utils.normalizePath)(dirPath) === normalizedIntlayerDir) {
|
|
107
|
+
appLogger([require_utils_formatter.formatPath(".intlayer"), "directory removed, running clean rebuild..."]);
|
|
108
|
+
processEvent(() => require_prepareIntlayer.prepareIntlayer(configuration, {
|
|
109
|
+
clean: true,
|
|
110
|
+
forceRun: true
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
});
|
|
60
114
|
return chokidarWatch(pathsToWatch, {
|
|
61
115
|
persistent: isWatchMode,
|
|
62
116
|
ignoreInitial: true,
|
package/dist/cjs/watcher.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watcher.cjs","names":["existsSync","ANSIColor","handleContentDeclarationFileMoved","writeContentDeclaration","handleAdditionalContentDeclarationFile","prepareIntlayer","handleContentDeclarationFileChange","handleUnlinkedContentDeclarationFile"],"sources":["../../src/watcher.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { basename } from 'node:path';\nimport * as ANSIColor from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n getConfigurationAndFilePath,\n} from '@intlayer/config/node';\nimport {\n clearAllCache,\n clearDiskCacheMemory,\n clearModuleCache,\n normalizePath,\n} from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { ChokidarOptions } from 'chokidar';\nimport { handleAdditionalContentDeclarationFile } from './handleAdditionalContentDeclarationFile';\nimport { handleContentDeclarationFileChange } from './handleContentDeclarationFileChange';\nimport { handleContentDeclarationFileMoved } from './handleContentDeclarationFileMoved';\nimport { handleUnlinkedContentDeclarationFile } from './handleUnlinkedContentDeclarationFile';\nimport { prepareIntlayer } from './prepareIntlayer';\nimport { writeContentDeclaration } from './writeContentDeclaration';\n\n// Map to track files that were recently unlinked: oldPath -> { timer, timestamp }\nconst pendingUnlinks = new Map<\n string,\n { timer: NodeJS.Timeout; oldPath: string }\n>();\n\n// Array-based sequential task queue — no Promise chain accumulation, no race conditions\nconst taskQueue: (() => Promise<void>)[] = [];\nlet isProcessing = false;\n\nconst processQueue = async () => {\n if (isProcessing) return;\n isProcessing = true;\n while (taskQueue.length > 0) {\n const task = taskQueue.shift()!;\n try {\n await task();\n } catch (error) {\n console.error(error);\n }\n }\n isProcessing = false;\n};\n\nconst processEvent = (task: () => Promise<void>) => {\n taskQueue.push(task);\n processQueue();\n};\n\ntype WatchOptions = ChokidarOptions & {\n configuration?: IntlayerConfig;\n configOptions?: GetConfigurationOptions;\n skipPrepare?: boolean;\n};\n\n// Initialize chokidar watcher (non-persistent)\nexport const watch = async (options?: WatchOptions) => {\n const { watch: chokidarWatch } = await import('chokidar');\n const configResult = getConfigurationAndFilePath(options?.configOptions);\n const configurationFilePath = configResult.configurationFilePath;\n let configuration: IntlayerConfig =\n options?.configuration ?? configResult.configuration;\n const appLogger = getAppLogger(configuration);\n\n const {\n watch: isWatchMode,\n fileExtensions,\n contentDir,\n excludedPath,\n } = configuration.content;\n\n // chokidar v5 dropped glob support — use fs to resolve dirs, filter extensions via ignored\n const pathsToWatch = [\n ...contentDir.map((dir) => normalizePath(dir)).filter(existsSync),\n ...(configurationFilePath ? [configurationFilePath] : []),\n ];\n\n if (!configuration.content.watch) return;\n\n appLogger('Watching Intlayer content declarations');\n\n if (configuration.build.optimize === true) {\n appLogger(\n [\n `Build optimization is forced to ${colorize('true', ANSIColor.GREY)}, but watching is enabled too.`,\n 'It may lead to dev mode performance degradation as well as import errors.',\n 'Its recommended to keep the',\n colorize('`build.optimized`', ANSIColor.BLUE),\n 'option',\n colorize('undefined', ANSIColor.GREY),\n 'to get the best dev mode experience',\n ],\n {\n level: 'warn',\n }\n );\n }\n\n // Strip glob markers from excludedPath entries to get plain segments (e.g. 'node_modules')\n const excludedSegments = excludedPath.map((segment) =>\n segment.replace(/^\\*\\*\\//, '').replace(/\\/\\*\\*$/, '')\n );\n\n const normalizedConfigPath = configurationFilePath\n ? normalizePath(configurationFilePath)\n : null;\n\n return chokidarWatch(pathsToWatch, {\n persistent: isWatchMode, // Make the watcher persistent\n ignoreInitial: true, // Process existing files\n awaitWriteFinish: {\n stabilityThreshold: 1000,\n pollInterval: 100,\n },\n ignored: (filePath: string, stats?: import('node:fs').Stats) => {\n const path = normalizePath(filePath);\n\n if (normalizedConfigPath && path === normalizedConfigPath) return false;\n\n if (excludedSegments.some((segment) => path.includes(`/${segment}`)))\n return true;\n\n if (stats?.isFile()) {\n return !fileExtensions.some((extension) => path.endsWith(extension));\n }\n\n return false;\n },\n ...options,\n })\n .on('add', async (filePath) => {\n const fileName = basename(filePath);\n let isMove = false;\n\n // Check if this Add corresponds to a pending Unlink (Move/Rename detection)\n // Heuristic:\n // - Priority A: Exact basename match (Moved to different folder)\n // - Priority B: Single entry in pendingUnlinks (Renamed file)\n let matchedOldPath: string | undefined;\n\n // Search for basename match\n for (const [oldPath] of pendingUnlinks) {\n if (basename(oldPath) === fileName) {\n matchedOldPath = oldPath;\n break;\n }\n }\n\n // If no basename match, but exactly one file was recently unlinked, assume it's a rename\n if (!matchedOldPath && pendingUnlinks.size === 1) {\n matchedOldPath = pendingUnlinks.keys().next().value;\n }\n\n if (matchedOldPath) {\n // It is a move! Cancel the unlink handler\n const pending = pendingUnlinks.get(matchedOldPath);\n if (pending) {\n clearTimeout(pending.timer);\n pendingUnlinks.delete(matchedOldPath);\n }\n\n isMove = true;\n appLogger(`File moved from ${matchedOldPath} to ${filePath}`);\n }\n\n processEvent(async () => {\n if (isMove && matchedOldPath) {\n await handleContentDeclarationFileMoved(\n matchedOldPath,\n filePath,\n configuration\n );\n } else {\n const fileContent = await readFile(filePath, 'utf-8');\n const isEmpty = fileContent === '';\n\n // Fill template content declaration file if it is empty\n if (isEmpty) {\n const extensionPattern = fileExtensions\n .map((ext) => ext.replace(/\\./g, '\\\\.'))\n .join('|');\n const name = fileName.replace(\n new RegExp(`(${extensionPattern})$`),\n ''\n );\n\n await writeContentDeclaration(\n {\n key: name,\n content: {},\n filePath,\n },\n configuration\n );\n }\n\n await handleAdditionalContentDeclarationFile(filePath, configuration);\n }\n });\n })\n .on('change', async (filePath) =>\n processEvent(async () => {\n if (configurationFilePath && filePath === configurationFilePath) {\n appLogger('Configuration file changed, repreparing Intlayer');\n\n clearModuleCache(configurationFilePath);\n clearAllCache();\n\n const { configuration: newConfiguration } =\n getConfigurationAndFilePath(options?.configOptions);\n\n configuration = options?.configuration ?? newConfiguration;\n\n await prepareIntlayer(configuration, { clean: false });\n } else {\n // Clear module cache for the changed file to avoid stale require() results\n clearModuleCache(filePath);\n // Evict in-memory caches so loadContentDeclaration picks up fresh content\n clearAllCache();\n clearDiskCacheMemory();\n await handleContentDeclarationFileChange(filePath, configuration);\n }\n })\n )\n .on('unlink', async (filePath) => {\n // Delay unlink processing to see if an 'add' event occurs (indicating a move)\n const timer = setTimeout(async () => {\n // If timer fires, the file was genuinely removed\n pendingUnlinks.delete(filePath);\n processEvent(async () =>\n handleUnlinkedContentDeclarationFile(filePath, configuration)\n );\n }, 200); // 200ms window to catch the 'add' event\n\n pendingUnlinks.set(filePath, { timer, oldPath: filePath });\n })\n .on('error', async (error) => {\n appLogger(`Watcher error: ${error}`, {\n level: 'error',\n });\n\n appLogger('Restarting watcher');\n\n await prepareIntlayer(configuration);\n });\n};\n\nexport const buildAndWatchIntlayer = async (options?: WatchOptions) => {\n const { skipPrepare, ...rest } = options ?? {};\n const configuration =\n options?.configuration ?? getConfiguration(options?.configOptions);\n\n if (!skipPrepare) {\n await prepareIntlayer(configuration, { forceRun: true });\n }\n\n if (configuration.content.watch || options?.persistent) {\n await watch({ ...rest, configuration });\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"watcher.cjs","names":["existsSync","ANSIColor","prepareIntlayer","formatPath","handleContentDeclarationFileMoved","writeContentDeclaration","handleAdditionalContentDeclarationFile","handleContentDeclarationFileChange","handleUnlinkedContentDeclarationFile"],"sources":["../../src/watcher.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { basename, dirname } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport * as ANSIColor from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n getConfigurationAndFilePath,\n} from '@intlayer/config/node';\nimport {\n clearAllCache,\n clearDiskCacheMemory,\n clearModuleCache,\n normalizePath,\n} from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { ChokidarOptions } from 'chokidar';\nimport { handleAdditionalContentDeclarationFile } from './handleAdditionalContentDeclarationFile';\nimport { handleContentDeclarationFileChange } from './handleContentDeclarationFileChange';\nimport { handleContentDeclarationFileMoved } from './handleContentDeclarationFileMoved';\nimport { handleUnlinkedContentDeclarationFile } from './handleUnlinkedContentDeclarationFile';\nimport { prepareIntlayer } from './prepareIntlayer';\nimport { formatPath } from './utils';\nimport { writeContentDeclaration } from './writeContentDeclaration';\n\n// Map to track files that were recently unlinked: oldPath -> { timer, timestamp }\nconst pendingUnlinks = new Map<\n string,\n { timer: NodeJS.Timeout; oldPath: string }\n>();\n\n// Array-based sequential task queue — no Promise chain accumulation, no race conditions\nconst taskQueue: (() => Promise<void>)[] = [];\nlet isProcessing = false;\n\nconst processQueue = async () => {\n if (isProcessing) return;\n isProcessing = true;\n while (taskQueue.length > 0) {\n const task = taskQueue.shift()!;\n try {\n await task();\n } catch (error) {\n console.error(error);\n }\n }\n isProcessing = false;\n};\n\nconst processEvent = (task: () => Promise<void>) => {\n taskQueue.push(task);\n processQueue();\n};\n\ntype WatchOptions = ChokidarOptions & {\n configuration?: IntlayerConfig;\n configOptions?: GetConfigurationOptions;\n skipPrepare?: boolean;\n};\n\n// Initialize chokidar watcher (non-persistent)\nexport const watch = async (options?: WatchOptions) => {\n const { watch: chokidarWatch } = await import('chokidar');\n const configResult = getConfigurationAndFilePath(options?.configOptions);\n const configurationFilePath = configResult.configurationFilePath;\n let configuration: IntlayerConfig =\n options?.configuration ?? configResult.configuration;\n const appLogger = getAppLogger(configuration);\n\n const {\n watch: isWatchMode,\n fileExtensions,\n contentDir,\n excludedPath,\n } = configuration.content;\n\n // chokidar v5 dropped glob support — use fs to resolve dirs, filter extensions via ignored\n const pathsToWatch = [\n ...contentDir.map((dir) => normalizePath(dir)).filter(existsSync),\n ...(configurationFilePath ? [configurationFilePath] : []),\n ];\n\n if (!configuration.content.watch) return;\n\n appLogger('Watching Intlayer content declarations');\n\n if (configuration.build.optimize === true) {\n appLogger(\n [\n `Build optimization is forced to ${colorize('true', ANSIColor.GREY)}, but watching is enabled too.`,\n 'It may lead to dev mode performance degradation as well as import errors.',\n 'Its recommended to keep the',\n colorize('`build.optimized`', ANSIColor.BLUE),\n 'option',\n colorize('undefined', ANSIColor.GREY),\n 'to get the best dev mode experience',\n ],\n {\n level: 'warn',\n }\n );\n }\n\n // Strip glob markers from excludedPath entries to get plain segments (e.g. 'node_modules')\n const excludedSegments = excludedPath.map((segment) =>\n segment.replace(/^\\*\\*\\//, '').replace(/\\/\\*\\*$/, '')\n );\n\n const normalizedConfigPath = configurationFilePath\n ? normalizePath(configurationFilePath)\n : null;\n\n const { mainDir, baseDir } = configuration.system;\n const normalizedIntlayerDir = normalizePath(dirname(mainDir));\n\n // Watch mainDir to detect broken or missing entry point files\n if (existsSync(mainDir)) {\n chokidarWatch(mainDir, {\n persistent: isWatchMode,\n ignoreInitial: true,\n depth: 0,\n })\n .on('change', async (filePath) => {\n if (isProcessing) return;\n\n processEvent(async () => {\n clearModuleCache(filePath);\n try {\n // Convert absolute path to a valid file:// URL\n const fileUrl = pathToFileURL(filePath).href;\n\n // Append a timestamp to bypass the ESM cache\n await import(`${fileUrl}?update=${Date.now()}`);\n } catch {\n appLogger(\n `Entry point ${basename(filePath)} failed to load, running clean rebuild...`,\n { level: 'warn' }\n );\n await prepareIntlayer(configuration, {\n clean: true,\n forceRun: true,\n });\n }\n });\n })\n .on('unlink', async (filePath) => {\n if (isProcessing) return;\n\n processEvent(async () => {\n appLogger(\n [\n 'Entry point',\n formatPath(basename(filePath)),\n 'was removed, running clean rebuild...',\n ],\n { level: 'warn' }\n );\n await prepareIntlayer(configuration, { clean: true, forceRun: true });\n });\n });\n }\n\n // Watch baseDir at depth 0 to detect the entire .intlayer folder being removed\n chokidarWatch(baseDir, {\n persistent: isWatchMode,\n ignoreInitial: true,\n depth: 0,\n ignored: (filePath: string) => {\n const path = normalizePath(filePath);\n return path !== normalizePath(baseDir) && path !== normalizedIntlayerDir;\n },\n }).on('unlinkDir', async (dirPath) => {\n if (isProcessing) return;\n\n if (normalizePath(dirPath) === normalizedIntlayerDir) {\n appLogger([\n formatPath('.intlayer'),\n 'directory removed, running clean rebuild...',\n ]);\n\n processEvent(() =>\n prepareIntlayer(configuration, { clean: true, forceRun: true })\n );\n }\n });\n\n return chokidarWatch(pathsToWatch, {\n persistent: isWatchMode, // Make the watcher persistent\n ignoreInitial: true, // Process existing files\n awaitWriteFinish: {\n stabilityThreshold: 1000,\n pollInterval: 100,\n },\n ignored: (filePath: string, stats?: import('node:fs').Stats) => {\n const path = normalizePath(filePath);\n\n if (normalizedConfigPath && path === normalizedConfigPath) return false;\n\n if (excludedSegments.some((segment) => path.includes(`/${segment}`)))\n return true;\n\n if (stats?.isFile()) {\n return !fileExtensions.some((extension) => path.endsWith(extension));\n }\n\n return false;\n },\n ...options,\n })\n .on('add', async (filePath) => {\n const fileName = basename(filePath);\n let isMove = false;\n\n // Check if this Add corresponds to a pending Unlink (Move/Rename detection)\n // Heuristic:\n // - Priority A: Exact basename match (Moved to different folder)\n // - Priority B: Single entry in pendingUnlinks (Renamed file)\n let matchedOldPath: string | undefined;\n\n // Search for basename match\n for (const [oldPath] of pendingUnlinks) {\n if (basename(oldPath) === fileName) {\n matchedOldPath = oldPath;\n break;\n }\n }\n\n // If no basename match, but exactly one file was recently unlinked, assume it's a rename\n if (!matchedOldPath && pendingUnlinks.size === 1) {\n matchedOldPath = pendingUnlinks.keys().next().value;\n }\n\n if (matchedOldPath) {\n // It is a move! Cancel the unlink handler\n const pending = pendingUnlinks.get(matchedOldPath);\n if (pending) {\n clearTimeout(pending.timer);\n pendingUnlinks.delete(matchedOldPath);\n }\n\n isMove = true;\n appLogger(`File moved from ${matchedOldPath} to ${filePath}`);\n }\n\n processEvent(async () => {\n if (isMove && matchedOldPath) {\n await handleContentDeclarationFileMoved(\n matchedOldPath,\n filePath,\n configuration\n );\n } else {\n const fileContent = await readFile(filePath, 'utf-8');\n const isEmpty = fileContent === '';\n\n // Fill template content declaration file if it is empty\n if (isEmpty) {\n const extensionPattern = fileExtensions\n .map((ext) => ext.replace(/\\./g, '\\\\.'))\n .join('|');\n const name = fileName.replace(\n new RegExp(`(${extensionPattern})$`),\n ''\n );\n\n await writeContentDeclaration(\n {\n key: name,\n content: {},\n filePath,\n },\n configuration\n );\n }\n\n await handleAdditionalContentDeclarationFile(filePath, configuration);\n }\n });\n })\n .on('change', async (filePath) =>\n processEvent(async () => {\n if (configurationFilePath && filePath === configurationFilePath) {\n appLogger('Configuration file changed, repreparing Intlayer');\n\n clearModuleCache(configurationFilePath);\n clearAllCache();\n\n const { configuration: newConfiguration } =\n getConfigurationAndFilePath(options?.configOptions);\n\n configuration = options?.configuration ?? newConfiguration;\n\n await prepareIntlayer(configuration, { clean: false });\n } else {\n // Clear module cache for the changed file to avoid stale require() results\n clearModuleCache(filePath);\n // Evict in-memory caches so loadContentDeclaration picks up fresh content\n clearAllCache();\n clearDiskCacheMemory();\n await handleContentDeclarationFileChange(filePath, configuration);\n }\n })\n )\n .on('unlink', async (filePath) => {\n // Delay unlink processing to see if an 'add' event occurs (indicating a move)\n const timer = setTimeout(async () => {\n // If timer fires, the file was genuinely removed\n pendingUnlinks.delete(filePath);\n processEvent(async () =>\n handleUnlinkedContentDeclarationFile(filePath, configuration)\n );\n }, 200); // 200ms window to catch the 'add' event\n\n pendingUnlinks.set(filePath, { timer, oldPath: filePath });\n })\n .on('error', async (error) => {\n appLogger(`Watcher error: ${error}`, {\n level: 'error',\n });\n\n appLogger('Restarting watcher');\n\n await prepareIntlayer(configuration);\n });\n};\n\nexport const buildAndWatchIntlayer = async (options?: WatchOptions) => {\n const { skipPrepare, ...rest } = options ?? {};\n const configuration =\n options?.configuration ?? getConfiguration(options?.configOptions);\n\n if (!skipPrepare) {\n await prepareIntlayer(configuration, { forceRun: true });\n }\n\n if (configuration.content.watch || options?.persistent) {\n await watch({ ...rest, configuration });\n }\n};\n"],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ import { writeJsonIfChanged } from "../writeJsonIfChanged.mjs";
|
|
|
4
4
|
import { mkdir } from "node:fs/promises";
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
import { colorizePath } from "@intlayer/config/logger";
|
|
7
|
+
import { assertPathWithin } from "@intlayer/config/utils";
|
|
7
8
|
import { OUTPUT_FORMAT } from "@intlayer/config/defaultValues";
|
|
8
9
|
import { getPerLocaleDictionary } from "@intlayer/core/plugins";
|
|
9
10
|
|
|
@@ -43,6 +44,7 @@ const writeDynamicDictionary = async (mergedDictionaries, configuration, formats
|
|
|
43
44
|
if (key === "undefined") return;
|
|
44
45
|
const localizedDictionariesPathsRecord = {};
|
|
45
46
|
const keyDir = resolve(dictDir, key);
|
|
47
|
+
assertPathWithin(keyDir, dictDir);
|
|
46
48
|
await mkdir(keyDir, { recursive: true });
|
|
47
49
|
await parallelize(locales, async (locale) => {
|
|
48
50
|
const localizedDictionary = getPerLocaleDictionary(dictionaryEntry.dictionary, locale, defaultLocale);
|
|
@@ -59,8 +61,10 @@ const writeDynamicDictionary = async (mergedDictionaries, configuration, formats
|
|
|
59
61
|
await parallelize(formats, async (format) => {
|
|
60
62
|
const extension = format === "cjs" ? "cjs" : "mjs";
|
|
61
63
|
const content = generateDictionaryEntryPoint(key, locales, format);
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
const dynEntryPath = resolve(dynamicDictionariesDir, `${key}.${extension}`);
|
|
65
|
+
assertPathWithin(dynEntryPath, dynamicDictionariesDir);
|
|
66
|
+
await writeFileIfChanged(dynEntryPath, content).catch((err) => {
|
|
67
|
+
console.error(`Error creating dynamic ${colorizePath(dynEntryPath)}:`, err);
|
|
64
68
|
});
|
|
65
69
|
});
|
|
66
70
|
});
|