@intlayer/cli 7.6.0-canary.1 → 8.0.0-canary.1

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/pull.cjs CHANGED
@@ -28,7 +28,7 @@ const pull = async (options) => {
28
28
  const location = unmergedDictionariesRecord[key]?.[0]?.location ?? config.dictionary?.location ?? "remote";
29
29
  return location === "remote" || location === "local&remote";
30
30
  }));
31
- const remoteDictionariesPath = (0, node_path.join)(config.content.mainDir, "remote_dictionaries.cjs");
31
+ const remoteDictionariesPath = (0, node_path.join)(config.system.mainDir, "remote_dictionaries.cjs");
32
32
  const requireFunction = config.build?.require ?? (0, _intlayer_config.getProjectRequire)();
33
33
  const remoteDictionariesRecord = (0, node_fs.existsSync)(remoteDictionariesPath) ? requireFunction(remoteDictionariesPath) : {};
34
34
  const entries = Object.entries(distantDictionariesUpdateTimeStamp);
@@ -80,6 +80,13 @@ const pull = async (options) => {
80
80
  if (isCached) sourceDictionary = remoteDictionariesRecord[statusObj.dictionaryKey];
81
81
  if (!sourceDictionary) sourceDictionary = (await intlayerAPI.dictionary.getDictionary(statusObj.dictionaryKey)).data;
82
82
  if (!sourceDictionary) throw new Error(`Dictionary ${statusObj.dictionaryKey} not found on remote`);
83
+ const localAndRemoteDictionary = unmergedDictionariesRecord[statusObj.dictionaryKey]?.find((d) => d.location === "local&remote");
84
+ if (localAndRemoteDictionary) sourceDictionary = {
85
+ ...sourceDictionary,
86
+ location: "local&remote",
87
+ filePath: localAndRemoteDictionary.filePath,
88
+ localId: localAndRemoteDictionary.localId
89
+ };
83
90
  const { status } = await (0, _intlayer_chokidar.writeContentDeclaration)(sourceDictionary, config, options);
84
91
  statusObj.status = status;
85
92
  logger.update([{
@@ -1 +1 @@
1
- {"version":3,"file":"pull.cjs","names":["checkCMSAuth","PullLogger","ANSIColors"],"sources":["../../src/pull.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n type DictionaryStatus,\n parallelize,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n getProjectRequire,\n} from '@intlayer/config';\nimport type { Dictionary } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { PullLogger, type PullStatus } from './push/pullLog';\nimport { checkCMSAuth } from './utils/checkAccess';\n\ntype PullOptions = {\n dictionaries?: string[];\n newDictionariesPath?: string;\n configOptions?: GetConfigurationOptions;\n};\n\ntype DictionariesStatus = {\n dictionaryKey: string;\n status: DictionaryStatus | 'pending' | 'fetching' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n/**\n * Fetch distant dictionaries and write them locally,\n * with progress indicators and concurrency control.\n */\nexport const pull = async (options?: PullOptions): Promise<void> => {\n const appLogger = getAppLogger(options?.configOptions?.override);\n\n try {\n const config = getConfiguration(options?.configOptions);\n\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n\n // Get remote update timestamps map\n const getDictionariesUpdateTimestampResult =\n await intlayerAPI.dictionary.getDictionariesUpdateTimestamp();\n\n if (!getDictionariesUpdateTimestampResult.data) {\n throw new Error('No distant dictionaries found');\n }\n\n let distantDictionariesUpdateTimeStamp: Record<string, any> =\n getDictionariesUpdateTimestampResult.data;\n\n // Optional filtering by requested dictionaries\n if (options?.dictionaries) {\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) =>\n options.dictionaries?.includes(key)\n )\n );\n }\n\n // Filter by location\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) => {\n const localDictionaries = unmergedDictionariesRecord[key];\n const location =\n localDictionaries?.[0]?.location ??\n config.dictionary?.location ??\n 'remote';\n\n return location === 'remote' || location === 'local&remote';\n })\n );\n\n // Load local cached remote dictionaries (if any)\n const remoteDictionariesPath = join(\n config.content.mainDir,\n 'remote_dictionaries.cjs'\n );\n const requireFunction = config.build?.require ?? getProjectRequire();\n const remoteDictionariesRecord: Record<string, any> = existsSync(\n remoteDictionariesPath\n )\n ? (requireFunction(remoteDictionariesPath) as any)\n : {};\n\n // Determine which keys need fetching by comparing updatedAt with local cache\n const entries = Object.entries(distantDictionariesUpdateTimeStamp);\n const keysToFetch = entries\n .filter(([key, remoteUpdatedAtValue]) => {\n if (!remoteUpdatedAtValue) return true;\n\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\n const local = (remoteDictionariesRecord as any)[key];\n if (!local) return true;\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n if (typeof localUpdatedAt !== 'number') return true;\n return remoteUpdatedAt > localUpdatedAt;\n })\n .map(([key]) => key);\n\n const cachedKeys = entries\n .filter(([key, remoteUpdatedAtValue]) => {\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\n const local = (remoteDictionariesRecord as any)[key];\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n return (\n typeof localUpdatedAt === 'number' &&\n typeof remoteUpdatedAt === 'number' &&\n localUpdatedAt >= remoteUpdatedAt\n );\n })\n .map(([key]) => key);\n\n // Check if dictionaries list is empty\n if (entries.length === 0) {\n appLogger('No dictionaries to fetch', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Fetching dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] = [\n ...cachedKeys.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'imported' as DictionaryStatus,\n })),\n ...keysToFetch.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'pending' as const,\n })),\n ];\n\n // Initialize aggregated logger\n const logger = new PullLogger();\n logger.update(\n dictionariesStatuses.map<PullStatus>((s) => ({\n dictionaryKey: s.dictionaryKey,\n status: s.status,\n }))\n );\n\n const successfullyFetchedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n const isCached =\n statusObj.status === 'imported' || statusObj.status === 'up-to-date';\n\n if (!isCached) {\n statusObj.status = 'fetching';\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'fetching' },\n ]);\n }\n\n try {\n let sourceDictionary: Dictionary | undefined;\n\n if (isCached) {\n sourceDictionary = remoteDictionariesRecord[\n statusObj.dictionaryKey\n ] as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n // Fetch the dictionary\n const getDictionaryResult =\n await intlayerAPI.dictionary.getDictionary(statusObj.dictionaryKey);\n\n sourceDictionary = getDictionaryResult.data as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n throw new Error(\n `Dictionary ${statusObj.dictionaryKey} not found on remote`\n );\n }\n\n // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n sourceDictionary,\n config,\n options\n );\n\n statusObj.status = status;\n logger.update([{ dictionaryKey: statusObj.dictionaryKey, status }]);\n\n successfullyFetchedDictionaries.push(sourceDictionary);\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error fetching dictionary ${statusObj.dictionaryKey}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with concurrency limit\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n // Per-dictionary summary\n const iconFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n case 'reimported in JSON':\n case 'new content file':\n return '✔';\n case 'error':\n return '✖';\n default:\n return '⏲';\n }\n };\n\n const colorFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n return ANSIColors.GREEN;\n case 'reimported in JSON':\n case 'new content file':\n return ANSIColors.YELLOW;\n case 'error':\n return ANSIColors.RED;\n default:\n return ANSIColors.BLUE;\n }\n };\n\n for (const s of dictionariesStatuses) {\n const icon = iconFor(s.status);\n const color = colorFor(s.status);\n appLogger(\n ` - ${s.dictionaryKey} ${ANSIColors.GREY}[${color}${icon} ${s.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\n\n // Output any error messages\n for (const statusObj of dictionariesStatuses) {\n if (statusObj.errorMessage) {\n appLogger(statusObj.errorMessage, {\n level: 'error',\n });\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"pull.cjs","names":["checkCMSAuth","PullLogger","ANSIColors"],"sources":["../../src/pull.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n type DictionaryStatus,\n parallelize,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n getProjectRequire,\n} from '@intlayer/config';\nimport type { Dictionary } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { PullLogger, type PullStatus } from './push/pullLog';\nimport { checkCMSAuth } from './utils/checkAccess';\n\ntype PullOptions = {\n dictionaries?: string[];\n newDictionariesPath?: string;\n configOptions?: GetConfigurationOptions;\n};\n\ntype DictionariesStatus = {\n dictionaryKey: string;\n status: DictionaryStatus | 'pending' | 'fetching' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n/**\n * Fetch distant dictionaries and write them locally,\n * with progress indicators and concurrency control.\n */\nexport const pull = async (options?: PullOptions): Promise<void> => {\n const appLogger = getAppLogger(options?.configOptions?.override);\n\n try {\n const config = getConfiguration(options?.configOptions);\n\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n\n // Get remote update timestamps map\n const getDictionariesUpdateTimestampResult =\n await intlayerAPI.dictionary.getDictionariesUpdateTimestamp();\n\n if (!getDictionariesUpdateTimestampResult.data) {\n throw new Error('No distant dictionaries found');\n }\n\n let distantDictionariesUpdateTimeStamp: Record<string, any> =\n getDictionariesUpdateTimestampResult.data;\n\n // Optional filtering by requested dictionaries\n if (options?.dictionaries) {\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) =>\n options.dictionaries?.includes(key)\n )\n );\n }\n\n // Filter by location\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) => {\n const localDictionaries = unmergedDictionariesRecord[key];\n const location =\n localDictionaries?.[0]?.location ??\n config.dictionary?.location ??\n 'remote';\n\n return location === 'remote' || location === 'local&remote';\n })\n );\n\n // Load local cached remote dictionaries (if any)\n const remoteDictionariesPath = join(\n config.system.mainDir,\n 'remote_dictionaries.cjs'\n );\n const requireFunction = config.build?.require ?? getProjectRequire();\n const remoteDictionariesRecord: Record<string, any> = existsSync(\n remoteDictionariesPath\n )\n ? (requireFunction(remoteDictionariesPath) as any)\n : {};\n\n // Determine which keys need fetching by comparing updatedAt with local cache\n const entries = Object.entries(distantDictionariesUpdateTimeStamp);\n const keysToFetch = entries\n .filter(([key, remoteUpdatedAtValue]) => {\n if (!remoteUpdatedAtValue) return true;\n\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\n const local = (remoteDictionariesRecord as any)[key];\n if (!local) return true;\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n if (typeof localUpdatedAt !== 'number') return true;\n return remoteUpdatedAt > localUpdatedAt;\n })\n .map(([key]) => key);\n\n const cachedKeys = entries\n .filter(([key, remoteUpdatedAtValue]) => {\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\n const local = (remoteDictionariesRecord as any)[key];\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n return (\n typeof localUpdatedAt === 'number' &&\n typeof remoteUpdatedAt === 'number' &&\n localUpdatedAt >= remoteUpdatedAt\n );\n })\n .map(([key]) => key);\n\n // Check if dictionaries list is empty\n if (entries.length === 0) {\n appLogger('No dictionaries to fetch', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Fetching dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] = [\n ...cachedKeys.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'imported' as DictionaryStatus,\n })),\n ...keysToFetch.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'pending' as const,\n })),\n ];\n\n // Initialize aggregated logger\n const logger = new PullLogger();\n logger.update(\n dictionariesStatuses.map<PullStatus>((s) => ({\n dictionaryKey: s.dictionaryKey,\n status: s.status,\n }))\n );\n\n const successfullyFetchedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n const isCached =\n statusObj.status === 'imported' || statusObj.status === 'up-to-date';\n\n if (!isCached) {\n statusObj.status = 'fetching';\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'fetching' },\n ]);\n }\n\n try {\n let sourceDictionary: Dictionary | undefined;\n\n if (isCached) {\n sourceDictionary = remoteDictionariesRecord[\n statusObj.dictionaryKey\n ] as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n // Fetch the dictionary\n const getDictionaryResult =\n await intlayerAPI.dictionary.getDictionary(statusObj.dictionaryKey);\n\n sourceDictionary = getDictionaryResult.data as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n throw new Error(\n `Dictionary ${statusObj.dictionaryKey} not found on remote`\n );\n }\n\n // Check if there is a local version of this dictionary that is local&remote\n const localDictionaries =\n unmergedDictionariesRecord[statusObj.dictionaryKey];\n const localAndRemoteDictionary = localDictionaries?.find(\n (d) => d.location === 'local&remote'\n );\n\n if (localAndRemoteDictionary) {\n // We want to preserve the local properties but use the remote content\n sourceDictionary = {\n ...sourceDictionary,\n location: 'local&remote',\n filePath: localAndRemoteDictionary.filePath,\n localId: localAndRemoteDictionary.localId,\n };\n }\n\n // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n sourceDictionary,\n config,\n options\n );\n\n statusObj.status = status;\n logger.update([{ dictionaryKey: statusObj.dictionaryKey, status }]);\n\n successfullyFetchedDictionaries.push(sourceDictionary);\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error fetching dictionary ${statusObj.dictionaryKey}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with concurrency limit\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n // Per-dictionary summary\n const iconFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n case 'reimported in JSON':\n case 'new content file':\n return '✔';\n case 'error':\n return '✖';\n default:\n return '⏲';\n }\n };\n\n const colorFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n return ANSIColors.GREEN;\n case 'reimported in JSON':\n case 'new content file':\n return ANSIColors.YELLOW;\n case 'error':\n return ANSIColors.RED;\n default:\n return ANSIColors.BLUE;\n }\n };\n\n for (const s of dictionariesStatuses) {\n const icon = iconFor(s.status);\n const color = colorFor(s.status);\n appLogger(\n ` - ${s.dictionaryKey} ${ANSIColors.GREY}[${color}${icon} ${s.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\n\n // Output any error messages\n for (const statusObj of dictionariesStatuses) {\n if (statusObj.errorMessage) {\n appLogger(statusObj.errorMessage, {\n level: 'error',\n });\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n"],"mappings":"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"}
@@ -8,8 +8,6 @@ let node_path = require("node:path");
8
8
  let _intlayer_unmerged_dictionaries_entry = require("@intlayer/unmerged-dictionaries-entry");
9
9
  let node_fs_promises = require("node:fs/promises");
10
10
  node_fs_promises = require_rolldown_runtime.__toESM(node_fs_promises);
11
- let node_readline = require("node:readline");
12
- node_readline = require_rolldown_runtime.__toESM(node_readline);
13
11
 
14
12
  //#region src/push/push.ts
15
13
  const statusIconsAndColors = {
@@ -45,12 +43,41 @@ const push = async (options) => {
45
43
  if (!await require_utils_checkAccess.checkCMSAuth(config)) return;
46
44
  const intlayerAPI = (0, _intlayer_api.getIntlayerAPIProxy)(void 0, config);
47
45
  const unmergedDictionariesRecord = (0, _intlayer_unmerged_dictionaries_entry.getUnmergedDictionaries)(config);
48
- let dictionaries = Object.values(unmergedDictionariesRecord).flat().filter((dictionary) => {
46
+ const allDictionaries = Object.values(unmergedDictionariesRecord).flat();
47
+ const customLocations = Array.from(new Set(allDictionaries.map((dictionary) => dictionary.location).filter((location) => location && ![
48
+ "remote",
49
+ "local",
50
+ "local&remote"
51
+ ].includes(location))));
52
+ let selectedCustomLocations = [];
53
+ if (customLocations.length > 0) {
54
+ const { multiselect, confirm, isCancel } = await import("@clack/prompts");
55
+ if (customLocations.length === 1) {
56
+ const shouldPush = await confirm({
57
+ message: `Do you want to push dictionaries with custom location ${(0, _intlayer_config.colorize)(customLocations[0], _intlayer_config.ANSIColors.BLUE, _intlayer_config.ANSIColors.RESET)}?`,
58
+ initialValue: false
59
+ });
60
+ if (isCancel(shouldPush)) return;
61
+ if (shouldPush) selectedCustomLocations = [customLocations[0]];
62
+ } else {
63
+ const selected = await multiselect({
64
+ message: "Select custom locations to push:",
65
+ options: customLocations.map((location) => ({
66
+ value: location,
67
+ label: location
68
+ })),
69
+ required: false
70
+ });
71
+ if (isCancel(selected)) return;
72
+ selectedCustomLocations = selected;
73
+ }
74
+ }
75
+ let dictionaries = allDictionaries.filter((dictionary) => {
49
76
  const location = dictionary.location ?? config.dictionary?.location ?? "local";
50
- return location === "remote" || location === "local&remote";
77
+ return location === "remote" || location === "local&remote" || selectedCustomLocations.includes(location);
51
78
  });
52
79
  if (dictionaries.length === 0) {
53
- appLogger(`No dictionaries found to push. Only dictionaries with location ${(0, _intlayer_config.colorize)("remote", _intlayer_config.ANSIColors.BLUE, _intlayer_config.ANSIColors.RESET)} or ${(0, _intlayer_config.colorize)("local&remote", _intlayer_config.ANSIColors.BLUE, _intlayer_config.ANSIColors.RESET)} are pushed.`, { level: "warn" });
80
+ appLogger(`No dictionaries found to push. Only dictionaries with location ${(0, _intlayer_config.colorize)("remote", _intlayer_config.ANSIColors.BLUE, _intlayer_config.ANSIColors.RESET)}, ${(0, _intlayer_config.colorize)("local&remote", _intlayer_config.ANSIColors.BLUE, _intlayer_config.ANSIColors.RESET)} or selected custom locations are pushed.`, { level: "warn" });
54
81
  appLogger(`You can set the location in your dictionary file (e.g. ${(0, _intlayer_config.colorize)("{ key: 'my-key', location: 'local&remote', ... }", _intlayer_config.ANSIColors.BLUE, _intlayer_config.ANSIColors.RESET)} or globally in your intlayer.config.ts file (e.g. ${(0, _intlayer_config.colorize)("{ dictionary: { location: 'local&remote' } }", _intlayer_config.ANSIColors.BLUE, _intlayer_config.ANSIColors.RESET)}).`, { level: "info" });
55
82
  return;
56
83
  }
@@ -89,8 +116,8 @@ const push = async (options) => {
89
116
  const pushResult = await intlayerAPI.dictionary.pushDictionaries([statusObj.dictionary]);
90
117
  const updatedDictionaries = pushResult.data?.updatedDictionaries ?? [];
91
118
  const newDictionaries = pushResult.data?.newDictionaries ?? [];
92
- const allDictionaries = [...updatedDictionaries, ...newDictionaries];
93
- for (const remoteDictionaryData of allDictionaries) {
119
+ const allDictionaries$1 = [...updatedDictionaries, ...newDictionaries];
120
+ for (const remoteDictionaryData of allDictionaries$1) {
94
121
  const localDictionary = unmergedDictionariesRecord[remoteDictionaryData.key]?.find((dictionary) => dictionary.localId === remoteDictionaryData.localId);
95
122
  if (!localDictionary) continue;
96
123
  await (0, _intlayer_chokidar.writeContentDeclaration)({
@@ -137,25 +164,20 @@ const push = async (options) => {
137
164
  else if (keepOption) {} else {
138
165
  const remoteDictionaries = successfullyPushedDictionaries.filter((dictionary) => dictionary.location === "remote");
139
166
  const remoteDictionariesKeys = remoteDictionaries.map((dictionary) => dictionary.key);
140
- const answer = await askUser(`Do you want to delete the local dictionaries that were successfully pushed? ${(0, _intlayer_config.colorize)("(Dictionaries:", _intlayer_config.ANSIColors.GREY, _intlayer_config.ANSIColors.RESET)} ${(0, _intlayer_config.colorizeKey)(remoteDictionariesKeys)}${(0, _intlayer_config.colorize)(")", _intlayer_config.ANSIColors.GREY, _intlayer_config.ANSIColors.RESET)} ${(0, _intlayer_config.colorize)("(yes/no)", _intlayer_config.ANSIColors.GREY_DARK, _intlayer_config.ANSIColors.RESET)}: `);
141
- if (answer.toLowerCase() === "yes" || answer.toLowerCase() === "y") await deleteLocalDictionaries(remoteDictionaries, options);
167
+ if (remoteDictionaries.length > 0) {
168
+ const { confirm, isCancel } = await import("@clack/prompts");
169
+ const shouldDelete = await confirm({
170
+ message: `Do you want to delete the local dictionaries that were successfully pushed? ${(0, _intlayer_config.colorize)("(Dictionaries:", _intlayer_config.ANSIColors.GREY, _intlayer_config.ANSIColors.RESET)} ${(0, _intlayer_config.colorizeKey)(remoteDictionariesKeys)}${(0, _intlayer_config.colorize)(")", _intlayer_config.ANSIColors.GREY, _intlayer_config.ANSIColors.RESET)}`,
171
+ initialValue: false
172
+ });
173
+ if (isCancel(shouldDelete)) return;
174
+ if (shouldDelete) await deleteLocalDictionaries(remoteDictionaries, options);
175
+ }
142
176
  }
143
177
  } catch (error) {
144
178
  appLogger(error, { level: "error" });
145
179
  }
146
180
  };
147
- const askUser = (question) => {
148
- const rl = node_readline.createInterface({
149
- input: process.stdin,
150
- output: process.stdout
151
- });
152
- return new Promise((resolve) => {
153
- rl.question(question, (answer) => {
154
- rl.close();
155
- resolve(answer);
156
- });
157
- });
158
- };
159
181
  const deleteLocalDictionaries = async (dictionariesToDelete, options) => {
160
182
  const appLogger = (0, _intlayer_config.getAppLogger)((0, _intlayer_config.getConfiguration)(options?.configOptions));
161
183
  const filePathsSet = /* @__PURE__ */ new Set();
@@ -1 +1 @@
1
- {"version":3,"file":"push.cjs","names":["ANSIColors","checkCMSAuth","PushLogger","readline","fsPromises"],"sources":["../../../src/push/push.ts"],"sourcesContent":["import * as fsPromises from 'node:fs/promises';\nimport { join } from 'node:path';\nimport * as readline from 'node:readline';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n formatPath,\n type ListGitFilesOptions,\n listGitFiles,\n parallelize,\n prepareIntlayer,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colorize,\n colorizeKey,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n} from '@intlayer/config';\nimport type { Dictionary } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { PushLogger, type PushStatus } from '../pushLog';\nimport { checkCMSAuth } from '../utils/checkAccess';\n\ntype PushOptions = {\n deleteLocaleDictionary?: boolean;\n keepLocaleDictionary?: boolean;\n dictionaries?: string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n build?: boolean;\n};\n\ntype DictionariesStatus = {\n dictionary: Dictionary;\n status: 'pending' | 'pushing' | 'modified' | 'pushed' | 'unknown' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n// Print per-dictionary summary similar to loadDictionaries\nconst statusIconsAndColors = {\n pushed: { icon: '✔', color: ANSIColors.GREEN },\n modified: { icon: '✔', color: ANSIColors.GREEN },\n error: { icon: '✖', color: ANSIColors.RED },\n default: { icon: '⏲', color: ANSIColors.BLUE },\n};\n\nconst getIconAndColor = (status: DictionariesStatus['status']) => {\n return (\n statusIconsAndColors[status as keyof typeof statusIconsAndColors] ??\n statusIconsAndColors.default\n );\n};\n\n/**\n * Get all local dictionaries and push them simultaneously.\n */\nexport const push = async (options?: PushOptions): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n if (options?.build === true) {\n await prepareIntlayer(config, { forceRun: true });\n } else if (typeof options?.build === 'undefined') {\n await prepareIntlayer(config);\n }\n\n try {\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n let dictionaries: Dictionary[] = Object.values(unmergedDictionariesRecord)\n .flat()\n .filter((dictionary) => {\n const location =\n dictionary.location ?? config.dictionary?.location ?? 'local';\n\n return location === 'remote' || location === 'local&remote';\n });\n\n // Check if the dictionaries list is empty after filtering by location\n if (dictionaries.length === 0) {\n appLogger(\n `No dictionaries found to push. Only dictionaries with location ${colorize('remote', ANSIColors.BLUE, ANSIColors.RESET)} or ${colorize('local&remote', ANSIColors.BLUE, ANSIColors.RESET)} are pushed.`,\n { level: 'warn' }\n );\n appLogger(\n `You can set the location in your dictionary file (e.g. ${colorize(\"{ key: 'my-key', location: 'local&remote', ... }\", ANSIColors.BLUE, ANSIColors.RESET)} or globally in your intlayer.config.ts file (e.g. ${colorize(\"{ dictionary: { location: 'local&remote' } }\", ANSIColors.BLUE, ANSIColors.RESET)}).`,\n { level: 'info' }\n );\n return;\n }\n\n const existingDictionariesKeys: string[] = Object.keys(\n unmergedDictionariesRecord\n );\n\n if (options?.dictionaries) {\n // Check if the provided dictionaries exist\n const noneExistingDictionariesOption = options.dictionaries.filter(\n (dictionaryId) => !existingDictionariesKeys.includes(dictionaryId)\n );\n\n if (noneExistingDictionariesOption.length > 0) {\n appLogger(\n `The following dictionaries do not exist: ${noneExistingDictionariesOption.join(\n ', '\n )} and have been ignored.`,\n {\n level: 'error',\n }\n );\n }\n\n // Filter the dictionaries from the provided list of IDs\n dictionaries = dictionaries.filter((dictionary) =>\n options.dictionaries?.includes(dictionary.key)\n );\n }\n\n if (options?.gitOptions) {\n const gitFiles = await listGitFiles(options.gitOptions);\n\n dictionaries = dictionaries.filter((dictionary) =>\n gitFiles.includes(\n join(config.content.baseDir, dictionary.filePath ?? '')\n )\n );\n }\n\n // Check if the dictionaries list is empty\n if (dictionaries.length === 0) {\n appLogger('No local dictionaries found', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Pushing dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] = dictionaries.map(\n (dictionary) => ({\n dictionary,\n status: 'pending',\n })\n );\n\n // Initialize aggregated logger similar to loadDictionaries\n const logger = new PushLogger();\n logger.update(\n dictionariesStatuses.map<PushStatus>((s) => ({\n dictionaryKey: s.dictionary.key,\n status: 'pending',\n }))\n );\n\n const successfullyPushedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n statusObj.status = 'pushing';\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushing' },\n ]);\n\n try {\n const pushResult = await intlayerAPI.dictionary.pushDictionaries([\n statusObj.dictionary,\n ]);\n\n const updatedDictionaries = pushResult.data?.updatedDictionaries ?? [];\n const newDictionaries = pushResult.data?.newDictionaries ?? [];\n\n const allDictionaries = [...updatedDictionaries, ...newDictionaries];\n\n for (const remoteDictionaryData of allDictionaries) {\n const localDictionary = unmergedDictionariesRecord[\n remoteDictionaryData.key\n ]?.find(\n (dictionary) => dictionary.localId === remoteDictionaryData.localId\n );\n\n if (!localDictionary) continue;\n\n await writeContentDeclaration(\n { ...localDictionary, id: remoteDictionaryData.id },\n config\n );\n }\n\n if (\n updatedDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'modified';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'modified' },\n ]);\n } else if (\n newDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'pushed';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushed' },\n ]);\n } else {\n statusObj.status = 'unknown';\n }\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error pushing dictionary ${statusObj.dictionary.key}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with a concurrency limit (reuse parallelize)\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n for (const dictionaryStatus of dictionariesStatuses) {\n const { icon, color } = getIconAndColor(dictionaryStatus.status);\n appLogger(\n ` - ${colorizeKey(dictionaryStatus.dictionary.key)} ${ANSIColors.GREY}[${color}${icon} ${dictionaryStatus.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\n\n // Output any error messages\n for (const statusObj of dictionariesStatuses) {\n if (statusObj.errorMessage) {\n appLogger(statusObj.errorMessage, {\n level: 'error',\n });\n }\n }\n\n // Handle delete or keep options\n const deleteOption = options?.deleteLocaleDictionary;\n const keepOption = options?.keepLocaleDictionary;\n\n if (deleteOption && keepOption) {\n throw new Error(\n 'Cannot specify both --deleteLocaleDictionary and --keepLocaleDictionary options.'\n );\n }\n\n if (deleteOption) {\n // Delete only the successfully pushed dictionaries\n await deleteLocalDictionaries(successfullyPushedDictionaries, options);\n } else if (keepOption) {\n // Do nothing, keep the local dictionaries\n } else {\n // Ask the user\n const remoteDictionaries = successfullyPushedDictionaries.filter(\n (dictionary) => dictionary.location === 'remote'\n );\n const remoteDictionariesKeys = remoteDictionaries.map(\n (dictionary) => dictionary.key\n );\n const answer = await askUser(\n `Do you want to delete the local dictionaries that were successfully pushed? ${colorize('(Dictionaries:', ANSIColors.GREY, ANSIColors.RESET)} ${colorizeKey(remoteDictionariesKeys)}${colorize(')', ANSIColors.GREY, ANSIColors.RESET)} ${colorize('(yes/no)', ANSIColors.GREY_DARK, ANSIColors.RESET)}: `\n );\n if (answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y') {\n await deleteLocalDictionaries(remoteDictionaries, options);\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n\nconst askUser = (question: string): Promise<string> => {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n return new Promise((resolve) => {\n rl.question(question, (answer: string) => {\n rl.close();\n resolve(answer);\n });\n });\n};\n\nconst deleteLocalDictionaries = async (\n dictionariesToDelete: Dictionary[],\n options?: PushOptions\n): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n // Use a Set to collect all unique file paths\n const filePathsSet: Set<string> = new Set();\n\n for (const dictionary of dictionariesToDelete) {\n const { filePath } = dictionary;\n\n if (!filePath) {\n appLogger(\n `Dictionary ${colorizeKey(dictionary.key)} does not have a file path`,\n {\n level: 'error',\n }\n );\n continue;\n }\n\n filePathsSet.add(filePath);\n }\n\n for (const filePath of filePathsSet) {\n try {\n const stats = await fsPromises.lstat(filePath);\n\n if (stats.isFile()) {\n await fsPromises.unlink(filePath);\n appLogger(`Deleted file ${formatPath(filePath)}`, {});\n } else if (stats.isDirectory()) {\n appLogger(`Path is a directory ${formatPath(filePath)}, skipping.`, {});\n } else {\n appLogger(\n `Unknown file type for ${formatPath(filePath)}, skipping.`,\n {}\n );\n }\n } catch (err) {\n appLogger(`Error deleting ${formatPath(filePath)}: ${err}`, {\n level: 'error',\n });\n }\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"push.cjs","names":["ANSIColors","checkCMSAuth","PushLogger","allDictionaries","fsPromises"],"sources":["../../../src/push/push.ts"],"sourcesContent":["import * as fsPromises from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n formatPath,\n type ListGitFilesOptions,\n listGitFiles,\n parallelize,\n prepareIntlayer,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colorize,\n colorizeKey,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n} from '@intlayer/config';\nimport type { Dictionary } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { PushLogger, type PushStatus } from '../pushLog';\nimport { checkCMSAuth } from '../utils/checkAccess';\n\ntype PushOptions = {\n deleteLocaleDictionary?: boolean;\n keepLocaleDictionary?: boolean;\n dictionaries?: string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n build?: boolean;\n};\n\ntype DictionariesStatus = {\n dictionary: Dictionary;\n status: 'pending' | 'pushing' | 'modified' | 'pushed' | 'unknown' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n// Print per-dictionary summary similar to loadDictionaries\nconst statusIconsAndColors = {\n pushed: { icon: '✔', color: ANSIColors.GREEN },\n modified: { icon: '✔', color: ANSIColors.GREEN },\n error: { icon: '✖', color: ANSIColors.RED },\n default: { icon: '⏲', color: ANSIColors.BLUE },\n};\n\nconst getIconAndColor = (status: DictionariesStatus['status']) => {\n return (\n statusIconsAndColors[status as keyof typeof statusIconsAndColors] ??\n statusIconsAndColors.default\n );\n};\n\n/**\n * Get all local dictionaries and push them simultaneously.\n */\nexport const push = async (options?: PushOptions): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n if (options?.build === true) {\n await prepareIntlayer(config, { forceRun: true });\n } else if (typeof options?.build === 'undefined') {\n await prepareIntlayer(config);\n }\n\n try {\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n const allDictionaries = Object.values(unmergedDictionariesRecord).flat();\n\n const customLocations = Array.from(\n new Set(\n allDictionaries\n .map((dictionary) => dictionary.location)\n .filter(\n (location) =>\n location &&\n !['remote', 'local', 'local&remote'].includes(location)\n )\n )\n ) as string[];\n\n let selectedCustomLocations: string[] = [];\n\n if (customLocations.length > 0) {\n const { multiselect, confirm, isCancel } = await import('@clack/prompts');\n\n if (customLocations.length === 1) {\n const shouldPush = await confirm({\n message: `Do you want to push dictionaries with custom location ${colorize(customLocations[0], ANSIColors.BLUE, ANSIColors.RESET)}?`,\n initialValue: false,\n });\n\n if (isCancel(shouldPush)) {\n return;\n }\n\n if (shouldPush) {\n selectedCustomLocations = [customLocations[0]];\n }\n } else {\n const selected = await multiselect({\n message: 'Select custom locations to push:',\n options: customLocations.map((location) => ({\n value: location,\n label: location,\n })),\n required: false,\n });\n\n if (isCancel(selected)) {\n return;\n }\n\n selectedCustomLocations = selected as string[];\n }\n }\n\n let dictionaries: Dictionary[] = allDictionaries.filter((dictionary) => {\n const location =\n dictionary.location ?? config.dictionary?.location ?? 'local';\n\n return (\n location === 'remote' ||\n location === 'local&remote' ||\n selectedCustomLocations.includes(location)\n );\n });\n\n // Check if the dictionaries list is empty after filtering by location\n if (dictionaries.length === 0) {\n appLogger(\n `No dictionaries found to push. Only dictionaries with location ${colorize('remote', ANSIColors.BLUE, ANSIColors.RESET)}, ${colorize('local&remote', ANSIColors.BLUE, ANSIColors.RESET)} or selected custom locations are pushed.`,\n { level: 'warn' }\n );\n appLogger(\n `You can set the location in your dictionary file (e.g. ${colorize(\"{ key: 'my-key', location: 'local&remote', ... }\", ANSIColors.BLUE, ANSIColors.RESET)} or globally in your intlayer.config.ts file (e.g. ${colorize(\"{ dictionary: { location: 'local&remote' } }\", ANSIColors.BLUE, ANSIColors.RESET)}).`,\n { level: 'info' }\n );\n return;\n }\n\n const existingDictionariesKeys: string[] = Object.keys(\n unmergedDictionariesRecord\n );\n\n if (options?.dictionaries) {\n // Check if the provided dictionaries exist\n const noneExistingDictionariesOption = options.dictionaries.filter(\n (dictionaryId) => !existingDictionariesKeys.includes(dictionaryId)\n );\n\n if (noneExistingDictionariesOption.length > 0) {\n appLogger(\n `The following dictionaries do not exist: ${noneExistingDictionariesOption.join(\n ', '\n )} and have been ignored.`,\n {\n level: 'error',\n }\n );\n }\n\n // Filter the dictionaries from the provided list of IDs\n dictionaries = dictionaries.filter((dictionary) =>\n options.dictionaries?.includes(dictionary.key)\n );\n }\n\n if (options?.gitOptions) {\n const gitFiles = await listGitFiles(options.gitOptions);\n\n dictionaries = dictionaries.filter((dictionary) =>\n gitFiles.includes(\n join(config.content.baseDir, dictionary.filePath ?? '')\n )\n );\n }\n\n // Check if the dictionaries list is empty\n if (dictionaries.length === 0) {\n appLogger('No local dictionaries found', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Pushing dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] = dictionaries.map(\n (dictionary) => ({\n dictionary,\n status: 'pending',\n })\n );\n\n // Initialize aggregated logger similar to loadDictionaries\n const logger = new PushLogger();\n logger.update(\n dictionariesStatuses.map<PushStatus>((s) => ({\n dictionaryKey: s.dictionary.key,\n status: 'pending',\n }))\n );\n\n const successfullyPushedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n statusObj.status = 'pushing';\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushing' },\n ]);\n\n try {\n const pushResult = await intlayerAPI.dictionary.pushDictionaries([\n statusObj.dictionary,\n ]);\n\n const updatedDictionaries = pushResult.data?.updatedDictionaries ?? [];\n const newDictionaries = pushResult.data?.newDictionaries ?? [];\n\n const allDictionaries = [...updatedDictionaries, ...newDictionaries];\n\n for (const remoteDictionaryData of allDictionaries) {\n const localDictionary = unmergedDictionariesRecord[\n remoteDictionaryData.key\n ]?.find(\n (dictionary) => dictionary.localId === remoteDictionaryData.localId\n );\n\n if (!localDictionary) continue;\n\n await writeContentDeclaration(\n { ...localDictionary, id: remoteDictionaryData.id },\n config\n );\n }\n\n if (\n updatedDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'modified';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'modified' },\n ]);\n } else if (\n newDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'pushed';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushed' },\n ]);\n } else {\n statusObj.status = 'unknown';\n }\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error pushing dictionary ${statusObj.dictionary.key}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with a concurrency limit (reuse parallelize)\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n for (const dictionaryStatus of dictionariesStatuses) {\n const { icon, color } = getIconAndColor(dictionaryStatus.status);\n appLogger(\n ` - ${colorizeKey(dictionaryStatus.dictionary.key)} ${ANSIColors.GREY}[${color}${icon} ${dictionaryStatus.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\n\n // Output any error messages\n for (const statusObj of dictionariesStatuses) {\n if (statusObj.errorMessage) {\n appLogger(statusObj.errorMessage, {\n level: 'error',\n });\n }\n }\n\n // Handle delete or keep options\n const deleteOption = options?.deleteLocaleDictionary;\n const keepOption = options?.keepLocaleDictionary;\n\n if (deleteOption && keepOption) {\n throw new Error(\n 'Cannot specify both --deleteLocaleDictionary and --keepLocaleDictionary options.'\n );\n }\n\n if (deleteOption) {\n // Delete only the successfully pushed dictionaries\n await deleteLocalDictionaries(successfullyPushedDictionaries, options);\n } else if (keepOption) {\n // Do nothing, keep the local dictionaries\n } else {\n // Ask the user\n const remoteDictionaries = successfullyPushedDictionaries.filter(\n (dictionary) => dictionary.location === 'remote'\n );\n const remoteDictionariesKeys = remoteDictionaries.map(\n (dictionary) => dictionary.key\n );\n\n if (remoteDictionaries.length > 0) {\n const { confirm, isCancel } = await import('@clack/prompts');\n\n const shouldDelete = await confirm({\n message: `Do you want to delete the local dictionaries that were successfully pushed? ${colorize('(Dictionaries:', ANSIColors.GREY, ANSIColors.RESET)} ${colorizeKey(remoteDictionariesKeys)}${colorize(')', ANSIColors.GREY, ANSIColors.RESET)}`,\n initialValue: false,\n });\n\n if (isCancel(shouldDelete)) {\n return;\n }\n\n if (shouldDelete) {\n await deleteLocalDictionaries(remoteDictionaries, options);\n }\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n\nconst deleteLocalDictionaries = async (\n dictionariesToDelete: Dictionary[],\n options?: PushOptions\n): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n // Use a Set to collect all unique file paths\n const filePathsSet: Set<string> = new Set();\n\n for (const dictionary of dictionariesToDelete) {\n const { filePath } = dictionary;\n\n if (!filePath) {\n appLogger(\n `Dictionary ${colorizeKey(dictionary.key)} does not have a file path`,\n {\n level: 'error',\n }\n );\n continue;\n }\n\n filePathsSet.add(filePath);\n }\n\n for (const filePath of filePathsSet) {\n try {\n const stats = await fsPromises.lstat(filePath);\n\n if (stats.isFile()) {\n await fsPromises.unlink(filePath);\n appLogger(`Deleted file ${formatPath(filePath)}`, {});\n } else if (stats.isDirectory()) {\n appLogger(`Path is a directory ${formatPath(filePath)}, skipping.`, {});\n } else {\n appLogger(\n `Unknown file type for ${formatPath(filePath)}, skipping.`,\n {}\n );\n }\n } catch (err) {\n appLogger(`Error deleting ${formatPath(filePath)}: ${err}`, {\n level: 'error',\n });\n }\n }\n};\n"],"mappings":"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"}
package/dist/esm/pull.mjs CHANGED
@@ -27,7 +27,7 @@ const pull = async (options) => {
27
27
  const location = unmergedDictionariesRecord[key]?.[0]?.location ?? config.dictionary?.location ?? "remote";
28
28
  return location === "remote" || location === "local&remote";
29
29
  }));
30
- const remoteDictionariesPath = join(config.content.mainDir, "remote_dictionaries.cjs");
30
+ const remoteDictionariesPath = join(config.system.mainDir, "remote_dictionaries.cjs");
31
31
  const requireFunction = config.build?.require ?? getProjectRequire();
32
32
  const remoteDictionariesRecord = existsSync(remoteDictionariesPath) ? requireFunction(remoteDictionariesPath) : {};
33
33
  const entries = Object.entries(distantDictionariesUpdateTimeStamp);
@@ -79,6 +79,13 @@ const pull = async (options) => {
79
79
  if (isCached) sourceDictionary = remoteDictionariesRecord[statusObj.dictionaryKey];
80
80
  if (!sourceDictionary) sourceDictionary = (await intlayerAPI.dictionary.getDictionary(statusObj.dictionaryKey)).data;
81
81
  if (!sourceDictionary) throw new Error(`Dictionary ${statusObj.dictionaryKey} not found on remote`);
82
+ const localAndRemoteDictionary = unmergedDictionariesRecord[statusObj.dictionaryKey]?.find((d) => d.location === "local&remote");
83
+ if (localAndRemoteDictionary) sourceDictionary = {
84
+ ...sourceDictionary,
85
+ location: "local&remote",
86
+ filePath: localAndRemoteDictionary.filePath,
87
+ localId: localAndRemoteDictionary.localId
88
+ };
82
89
  const { status } = await writeContentDeclaration(sourceDictionary, config, options);
83
90
  statusObj.status = status;
84
91
  logger$1.update([{
@@ -1 +1 @@
1
- {"version":3,"file":"pull.mjs","names":["logger"],"sources":["../../src/pull.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n type DictionaryStatus,\n parallelize,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n getProjectRequire,\n} from '@intlayer/config';\nimport type { Dictionary } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { PullLogger, type PullStatus } from './push/pullLog';\nimport { checkCMSAuth } from './utils/checkAccess';\n\ntype PullOptions = {\n dictionaries?: string[];\n newDictionariesPath?: string;\n configOptions?: GetConfigurationOptions;\n};\n\ntype DictionariesStatus = {\n dictionaryKey: string;\n status: DictionaryStatus | 'pending' | 'fetching' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n/**\n * Fetch distant dictionaries and write them locally,\n * with progress indicators and concurrency control.\n */\nexport const pull = async (options?: PullOptions): Promise<void> => {\n const appLogger = getAppLogger(options?.configOptions?.override);\n\n try {\n const config = getConfiguration(options?.configOptions);\n\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n\n // Get remote update timestamps map\n const getDictionariesUpdateTimestampResult =\n await intlayerAPI.dictionary.getDictionariesUpdateTimestamp();\n\n if (!getDictionariesUpdateTimestampResult.data) {\n throw new Error('No distant dictionaries found');\n }\n\n let distantDictionariesUpdateTimeStamp: Record<string, any> =\n getDictionariesUpdateTimestampResult.data;\n\n // Optional filtering by requested dictionaries\n if (options?.dictionaries) {\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) =>\n options.dictionaries?.includes(key)\n )\n );\n }\n\n // Filter by location\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) => {\n const localDictionaries = unmergedDictionariesRecord[key];\n const location =\n localDictionaries?.[0]?.location ??\n config.dictionary?.location ??\n 'remote';\n\n return location === 'remote' || location === 'local&remote';\n })\n );\n\n // Load local cached remote dictionaries (if any)\n const remoteDictionariesPath = join(\n config.content.mainDir,\n 'remote_dictionaries.cjs'\n );\n const requireFunction = config.build?.require ?? getProjectRequire();\n const remoteDictionariesRecord: Record<string, any> = existsSync(\n remoteDictionariesPath\n )\n ? (requireFunction(remoteDictionariesPath) as any)\n : {};\n\n // Determine which keys need fetching by comparing updatedAt with local cache\n const entries = Object.entries(distantDictionariesUpdateTimeStamp);\n const keysToFetch = entries\n .filter(([key, remoteUpdatedAtValue]) => {\n if (!remoteUpdatedAtValue) return true;\n\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\n const local = (remoteDictionariesRecord as any)[key];\n if (!local) return true;\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n if (typeof localUpdatedAt !== 'number') return true;\n return remoteUpdatedAt > localUpdatedAt;\n })\n .map(([key]) => key);\n\n const cachedKeys = entries\n .filter(([key, remoteUpdatedAtValue]) => {\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\n const local = (remoteDictionariesRecord as any)[key];\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n return (\n typeof localUpdatedAt === 'number' &&\n typeof remoteUpdatedAt === 'number' &&\n localUpdatedAt >= remoteUpdatedAt\n );\n })\n .map(([key]) => key);\n\n // Check if dictionaries list is empty\n if (entries.length === 0) {\n appLogger('No dictionaries to fetch', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Fetching dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] = [\n ...cachedKeys.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'imported' as DictionaryStatus,\n })),\n ...keysToFetch.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'pending' as const,\n })),\n ];\n\n // Initialize aggregated logger\n const logger = new PullLogger();\n logger.update(\n dictionariesStatuses.map<PullStatus>((s) => ({\n dictionaryKey: s.dictionaryKey,\n status: s.status,\n }))\n );\n\n const successfullyFetchedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n const isCached =\n statusObj.status === 'imported' || statusObj.status === 'up-to-date';\n\n if (!isCached) {\n statusObj.status = 'fetching';\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'fetching' },\n ]);\n }\n\n try {\n let sourceDictionary: Dictionary | undefined;\n\n if (isCached) {\n sourceDictionary = remoteDictionariesRecord[\n statusObj.dictionaryKey\n ] as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n // Fetch the dictionary\n const getDictionaryResult =\n await intlayerAPI.dictionary.getDictionary(statusObj.dictionaryKey);\n\n sourceDictionary = getDictionaryResult.data as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n throw new Error(\n `Dictionary ${statusObj.dictionaryKey} not found on remote`\n );\n }\n\n // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n sourceDictionary,\n config,\n options\n );\n\n statusObj.status = status;\n logger.update([{ dictionaryKey: statusObj.dictionaryKey, status }]);\n\n successfullyFetchedDictionaries.push(sourceDictionary);\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error fetching dictionary ${statusObj.dictionaryKey}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with concurrency limit\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n // Per-dictionary summary\n const iconFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n case 'reimported in JSON':\n case 'new content file':\n return '✔';\n case 'error':\n return '✖';\n default:\n return '⏲';\n }\n };\n\n const colorFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n return ANSIColors.GREEN;\n case 'reimported in JSON':\n case 'new content file':\n return ANSIColors.YELLOW;\n case 'error':\n return ANSIColors.RED;\n default:\n return ANSIColors.BLUE;\n }\n };\n\n for (const s of dictionariesStatuses) {\n const icon = iconFor(s.status);\n const color = colorFor(s.status);\n appLogger(\n ` - ${s.dictionaryKey} ${ANSIColors.GREY}[${color}${icon} ${s.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\n\n // Output any error messages\n for (const statusObj of dictionariesStatuses) {\n if (statusObj.errorMessage) {\n appLogger(statusObj.errorMessage, {\n level: 'error',\n });\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"pull.mjs","names":["logger"],"sources":["../../src/pull.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n type DictionaryStatus,\n parallelize,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n getProjectRequire,\n} from '@intlayer/config';\nimport type { Dictionary } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { PullLogger, type PullStatus } from './push/pullLog';\nimport { checkCMSAuth } from './utils/checkAccess';\n\ntype PullOptions = {\n dictionaries?: string[];\n newDictionariesPath?: string;\n configOptions?: GetConfigurationOptions;\n};\n\ntype DictionariesStatus = {\n dictionaryKey: string;\n status: DictionaryStatus | 'pending' | 'fetching' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n/**\n * Fetch distant dictionaries and write them locally,\n * with progress indicators and concurrency control.\n */\nexport const pull = async (options?: PullOptions): Promise<void> => {\n const appLogger = getAppLogger(options?.configOptions?.override);\n\n try {\n const config = getConfiguration(options?.configOptions);\n\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n\n // Get remote update timestamps map\n const getDictionariesUpdateTimestampResult =\n await intlayerAPI.dictionary.getDictionariesUpdateTimestamp();\n\n if (!getDictionariesUpdateTimestampResult.data) {\n throw new Error('No distant dictionaries found');\n }\n\n let distantDictionariesUpdateTimeStamp: Record<string, any> =\n getDictionariesUpdateTimestampResult.data;\n\n // Optional filtering by requested dictionaries\n if (options?.dictionaries) {\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) =>\n options.dictionaries?.includes(key)\n )\n );\n }\n\n // Filter by location\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) => {\n const localDictionaries = unmergedDictionariesRecord[key];\n const location =\n localDictionaries?.[0]?.location ??\n config.dictionary?.location ??\n 'remote';\n\n return location === 'remote' || location === 'local&remote';\n })\n );\n\n // Load local cached remote dictionaries (if any)\n const remoteDictionariesPath = join(\n config.system.mainDir,\n 'remote_dictionaries.cjs'\n );\n const requireFunction = config.build?.require ?? getProjectRequire();\n const remoteDictionariesRecord: Record<string, any> = existsSync(\n remoteDictionariesPath\n )\n ? (requireFunction(remoteDictionariesPath) as any)\n : {};\n\n // Determine which keys need fetching by comparing updatedAt with local cache\n const entries = Object.entries(distantDictionariesUpdateTimeStamp);\n const keysToFetch = entries\n .filter(([key, remoteUpdatedAtValue]) => {\n if (!remoteUpdatedAtValue) return true;\n\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\n const local = (remoteDictionariesRecord as any)[key];\n if (!local) return true;\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n if (typeof localUpdatedAt !== 'number') return true;\n return remoteUpdatedAt > localUpdatedAt;\n })\n .map(([key]) => key);\n\n const cachedKeys = entries\n .filter(([key, remoteUpdatedAtValue]) => {\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\n const local = (remoteDictionariesRecord as any)[key];\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n return (\n typeof localUpdatedAt === 'number' &&\n typeof remoteUpdatedAt === 'number' &&\n localUpdatedAt >= remoteUpdatedAt\n );\n })\n .map(([key]) => key);\n\n // Check if dictionaries list is empty\n if (entries.length === 0) {\n appLogger('No dictionaries to fetch', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Fetching dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] = [\n ...cachedKeys.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'imported' as DictionaryStatus,\n })),\n ...keysToFetch.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'pending' as const,\n })),\n ];\n\n // Initialize aggregated logger\n const logger = new PullLogger();\n logger.update(\n dictionariesStatuses.map<PullStatus>((s) => ({\n dictionaryKey: s.dictionaryKey,\n status: s.status,\n }))\n );\n\n const successfullyFetchedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n const isCached =\n statusObj.status === 'imported' || statusObj.status === 'up-to-date';\n\n if (!isCached) {\n statusObj.status = 'fetching';\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'fetching' },\n ]);\n }\n\n try {\n let sourceDictionary: Dictionary | undefined;\n\n if (isCached) {\n sourceDictionary = remoteDictionariesRecord[\n statusObj.dictionaryKey\n ] as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n // Fetch the dictionary\n const getDictionaryResult =\n await intlayerAPI.dictionary.getDictionary(statusObj.dictionaryKey);\n\n sourceDictionary = getDictionaryResult.data as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n throw new Error(\n `Dictionary ${statusObj.dictionaryKey} not found on remote`\n );\n }\n\n // Check if there is a local version of this dictionary that is local&remote\n const localDictionaries =\n unmergedDictionariesRecord[statusObj.dictionaryKey];\n const localAndRemoteDictionary = localDictionaries?.find(\n (d) => d.location === 'local&remote'\n );\n\n if (localAndRemoteDictionary) {\n // We want to preserve the local properties but use the remote content\n sourceDictionary = {\n ...sourceDictionary,\n location: 'local&remote',\n filePath: localAndRemoteDictionary.filePath,\n localId: localAndRemoteDictionary.localId,\n };\n }\n\n // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n sourceDictionary,\n config,\n options\n );\n\n statusObj.status = status;\n logger.update([{ dictionaryKey: statusObj.dictionaryKey, status }]);\n\n successfullyFetchedDictionaries.push(sourceDictionary);\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error fetching dictionary ${statusObj.dictionaryKey}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with concurrency limit\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n // Per-dictionary summary\n const iconFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n case 'reimported in JSON':\n case 'new content file':\n return '✔';\n case 'error':\n return '✖';\n default:\n return '⏲';\n }\n };\n\n const colorFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n return ANSIColors.GREEN;\n case 'reimported in JSON':\n case 'new content file':\n return ANSIColors.YELLOW;\n case 'error':\n return ANSIColors.RED;\n default:\n return ANSIColors.BLUE;\n }\n };\n\n for (const s of dictionariesStatuses) {\n const icon = iconFor(s.status);\n const color = colorFor(s.status);\n appLogger(\n ` - ${s.dictionaryKey} ${ANSIColors.GREY}[${color}${icon} ${s.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\n\n // Output any error messages\n for (const statusObj of dictionariesStatuses) {\n if (statusObj.errorMessage) {\n appLogger(statusObj.errorMessage, {\n level: 'error',\n });\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n"],"mappings":"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"}
@@ -6,7 +6,6 @@ import { ANSIColors, colorize, colorizeKey, getAppLogger, getConfiguration } fro
6
6
  import { join } from "node:path";
7
7
  import { getUnmergedDictionaries } from "@intlayer/unmerged-dictionaries-entry";
8
8
  import * as fsPromises from "node:fs/promises";
9
- import * as readline from "node:readline";
10
9
 
11
10
  //#region src/push/push.ts
12
11
  const statusIconsAndColors = {
@@ -42,12 +41,41 @@ const push = async (options) => {
42
41
  if (!await checkCMSAuth(config)) return;
43
42
  const intlayerAPI = getIntlayerAPIProxy(void 0, config);
44
43
  const unmergedDictionariesRecord = getUnmergedDictionaries(config);
45
- let dictionaries = Object.values(unmergedDictionariesRecord).flat().filter((dictionary) => {
44
+ const allDictionaries = Object.values(unmergedDictionariesRecord).flat();
45
+ const customLocations = Array.from(new Set(allDictionaries.map((dictionary) => dictionary.location).filter((location) => location && ![
46
+ "remote",
47
+ "local",
48
+ "local&remote"
49
+ ].includes(location))));
50
+ let selectedCustomLocations = [];
51
+ if (customLocations.length > 0) {
52
+ const { multiselect, confirm, isCancel } = await import("@clack/prompts");
53
+ if (customLocations.length === 1) {
54
+ const shouldPush = await confirm({
55
+ message: `Do you want to push dictionaries with custom location ${colorize(customLocations[0], ANSIColors.BLUE, ANSIColors.RESET)}?`,
56
+ initialValue: false
57
+ });
58
+ if (isCancel(shouldPush)) return;
59
+ if (shouldPush) selectedCustomLocations = [customLocations[0]];
60
+ } else {
61
+ const selected = await multiselect({
62
+ message: "Select custom locations to push:",
63
+ options: customLocations.map((location) => ({
64
+ value: location,
65
+ label: location
66
+ })),
67
+ required: false
68
+ });
69
+ if (isCancel(selected)) return;
70
+ selectedCustomLocations = selected;
71
+ }
72
+ }
73
+ let dictionaries = allDictionaries.filter((dictionary) => {
46
74
  const location = dictionary.location ?? config.dictionary?.location ?? "local";
47
- return location === "remote" || location === "local&remote";
75
+ return location === "remote" || location === "local&remote" || selectedCustomLocations.includes(location);
48
76
  });
49
77
  if (dictionaries.length === 0) {
50
- appLogger(`No dictionaries found to push. Only dictionaries with location ${colorize("remote", ANSIColors.BLUE, ANSIColors.RESET)} or ${colorize("local&remote", ANSIColors.BLUE, ANSIColors.RESET)} are pushed.`, { level: "warn" });
78
+ appLogger(`No dictionaries found to push. Only dictionaries with location ${colorize("remote", ANSIColors.BLUE, ANSIColors.RESET)}, ${colorize("local&remote", ANSIColors.BLUE, ANSIColors.RESET)} or selected custom locations are pushed.`, { level: "warn" });
51
79
  appLogger(`You can set the location in your dictionary file (e.g. ${colorize("{ key: 'my-key', location: 'local&remote', ... }", ANSIColors.BLUE, ANSIColors.RESET)} or globally in your intlayer.config.ts file (e.g. ${colorize("{ dictionary: { location: 'local&remote' } }", ANSIColors.BLUE, ANSIColors.RESET)}).`, { level: "info" });
52
80
  return;
53
81
  }
@@ -86,8 +114,8 @@ const push = async (options) => {
86
114
  const pushResult = await intlayerAPI.dictionary.pushDictionaries([statusObj.dictionary]);
87
115
  const updatedDictionaries = pushResult.data?.updatedDictionaries ?? [];
88
116
  const newDictionaries = pushResult.data?.newDictionaries ?? [];
89
- const allDictionaries = [...updatedDictionaries, ...newDictionaries];
90
- for (const remoteDictionaryData of allDictionaries) {
117
+ const allDictionaries$1 = [...updatedDictionaries, ...newDictionaries];
118
+ for (const remoteDictionaryData of allDictionaries$1) {
91
119
  const localDictionary = unmergedDictionariesRecord[remoteDictionaryData.key]?.find((dictionary) => dictionary.localId === remoteDictionaryData.localId);
92
120
  if (!localDictionary) continue;
93
121
  await writeContentDeclaration({
@@ -134,25 +162,20 @@ const push = async (options) => {
134
162
  else if (keepOption) {} else {
135
163
  const remoteDictionaries = successfullyPushedDictionaries.filter((dictionary) => dictionary.location === "remote");
136
164
  const remoteDictionariesKeys = remoteDictionaries.map((dictionary) => dictionary.key);
137
- const answer = await askUser(`Do you want to delete the local dictionaries that were successfully pushed? ${colorize("(Dictionaries:", ANSIColors.GREY, ANSIColors.RESET)} ${colorizeKey(remoteDictionariesKeys)}${colorize(")", ANSIColors.GREY, ANSIColors.RESET)} ${colorize("(yes/no)", ANSIColors.GREY_DARK, ANSIColors.RESET)}: `);
138
- if (answer.toLowerCase() === "yes" || answer.toLowerCase() === "y") await deleteLocalDictionaries(remoteDictionaries, options);
165
+ if (remoteDictionaries.length > 0) {
166
+ const { confirm, isCancel } = await import("@clack/prompts");
167
+ const shouldDelete = await confirm({
168
+ message: `Do you want to delete the local dictionaries that were successfully pushed? ${colorize("(Dictionaries:", ANSIColors.GREY, ANSIColors.RESET)} ${colorizeKey(remoteDictionariesKeys)}${colorize(")", ANSIColors.GREY, ANSIColors.RESET)}`,
169
+ initialValue: false
170
+ });
171
+ if (isCancel(shouldDelete)) return;
172
+ if (shouldDelete) await deleteLocalDictionaries(remoteDictionaries, options);
173
+ }
139
174
  }
140
175
  } catch (error) {
141
176
  appLogger(error, { level: "error" });
142
177
  }
143
178
  };
144
- const askUser = (question) => {
145
- const rl = readline.createInterface({
146
- input: process.stdin,
147
- output: process.stdout
148
- });
149
- return new Promise((resolve$1) => {
150
- rl.question(question, (answer) => {
151
- rl.close();
152
- resolve$1(answer);
153
- });
154
- });
155
- };
156
179
  const deleteLocalDictionaries = async (dictionariesToDelete, options) => {
157
180
  const appLogger = getAppLogger(getConfiguration(options?.configOptions));
158
181
  const filePathsSet = /* @__PURE__ */ new Set();
@@ -1 +1 @@
1
- {"version":3,"file":"push.mjs","names":["logger"],"sources":["../../../src/push/push.ts"],"sourcesContent":["import * as fsPromises from 'node:fs/promises';\nimport { join } from 'node:path';\nimport * as readline from 'node:readline';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n formatPath,\n type ListGitFilesOptions,\n listGitFiles,\n parallelize,\n prepareIntlayer,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colorize,\n colorizeKey,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n} from '@intlayer/config';\nimport type { Dictionary } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { PushLogger, type PushStatus } from '../pushLog';\nimport { checkCMSAuth } from '../utils/checkAccess';\n\ntype PushOptions = {\n deleteLocaleDictionary?: boolean;\n keepLocaleDictionary?: boolean;\n dictionaries?: string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n build?: boolean;\n};\n\ntype DictionariesStatus = {\n dictionary: Dictionary;\n status: 'pending' | 'pushing' | 'modified' | 'pushed' | 'unknown' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n// Print per-dictionary summary similar to loadDictionaries\nconst statusIconsAndColors = {\n pushed: { icon: '✔', color: ANSIColors.GREEN },\n modified: { icon: '✔', color: ANSIColors.GREEN },\n error: { icon: '✖', color: ANSIColors.RED },\n default: { icon: '⏲', color: ANSIColors.BLUE },\n};\n\nconst getIconAndColor = (status: DictionariesStatus['status']) => {\n return (\n statusIconsAndColors[status as keyof typeof statusIconsAndColors] ??\n statusIconsAndColors.default\n );\n};\n\n/**\n * Get all local dictionaries and push them simultaneously.\n */\nexport const push = async (options?: PushOptions): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n if (options?.build === true) {\n await prepareIntlayer(config, { forceRun: true });\n } else if (typeof options?.build === 'undefined') {\n await prepareIntlayer(config);\n }\n\n try {\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n let dictionaries: Dictionary[] = Object.values(unmergedDictionariesRecord)\n .flat()\n .filter((dictionary) => {\n const location =\n dictionary.location ?? config.dictionary?.location ?? 'local';\n\n return location === 'remote' || location === 'local&remote';\n });\n\n // Check if the dictionaries list is empty after filtering by location\n if (dictionaries.length === 0) {\n appLogger(\n `No dictionaries found to push. Only dictionaries with location ${colorize('remote', ANSIColors.BLUE, ANSIColors.RESET)} or ${colorize('local&remote', ANSIColors.BLUE, ANSIColors.RESET)} are pushed.`,\n { level: 'warn' }\n );\n appLogger(\n `You can set the location in your dictionary file (e.g. ${colorize(\"{ key: 'my-key', location: 'local&remote', ... }\", ANSIColors.BLUE, ANSIColors.RESET)} or globally in your intlayer.config.ts file (e.g. ${colorize(\"{ dictionary: { location: 'local&remote' } }\", ANSIColors.BLUE, ANSIColors.RESET)}).`,\n { level: 'info' }\n );\n return;\n }\n\n const existingDictionariesKeys: string[] = Object.keys(\n unmergedDictionariesRecord\n );\n\n if (options?.dictionaries) {\n // Check if the provided dictionaries exist\n const noneExistingDictionariesOption = options.dictionaries.filter(\n (dictionaryId) => !existingDictionariesKeys.includes(dictionaryId)\n );\n\n if (noneExistingDictionariesOption.length > 0) {\n appLogger(\n `The following dictionaries do not exist: ${noneExistingDictionariesOption.join(\n ', '\n )} and have been ignored.`,\n {\n level: 'error',\n }\n );\n }\n\n // Filter the dictionaries from the provided list of IDs\n dictionaries = dictionaries.filter((dictionary) =>\n options.dictionaries?.includes(dictionary.key)\n );\n }\n\n if (options?.gitOptions) {\n const gitFiles = await listGitFiles(options.gitOptions);\n\n dictionaries = dictionaries.filter((dictionary) =>\n gitFiles.includes(\n join(config.content.baseDir, dictionary.filePath ?? '')\n )\n );\n }\n\n // Check if the dictionaries list is empty\n if (dictionaries.length === 0) {\n appLogger('No local dictionaries found', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Pushing dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] = dictionaries.map(\n (dictionary) => ({\n dictionary,\n status: 'pending',\n })\n );\n\n // Initialize aggregated logger similar to loadDictionaries\n const logger = new PushLogger();\n logger.update(\n dictionariesStatuses.map<PushStatus>((s) => ({\n dictionaryKey: s.dictionary.key,\n status: 'pending',\n }))\n );\n\n const successfullyPushedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n statusObj.status = 'pushing';\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushing' },\n ]);\n\n try {\n const pushResult = await intlayerAPI.dictionary.pushDictionaries([\n statusObj.dictionary,\n ]);\n\n const updatedDictionaries = pushResult.data?.updatedDictionaries ?? [];\n const newDictionaries = pushResult.data?.newDictionaries ?? [];\n\n const allDictionaries = [...updatedDictionaries, ...newDictionaries];\n\n for (const remoteDictionaryData of allDictionaries) {\n const localDictionary = unmergedDictionariesRecord[\n remoteDictionaryData.key\n ]?.find(\n (dictionary) => dictionary.localId === remoteDictionaryData.localId\n );\n\n if (!localDictionary) continue;\n\n await writeContentDeclaration(\n { ...localDictionary, id: remoteDictionaryData.id },\n config\n );\n }\n\n if (\n updatedDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'modified';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'modified' },\n ]);\n } else if (\n newDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'pushed';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushed' },\n ]);\n } else {\n statusObj.status = 'unknown';\n }\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error pushing dictionary ${statusObj.dictionary.key}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with a concurrency limit (reuse parallelize)\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n for (const dictionaryStatus of dictionariesStatuses) {\n const { icon, color } = getIconAndColor(dictionaryStatus.status);\n appLogger(\n ` - ${colorizeKey(dictionaryStatus.dictionary.key)} ${ANSIColors.GREY}[${color}${icon} ${dictionaryStatus.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\n\n // Output any error messages\n for (const statusObj of dictionariesStatuses) {\n if (statusObj.errorMessage) {\n appLogger(statusObj.errorMessage, {\n level: 'error',\n });\n }\n }\n\n // Handle delete or keep options\n const deleteOption = options?.deleteLocaleDictionary;\n const keepOption = options?.keepLocaleDictionary;\n\n if (deleteOption && keepOption) {\n throw new Error(\n 'Cannot specify both --deleteLocaleDictionary and --keepLocaleDictionary options.'\n );\n }\n\n if (deleteOption) {\n // Delete only the successfully pushed dictionaries\n await deleteLocalDictionaries(successfullyPushedDictionaries, options);\n } else if (keepOption) {\n // Do nothing, keep the local dictionaries\n } else {\n // Ask the user\n const remoteDictionaries = successfullyPushedDictionaries.filter(\n (dictionary) => dictionary.location === 'remote'\n );\n const remoteDictionariesKeys = remoteDictionaries.map(\n (dictionary) => dictionary.key\n );\n const answer = await askUser(\n `Do you want to delete the local dictionaries that were successfully pushed? ${colorize('(Dictionaries:', ANSIColors.GREY, ANSIColors.RESET)} ${colorizeKey(remoteDictionariesKeys)}${colorize(')', ANSIColors.GREY, ANSIColors.RESET)} ${colorize('(yes/no)', ANSIColors.GREY_DARK, ANSIColors.RESET)}: `\n );\n if (answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y') {\n await deleteLocalDictionaries(remoteDictionaries, options);\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n\nconst askUser = (question: string): Promise<string> => {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n return new Promise((resolve) => {\n rl.question(question, (answer: string) => {\n rl.close();\n resolve(answer);\n });\n });\n};\n\nconst deleteLocalDictionaries = async (\n dictionariesToDelete: Dictionary[],\n options?: PushOptions\n): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n // Use a Set to collect all unique file paths\n const filePathsSet: Set<string> = new Set();\n\n for (const dictionary of dictionariesToDelete) {\n const { filePath } = dictionary;\n\n if (!filePath) {\n appLogger(\n `Dictionary ${colorizeKey(dictionary.key)} does not have a file path`,\n {\n level: 'error',\n }\n );\n continue;\n }\n\n filePathsSet.add(filePath);\n }\n\n for (const filePath of filePathsSet) {\n try {\n const stats = await fsPromises.lstat(filePath);\n\n if (stats.isFile()) {\n await fsPromises.unlink(filePath);\n appLogger(`Deleted file ${formatPath(filePath)}`, {});\n } else if (stats.isDirectory()) {\n appLogger(`Path is a directory ${formatPath(filePath)}, skipping.`, {});\n } else {\n appLogger(\n `Unknown file type for ${formatPath(filePath)}, skipping.`,\n {}\n );\n }\n } catch (err) {\n appLogger(`Error deleting ${formatPath(filePath)}: ${err}`, {\n level: 'error',\n });\n }\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"push.mjs","names":["logger","allDictionaries"],"sources":["../../../src/push/push.ts"],"sourcesContent":["import * as fsPromises from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n formatPath,\n type ListGitFilesOptions,\n listGitFiles,\n parallelize,\n prepareIntlayer,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colorize,\n colorizeKey,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n} from '@intlayer/config';\nimport type { Dictionary } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { PushLogger, type PushStatus } from '../pushLog';\nimport { checkCMSAuth } from '../utils/checkAccess';\n\ntype PushOptions = {\n deleteLocaleDictionary?: boolean;\n keepLocaleDictionary?: boolean;\n dictionaries?: string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n build?: boolean;\n};\n\ntype DictionariesStatus = {\n dictionary: Dictionary;\n status: 'pending' | 'pushing' | 'modified' | 'pushed' | 'unknown' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n// Print per-dictionary summary similar to loadDictionaries\nconst statusIconsAndColors = {\n pushed: { icon: '✔', color: ANSIColors.GREEN },\n modified: { icon: '✔', color: ANSIColors.GREEN },\n error: { icon: '✖', color: ANSIColors.RED },\n default: { icon: '⏲', color: ANSIColors.BLUE },\n};\n\nconst getIconAndColor = (status: DictionariesStatus['status']) => {\n return (\n statusIconsAndColors[status as keyof typeof statusIconsAndColors] ??\n statusIconsAndColors.default\n );\n};\n\n/**\n * Get all local dictionaries and push them simultaneously.\n */\nexport const push = async (options?: PushOptions): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n if (options?.build === true) {\n await prepareIntlayer(config, { forceRun: true });\n } else if (typeof options?.build === 'undefined') {\n await prepareIntlayer(config);\n }\n\n try {\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n const allDictionaries = Object.values(unmergedDictionariesRecord).flat();\n\n const customLocations = Array.from(\n new Set(\n allDictionaries\n .map((dictionary) => dictionary.location)\n .filter(\n (location) =>\n location &&\n !['remote', 'local', 'local&remote'].includes(location)\n )\n )\n ) as string[];\n\n let selectedCustomLocations: string[] = [];\n\n if (customLocations.length > 0) {\n const { multiselect, confirm, isCancel } = await import('@clack/prompts');\n\n if (customLocations.length === 1) {\n const shouldPush = await confirm({\n message: `Do you want to push dictionaries with custom location ${colorize(customLocations[0], ANSIColors.BLUE, ANSIColors.RESET)}?`,\n initialValue: false,\n });\n\n if (isCancel(shouldPush)) {\n return;\n }\n\n if (shouldPush) {\n selectedCustomLocations = [customLocations[0]];\n }\n } else {\n const selected = await multiselect({\n message: 'Select custom locations to push:',\n options: customLocations.map((location) => ({\n value: location,\n label: location,\n })),\n required: false,\n });\n\n if (isCancel(selected)) {\n return;\n }\n\n selectedCustomLocations = selected as string[];\n }\n }\n\n let dictionaries: Dictionary[] = allDictionaries.filter((dictionary) => {\n const location =\n dictionary.location ?? config.dictionary?.location ?? 'local';\n\n return (\n location === 'remote' ||\n location === 'local&remote' ||\n selectedCustomLocations.includes(location)\n );\n });\n\n // Check if the dictionaries list is empty after filtering by location\n if (dictionaries.length === 0) {\n appLogger(\n `No dictionaries found to push. Only dictionaries with location ${colorize('remote', ANSIColors.BLUE, ANSIColors.RESET)}, ${colorize('local&remote', ANSIColors.BLUE, ANSIColors.RESET)} or selected custom locations are pushed.`,\n { level: 'warn' }\n );\n appLogger(\n `You can set the location in your dictionary file (e.g. ${colorize(\"{ key: 'my-key', location: 'local&remote', ... }\", ANSIColors.BLUE, ANSIColors.RESET)} or globally in your intlayer.config.ts file (e.g. ${colorize(\"{ dictionary: { location: 'local&remote' } }\", ANSIColors.BLUE, ANSIColors.RESET)}).`,\n { level: 'info' }\n );\n return;\n }\n\n const existingDictionariesKeys: string[] = Object.keys(\n unmergedDictionariesRecord\n );\n\n if (options?.dictionaries) {\n // Check if the provided dictionaries exist\n const noneExistingDictionariesOption = options.dictionaries.filter(\n (dictionaryId) => !existingDictionariesKeys.includes(dictionaryId)\n );\n\n if (noneExistingDictionariesOption.length > 0) {\n appLogger(\n `The following dictionaries do not exist: ${noneExistingDictionariesOption.join(\n ', '\n )} and have been ignored.`,\n {\n level: 'error',\n }\n );\n }\n\n // Filter the dictionaries from the provided list of IDs\n dictionaries = dictionaries.filter((dictionary) =>\n options.dictionaries?.includes(dictionary.key)\n );\n }\n\n if (options?.gitOptions) {\n const gitFiles = await listGitFiles(options.gitOptions);\n\n dictionaries = dictionaries.filter((dictionary) =>\n gitFiles.includes(\n join(config.content.baseDir, dictionary.filePath ?? '')\n )\n );\n }\n\n // Check if the dictionaries list is empty\n if (dictionaries.length === 0) {\n appLogger('No local dictionaries found', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Pushing dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] = dictionaries.map(\n (dictionary) => ({\n dictionary,\n status: 'pending',\n })\n );\n\n // Initialize aggregated logger similar to loadDictionaries\n const logger = new PushLogger();\n logger.update(\n dictionariesStatuses.map<PushStatus>((s) => ({\n dictionaryKey: s.dictionary.key,\n status: 'pending',\n }))\n );\n\n const successfullyPushedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n statusObj.status = 'pushing';\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushing' },\n ]);\n\n try {\n const pushResult = await intlayerAPI.dictionary.pushDictionaries([\n statusObj.dictionary,\n ]);\n\n const updatedDictionaries = pushResult.data?.updatedDictionaries ?? [];\n const newDictionaries = pushResult.data?.newDictionaries ?? [];\n\n const allDictionaries = [...updatedDictionaries, ...newDictionaries];\n\n for (const remoteDictionaryData of allDictionaries) {\n const localDictionary = unmergedDictionariesRecord[\n remoteDictionaryData.key\n ]?.find(\n (dictionary) => dictionary.localId === remoteDictionaryData.localId\n );\n\n if (!localDictionary) continue;\n\n await writeContentDeclaration(\n { ...localDictionary, id: remoteDictionaryData.id },\n config\n );\n }\n\n if (\n updatedDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'modified';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'modified' },\n ]);\n } else if (\n newDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'pushed';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushed' },\n ]);\n } else {\n statusObj.status = 'unknown';\n }\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error pushing dictionary ${statusObj.dictionary.key}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with a concurrency limit (reuse parallelize)\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n for (const dictionaryStatus of dictionariesStatuses) {\n const { icon, color } = getIconAndColor(dictionaryStatus.status);\n appLogger(\n ` - ${colorizeKey(dictionaryStatus.dictionary.key)} ${ANSIColors.GREY}[${color}${icon} ${dictionaryStatus.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\n\n // Output any error messages\n for (const statusObj of dictionariesStatuses) {\n if (statusObj.errorMessage) {\n appLogger(statusObj.errorMessage, {\n level: 'error',\n });\n }\n }\n\n // Handle delete or keep options\n const deleteOption = options?.deleteLocaleDictionary;\n const keepOption = options?.keepLocaleDictionary;\n\n if (deleteOption && keepOption) {\n throw new Error(\n 'Cannot specify both --deleteLocaleDictionary and --keepLocaleDictionary options.'\n );\n }\n\n if (deleteOption) {\n // Delete only the successfully pushed dictionaries\n await deleteLocalDictionaries(successfullyPushedDictionaries, options);\n } else if (keepOption) {\n // Do nothing, keep the local dictionaries\n } else {\n // Ask the user\n const remoteDictionaries = successfullyPushedDictionaries.filter(\n (dictionary) => dictionary.location === 'remote'\n );\n const remoteDictionariesKeys = remoteDictionaries.map(\n (dictionary) => dictionary.key\n );\n\n if (remoteDictionaries.length > 0) {\n const { confirm, isCancel } = await import('@clack/prompts');\n\n const shouldDelete = await confirm({\n message: `Do you want to delete the local dictionaries that were successfully pushed? ${colorize('(Dictionaries:', ANSIColors.GREY, ANSIColors.RESET)} ${colorizeKey(remoteDictionariesKeys)}${colorize(')', ANSIColors.GREY, ANSIColors.RESET)}`,\n initialValue: false,\n });\n\n if (isCancel(shouldDelete)) {\n return;\n }\n\n if (shouldDelete) {\n await deleteLocalDictionaries(remoteDictionaries, options);\n }\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n\nconst deleteLocalDictionaries = async (\n dictionariesToDelete: Dictionary[],\n options?: PushOptions\n): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n // Use a Set to collect all unique file paths\n const filePathsSet: Set<string> = new Set();\n\n for (const dictionary of dictionariesToDelete) {\n const { filePath } = dictionary;\n\n if (!filePath) {\n appLogger(\n `Dictionary ${colorizeKey(dictionary.key)} does not have a file path`,\n {\n level: 'error',\n }\n );\n continue;\n }\n\n filePathsSet.add(filePath);\n }\n\n for (const filePath of filePathsSet) {\n try {\n const stats = await fsPromises.lstat(filePath);\n\n if (stats.isFile()) {\n await fsPromises.unlink(filePath);\n appLogger(`Deleted file ${formatPath(filePath)}`, {});\n } else if (stats.isDirectory()) {\n appLogger(`Path is a directory ${formatPath(filePath)}, skipping.`, {});\n } else {\n appLogger(\n `Unknown file type for ${formatPath(filePath)}, skipping.`,\n {}\n );\n }\n } catch (err) {\n appLogger(`Error deleting ${formatPath(filePath)}: ${err}`, {\n level: 'error',\n });\n }\n }\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","names":[],"sources":["../../src/config.ts"],"sourcesContent":[],"mappings":";;;KAMK,aAAA;kBACa;AAHQ,CAAA;AAMb,cAAA,SAAuB,EAAA,CAAA,OAAa,CAAb,EAAA,aAAa,EAAA,GAAA,IAAA"}
1
+ {"version":3,"file":"config.d.ts","names":[],"sources":["../../src/config.ts"],"sourcesContent":[],"mappings":";;;KAMK,aAAA;kBACa;AAHQ,CAAA;AAMb,cAAA,SAAuB,EAAA,CAAA,OAAa,CAAA,EAAb,aAAa,EAAA,GAAA,IAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"pull.d.ts","names":[],"sources":["../../src/pull.ts"],"sourcesContent":[],"mappings":";;;KAoBK,WAAA;;EAAA,mBAAW,CAAA,EAAA,MAGE;EAcL,aAwQZ,CAAA,EAtRiB,uBAciC;;;;;;cAAtC,iBAAwB,gBAAc"}
1
+ {"version":3,"file":"pull.d.ts","names":[],"sources":["../../src/pull.ts"],"sourcesContent":[],"mappings":";;;KAoBK,WAAA;;EAAA,mBAAW,CAAA,EAAA,MAGE;EAcL,aAyRZ,CAAA,EAvSiB,uBAciC;;;;;;cAAtC,iBAAwB,gBAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"push.d.ts","names":[],"sources":["../../../src/push/push.ts"],"sourcesContent":[],"mappings":";;;;KAyBK,WAAA;;EAAA,oBAAW,CAAA,EAAA,OAID;EA8BF,YAqOZ,CAAA,EAAA,MArOoC,EAAA;eA9BtB;kBACG;;;;;;cA6BL,iBAAwB,gBAAc"}
1
+ {"version":3,"file":"push.d.ts","names":[],"sources":["../../../src/push/push.ts"],"sourcesContent":[],"mappings":";;;;KAwBK,WAAA;;EAAA,oBAAW,CAAA,EAAA,OAID;EA8BF,YAoSZ,CAAA,EAAA,MApSoC,EAAA;eA9BtB;kBACG;;;;;;cA6BL,iBAAwB,gBAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"computeSimilarity.d.ts","names":[],"sources":["../../../src/translation-alignment/computeSimilarity.ts"],"sourcesContent":[],"mappings":";cACa,oEAGV;AAHU,cAkBA,wBAfV,EAAA,CAAA,CAAG,EAAA,MAAA,EAAA,CAAA,EAAA,MAAA,EAAA,aAAA,CAAA,EAAA,MAAA,EAAA,GAAA,MAAA"}
1
+ {"version":3,"file":"computeSimilarity.d.ts","names":[],"sources":["../../../src/translation-alignment/computeSimilarity.ts"],"sourcesContent":[],"mappings":";cACa,oEAGV;AAHU,cAkBA,wBAfV,EAAA,CAAG,CAAA,EAAA,MAAA,EAAA,CAAA,EAAA,MAAA,EAAA,aAAA,CAAA,EAAA,MAAA,EAAA,GAAA,MAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"rebuildDocument.d.ts","names":[],"sources":["../../../src/translation-alignment/rebuildDocument.ts"],"sourcesContent":[],"mappings":";;;KAEY,eAAA;gBACI;EADJ,eAAA,EAAA,MAAe,GAAA,IAAA;EAMf,WAAA,EAAA,MAAY;CACP;AACD,KAFJ,YAAA,GAEI;EACR,aAAA,EAFS,kBAET,EAAA;EAAa,YAAA,EADL,kBACK,EAAA;EAGT,IAAA,EAHJ,aAGiB;AAQzB,CAAA;AAAyC,KAR7B,aAAA,GAQ6B;EAAA,gBAAA,EAPrB,eAOqB,EAAA;CAAA;;;;AAiCzC;AACQ,cAlCK,wBAkCL,EAAA,CAAA;EAAA,aAAA;EAAA,YAAA;EAAA;AAAA,CAAA,EA9BL,YA8BK,EAAA,GA9BU,aA8BV;;;;cADK,8BACL,6BACQ,wCACI"}
1
+ {"version":3,"file":"rebuildDocument.d.ts","names":[],"sources":["../../../src/translation-alignment/rebuildDocument.ts"],"sourcesContent":[],"mappings":";;;KAEY,eAAA;gBACI;EADJ,eAAA,EAAA,MAAe,GAAA,IACX;EAKJ,WAAA,EAAA,MAAY;CACP;AACD,KAFJ,YAAA,GAEI;EACR,aAAA,EAFS,kBAET,EAAA;EAAa,YAAA,EADL,kBACK,EAAA;EAGT,IAAA,EAHJ,aAGiB;AAQzB,CAAA;AAAyC,KAR7B,aAAA,GAQ6B;EAAA,gBAAA,EAPrB,eAOqB,EAAA;CAAA;;;;AAiCzC;AACQ,cAlCK,wBAkCL,EAAA,CAAA;EAAA,aAAA;EAAA,YAAA;EAAA;AAAA,CAAA,EA9BL,YA8BK,EAAA,GA9BU,aA8BV;;;;cADK,8BACL,6BACQ,wCACI"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/cli",
3
- "version": "7.6.0-canary.1",
3
+ "version": "8.0.0-canary.1",
4
4
  "private": false,
5
5
  "description": "Provides uniform command-line interface scripts for Intlayer, used in packages like intlayer-cli and intlayer.",
6
6
  "keywords": [
@@ -68,21 +68,21 @@
68
68
  },
69
69
  "dependencies": {
70
70
  "@clack/prompts": "^0.11.0",
71
- "@intlayer/ai": "7.6.0-canary.1",
72
- "@intlayer/api": "7.6.0-canary.1",
73
- "@intlayer/chokidar": "7.6.0-canary.1",
74
- "@intlayer/config": "7.6.0-canary.1",
75
- "@intlayer/core": "7.6.0-canary.1",
76
- "@intlayer/dictionaries-entry": "7.6.0-canary.1",
77
- "@intlayer/remote-dictionaries-entry": "7.6.0-canary.1",
78
- "@intlayer/types": "7.6.0-canary.1",
79
- "@intlayer/unmerged-dictionaries-entry": "7.6.0-canary.1",
80
- "commander": "14.0.1",
81
- "eventsource": "3.0.7",
71
+ "@intlayer/ai": "8.0.0-canary.1",
72
+ "@intlayer/api": "8.0.0-canary.1",
73
+ "@intlayer/chokidar": "8.0.0-canary.1",
74
+ "@intlayer/config": "8.0.0-canary.1",
75
+ "@intlayer/core": "8.0.0-canary.1",
76
+ "@intlayer/dictionaries-entry": "8.0.0-canary.1",
77
+ "@intlayer/remote-dictionaries-entry": "8.0.0-canary.1",
78
+ "@intlayer/types": "8.0.0-canary.1",
79
+ "@intlayer/unmerged-dictionaries-entry": "8.0.0-canary.1",
80
+ "commander": "14.0.2",
81
+ "eventsource": "4.1.0",
82
82
  "fast-glob": "3.3.3"
83
83
  },
84
84
  "devDependencies": {
85
- "@types/node": "25.0.7",
85
+ "@types/node": "25.0.9",
86
86
  "@utils/ts-config": "1.0.4",
87
87
  "@utils/ts-config-types": "1.0.4",
88
88
  "@utils/tsdown-config": "1.0.4",
@@ -92,7 +92,7 @@
92
92
  "vitest": "4.0.17"
93
93
  },
94
94
  "peerDependencies": {
95
- "@intlayer/ai": "7.6.0-canary.1"
95
+ "@intlayer/ai": "8.0.0-canary.1"
96
96
  },
97
97
  "peerDependenciesMeta": {
98
98
  "@intlayer/ai": {