@windrun-huaiin/dev-scripts 6.8.1 → 6.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +137 -1410
  4. package/dist/cli.mjs +145 -432
  5. package/dist/commands/check-translations.d.ts +3 -0
  6. package/dist/commands/check-translations.d.ts.map +1 -0
  7. package/dist/commands/check-translations.js +132 -0
  8. package/dist/commands/check-translations.mjs +130 -0
  9. package/dist/commands/clean-translations.d.ts +3 -0
  10. package/dist/commands/clean-translations.d.ts.map +1 -0
  11. package/dist/commands/clean-translations.js +148 -0
  12. package/dist/commands/clean-translations.mjs +146 -0
  13. package/dist/commands/create-diaomao-app.d.ts +2 -0
  14. package/dist/commands/create-diaomao-app.d.ts.map +1 -0
  15. package/dist/commands/create-diaomao-app.js +151 -0
  16. package/dist/commands/create-diaomao-app.mjs +149 -0
  17. package/dist/commands/deep-clean.d.ts +3 -0
  18. package/dist/commands/deep-clean.d.ts.map +1 -0
  19. package/dist/commands/deep-clean.js +119 -0
  20. package/dist/commands/deep-clean.mjs +117 -0
  21. package/dist/commands/easy-changeset.d.ts +2 -0
  22. package/dist/commands/easy-changeset.d.ts.map +1 -0
  23. package/dist/commands/easy-changeset.js +39 -0
  24. package/dist/commands/easy-changeset.mjs +37 -0
  25. package/dist/commands/generate-blog-index.d.ts +3 -0
  26. package/dist/commands/generate-blog-index.d.ts.map +1 -0
  27. package/dist/commands/generate-blog-index.js +302 -0
  28. package/dist/commands/generate-blog-index.mjs +300 -0
  29. package/dist/commands/generate-nextjs-architecture.d.ts +3 -0
  30. package/dist/commands/generate-nextjs-architecture.d.ts.map +1 -0
  31. package/dist/commands/generate-nextjs-architecture.js +84 -0
  32. package/dist/commands/generate-nextjs-architecture.mjs +82 -0
  33. package/dist/config/index.d.ts +10 -0
  34. package/dist/config/index.d.ts.map +1 -0
  35. package/dist/config/index.js +173 -0
  36. package/dist/config/index.mjs +170 -0
  37. package/dist/config/schema.d.ts +34 -0
  38. package/dist/config/schema.d.ts.map +1 -0
  39. package/dist/config/schema.js +80 -0
  40. package/dist/config/schema.mjs +78 -0
  41. package/dist/index.d.ts +6 -49
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +9 -996
  44. package/dist/index.mjs +4 -3
  45. package/dist/utils/file-scanner.d.ts +22 -0
  46. package/dist/utils/file-scanner.d.ts.map +1 -0
  47. package/dist/utils/file-scanner.js +70 -0
  48. package/dist/utils/file-scanner.mjs +65 -0
  49. package/dist/utils/logger.d.ts +24 -0
  50. package/dist/utils/logger.d.ts.map +1 -0
  51. package/dist/utils/logger.js +63 -0
  52. package/dist/utils/logger.mjs +61 -0
  53. package/dist/utils/translation-parser.d.ts +29 -0
  54. package/dist/utils/translation-parser.d.ts.map +1 -0
  55. package/dist/utils/translation-parser.js +225 -0
  56. package/dist/utils/translation-parser.mjs +218 -0
  57. package/package.json +5 -5
  58. package/dist/chunk-GVR6HFHM.mjs +0 -989
  59. package/dist/chunk-GVR6HFHM.mjs.map +0 -1
  60. package/dist/cli.d.mts +0 -1
  61. package/dist/cli.js.map +0 -1
  62. package/dist/cli.mjs.map +0 -1
  63. package/dist/index.d.mts +0 -49
  64. package/dist/index.js.map +0 -1
  65. package/dist/index.mjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
- export { checkTranslations, cleanTranslations, generateBlogIndex, loadConfig, validateConfig } from './chunk-GVR6HFHM.mjs';
2
- //# sourceMappingURL=index.mjs.map
3
- //# sourceMappingURL=index.mjs.map
1
+ export { checkTranslations } from './commands/check-translations.mjs';
2
+ export { cleanTranslations } from './commands/clean-translations.mjs';
3
+ export { generateBlogIndex } from './commands/generate-blog-index.mjs';
4
+ export { loadConfig, validateConfig } from './config/index.mjs';
@@ -0,0 +1,22 @@
1
+ import { DevScriptsConfig } from '@dev-scripts/config/schema';
2
+ export interface ScanResult {
3
+ filePath: string;
4
+ content: string;
5
+ }
6
+ /**
7
+ * scan matching files
8
+ */
9
+ export declare function scanFiles(config: DevScriptsConfig, cwd?: string): Promise<ScanResult[]>;
10
+ /**
11
+ * read JSON file from given path
12
+ */
13
+ export declare function readJsonFile<T = any>(filePath: string): T | null;
14
+ /**
15
+ * get translation file path
16
+ */
17
+ export declare function getTranslationFilePath(locale: string, config: DevScriptsConfig, cwd?: string): string;
18
+ /**
19
+ * load all translation files
20
+ */
21
+ export declare function loadTranslations(config: DevScriptsConfig, cwd?: string): Record<string, Record<string, any>>;
22
+ //# sourceMappingURL=file-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-scanner.d.ts","sourceRoot":"","sources":["../../src/utils/file-scanner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAE7D,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,GAAE,MAA6D,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAsBnJ;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAOhE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,GAAE,MAA6D,GAAG,MAAM,CAE3J;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,GAAE,MAA6D,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAgBlK"}
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ var fg = require('fast-glob');
4
+ var fs = require('fs');
5
+
6
+ /**
7
+ * scan matching files
8
+ */
9
+ async function scanFiles(config, cwd = typeof process !== 'undefined' ? process.cwd() : '.') {
10
+ const files = await fg(config.scan.include, {
11
+ ignore: config.scan.exclude || [],
12
+ cwd,
13
+ absolute: false
14
+ });
15
+ const results = [];
16
+ for (const file of files) {
17
+ try {
18
+ const content = fs.readFileSync(file, 'utf8');
19
+ results.push({
20
+ filePath: file,
21
+ content
22
+ });
23
+ }
24
+ catch (error) {
25
+ console.warn(`Warning: Failed to read file ${file}: ${error}`);
26
+ }
27
+ }
28
+ return results;
29
+ }
30
+ /**
31
+ * read JSON file from given path
32
+ */
33
+ function readJsonFile(filePath) {
34
+ try {
35
+ const content = fs.readFileSync(filePath, 'utf8');
36
+ return JSON.parse(content);
37
+ }
38
+ catch (error) {
39
+ return null;
40
+ }
41
+ }
42
+ /**
43
+ * get translation file path
44
+ */
45
+ function getTranslationFilePath(locale, config, cwd = typeof process !== 'undefined' ? process.cwd() : '.') {
46
+ return `${cwd}/${config.i18n.messageRoot}/${locale}.json`;
47
+ }
48
+ /**
49
+ * load all translation files
50
+ */
51
+ function loadTranslations(config, cwd = typeof process !== 'undefined' ? process.cwd() : '.') {
52
+ const translations = {};
53
+ for (const locale of config.i18n.locales) {
54
+ const filePath = getTranslationFilePath(locale, config, cwd);
55
+ const translation = readJsonFile(filePath);
56
+ if (translation) {
57
+ translations[locale] = translation;
58
+ }
59
+ else {
60
+ console.warn(`Warning: Failed to load translation file for locale: ${locale}`);
61
+ translations[locale] = {};
62
+ }
63
+ }
64
+ return translations;
65
+ }
66
+
67
+ exports.getTranslationFilePath = getTranslationFilePath;
68
+ exports.loadTranslations = loadTranslations;
69
+ exports.readJsonFile = readJsonFile;
70
+ exports.scanFiles = scanFiles;
@@ -0,0 +1,65 @@
1
+ import fg from 'fast-glob';
2
+ import { readFileSync } from 'fs';
3
+
4
+ /**
5
+ * scan matching files
6
+ */
7
+ async function scanFiles(config, cwd = typeof process !== 'undefined' ? process.cwd() : '.') {
8
+ const files = await fg(config.scan.include, {
9
+ ignore: config.scan.exclude || [],
10
+ cwd,
11
+ absolute: false
12
+ });
13
+ const results = [];
14
+ for (const file of files) {
15
+ try {
16
+ const content = readFileSync(file, 'utf8');
17
+ results.push({
18
+ filePath: file,
19
+ content
20
+ });
21
+ }
22
+ catch (error) {
23
+ console.warn(`Warning: Failed to read file ${file}: ${error}`);
24
+ }
25
+ }
26
+ return results;
27
+ }
28
+ /**
29
+ * read JSON file from given path
30
+ */
31
+ function readJsonFile(filePath) {
32
+ try {
33
+ const content = readFileSync(filePath, 'utf8');
34
+ return JSON.parse(content);
35
+ }
36
+ catch (error) {
37
+ return null;
38
+ }
39
+ }
40
+ /**
41
+ * get translation file path
42
+ */
43
+ function getTranslationFilePath(locale, config, cwd = typeof process !== 'undefined' ? process.cwd() : '.') {
44
+ return `${cwd}/${config.i18n.messageRoot}/${locale}.json`;
45
+ }
46
+ /**
47
+ * load all translation files
48
+ */
49
+ function loadTranslations(config, cwd = typeof process !== 'undefined' ? process.cwd() : '.') {
50
+ const translations = {};
51
+ for (const locale of config.i18n.locales) {
52
+ const filePath = getTranslationFilePath(locale, config, cwd);
53
+ const translation = readJsonFile(filePath);
54
+ if (translation) {
55
+ translations[locale] = translation;
56
+ }
57
+ else {
58
+ console.warn(`Warning: Failed to load translation file for locale: ${locale}`);
59
+ translations[locale] = {};
60
+ }
61
+ }
62
+ return translations;
63
+ }
64
+
65
+ export { getTranslationFilePath, loadTranslations, readJsonFile, scanFiles };
@@ -0,0 +1,24 @@
1
+ import { DevScriptsConfig } from '@dev-scripts/config/schema';
2
+ export declare class Logger {
3
+ private messages;
4
+ private config;
5
+ constructor(config: DevScriptsConfig);
6
+ log(message: string): void;
7
+ error(message: string): void;
8
+ warn(message: string): void;
9
+ info(message: string): void;
10
+ success(message: string): void;
11
+ /**
12
+ * save log to file
13
+ */
14
+ saveToFile(filename: string, cwd?: string): void;
15
+ /**
16
+ * clear log messages
17
+ */
18
+ clear(): void;
19
+ /**
20
+ * get all log messages
21
+ */
22
+ getMessages(): string[];
23
+ }
24
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAE7D,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,MAAM,CAAkB;gBAEpB,MAAM,EAAE,gBAAgB;IAIpC,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO1B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK5B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK3B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK9B;;OAEG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,MAA6D,GAAG,IAAI;IAetG;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,WAAW,IAAI,MAAM,EAAE;CAGxB"}
@@ -0,0 +1,63 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+
6
+ class Logger {
7
+ constructor(config) {
8
+ this.messages = [];
9
+ this.config = config;
10
+ }
11
+ log(message) {
12
+ if (this.config.output.verbose) {
13
+ console.log(message);
14
+ }
15
+ this.messages.push(message);
16
+ }
17
+ error(message) {
18
+ console.error(message);
19
+ this.messages.push('[ERROR] ' + message);
20
+ }
21
+ warn(message) {
22
+ console.warn(message);
23
+ this.messages.push('[WARN] ' + message);
24
+ }
25
+ info(message) {
26
+ console.info(message);
27
+ this.messages.push('[INFO] ' + message);
28
+ }
29
+ success(message) {
30
+ console.log(`✅ ${message}`);
31
+ this.messages.push(`[SUCCESS] ${message}`);
32
+ }
33
+ /**
34
+ * save log to file
35
+ */
36
+ saveToFile(filename, cwd = typeof process !== 'undefined' ? process.cwd() : '.') {
37
+ try {
38
+ const logFilePath = path.join(cwd, this.config.output.logDir, filename);
39
+ const logDir = path.dirname(logFilePath);
40
+ // create log directory if it doesn't exist
41
+ fs.mkdirSync(logDir, { recursive: true });
42
+ fs.writeFileSync(logFilePath, this.messages.join('\n'), 'utf8');
43
+ console.log(`log saved to ${logFilePath}`);
44
+ }
45
+ catch (error) {
46
+ console.error(`failed to save log file: ${error}`);
47
+ }
48
+ }
49
+ /**
50
+ * clear log messages
51
+ */
52
+ clear() {
53
+ this.messages = [];
54
+ }
55
+ /**
56
+ * get all log messages
57
+ */
58
+ getMessages() {
59
+ return [...this.messages];
60
+ }
61
+ }
62
+
63
+ exports.Logger = Logger;
@@ -0,0 +1,61 @@
1
+ import { mkdirSync, writeFileSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+
4
+ class Logger {
5
+ constructor(config) {
6
+ this.messages = [];
7
+ this.config = config;
8
+ }
9
+ log(message) {
10
+ if (this.config.output.verbose) {
11
+ console.log(message);
12
+ }
13
+ this.messages.push(message);
14
+ }
15
+ error(message) {
16
+ console.error(message);
17
+ this.messages.push('[ERROR] ' + message);
18
+ }
19
+ warn(message) {
20
+ console.warn(message);
21
+ this.messages.push('[WARN] ' + message);
22
+ }
23
+ info(message) {
24
+ console.info(message);
25
+ this.messages.push('[INFO] ' + message);
26
+ }
27
+ success(message) {
28
+ console.log(`✅ ${message}`);
29
+ this.messages.push(`[SUCCESS] ${message}`);
30
+ }
31
+ /**
32
+ * save log to file
33
+ */
34
+ saveToFile(filename, cwd = typeof process !== 'undefined' ? process.cwd() : '.') {
35
+ try {
36
+ const logFilePath = join(cwd, this.config.output.logDir, filename);
37
+ const logDir = dirname(logFilePath);
38
+ // create log directory if it doesn't exist
39
+ mkdirSync(logDir, { recursive: true });
40
+ writeFileSync(logFilePath, this.messages.join('\n'), 'utf8');
41
+ console.log(`log saved to ${logFilePath}`);
42
+ }
43
+ catch (error) {
44
+ console.error(`failed to save log file: ${error}`);
45
+ }
46
+ }
47
+ /**
48
+ * clear log messages
49
+ */
50
+ clear() {
51
+ this.messages = [];
52
+ }
53
+ /**
54
+ * get all log messages
55
+ */
56
+ getMessages() {
57
+ return [...this.messages];
58
+ }
59
+ }
60
+
61
+ export { Logger };
@@ -0,0 +1,29 @@
1
+ export interface TranslationInfo {
2
+ namespaces: Map<string, string>;
3
+ keys: string[];
4
+ }
5
+ /**
6
+ * extract translation keys and namespaces from file content
7
+ */
8
+ export declare function extractTranslationsInfo(content: string, filePath: string): TranslationInfo;
9
+ /**
10
+ * get all keys from an object (including nested keys)
11
+ */
12
+ export declare function getAllKeys(obj: Record<string, any>, prefix?: string): string[];
13
+ /**
14
+ * check if the key exists in the translation file
15
+ */
16
+ export declare function checkKeyExists(key: string, translations: Record<string, any>): boolean;
17
+ /**
18
+ * check if the namespace exists in the translation file
19
+ */
20
+ export declare function checkNamespaceExists(namespace: string, translations: Record<string, any>): boolean;
21
+ /**
22
+ * remove the specified key from the translation object
23
+ */
24
+ export declare function removeKeyFromTranslations(key: string, translations: Record<string, any>): boolean;
25
+ /**
26
+ * clean empty objects (recursively)
27
+ */
28
+ export declare function cleanEmptyObjects(obj: Record<string, any>): Record<string, any>;
29
+ //# sourceMappingURL=translation-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translation-parser.d.ts","sourceRoot":"","sources":["../../src/utils/translation-parser.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,IAAI,EAAE,MAAM,EAAE,CAAA;CACf;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,CA8I1F;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAE,MAAW,GAAG,MAAM,EAAE,CAalF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAYtF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAElG;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAuBjG;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAa/E"}
@@ -0,0 +1,225 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * extract translation keys and namespaces from file content
5
+ */
6
+ function extractTranslationsInfo(content, filePath) {
7
+ const result = {
8
+ namespaces: new Map(),
9
+ keys: []
10
+ };
11
+ // match getTranslations({ locale, namespace: 'namespace' }) or getTranslations('namespace')
12
+ const getTranslationsPattern = /getTranslations\(\s*(?:{[^}]*namespace:\s*['"]([^'"]+)['"][^}]*}|['"]([^'"]+)['"])\s*\)/g;
13
+ let match;
14
+ while ((match = getTranslationsPattern.exec(content)) !== null) {
15
+ const namespace = match[1] || match[2];
16
+ if (namespace) {
17
+ // try to find assignment statement, like const t = await getTranslations(...)
18
+ // find the nearest const declaration
19
+ const linesBefore = content.substring(0, match.index).split('\n');
20
+ for (let i = linesBefore.length - 1; i >= Math.max(0, linesBefore.length - 5); i--) {
21
+ const line = linesBefore[i];
22
+ const constMatch = /const\s+(\w+)\s*=/.exec(line);
23
+ if (constMatch && !line.includes('useTranslations') && !line.includes('getTranslations')) {
24
+ result.namespaces.set(constMatch[1], namespace);
25
+ break;
26
+ }
27
+ }
28
+ }
29
+ }
30
+ // match useTranslations('namespace')
31
+ const useTranslationsPattern = /useTranslations\(\s*['"]([^'"]+)['"]\s*\)/g;
32
+ while ((match = useTranslationsPattern.exec(content)) !== null) {
33
+ const namespace = match[1];
34
+ // try to find assignment statement, like const t = useTranslations(...)
35
+ // find the line containing useTranslations
36
+ const currentLine = content.substring(0, match.index).split('\n').pop() || '';
37
+ const constMatch = /const\s+(\w+)\s*=/.exec(currentLine);
38
+ if (constMatch) {
39
+ result.namespaces.set(constMatch[1], namespace);
40
+ }
41
+ }
42
+ // match t('key') or t("key"), and check if t is associated with known namespaces
43
+ // modify the matching pattern of t function call
44
+ const tPatterns = [
45
+ // normal string key: t('key') or t("key")
46
+ /(\w+)\(\s*['"]([^'"]+)['"]\s*\)/g,
47
+ // template string key: t(`tags.${id}`) or t(`section.${key}`)
48
+ /(\w+)\(\s*`([^`]+)`\s*\)/g,
49
+ // variable key: t(item.key) or t(item.id)
50
+ /(\w+)\(\s*(\w+)\.(\w+)\s*\)/g
51
+ ];
52
+ for (const pattern of tPatterns) {
53
+ let match;
54
+ while ((match = pattern.exec(content)) !== null) {
55
+ const funcName = match[1];
56
+ // if the function name is associated with known namespaces
57
+ if (result.namespaces.has(funcName)) {
58
+ const namespace = result.namespaces.get(funcName);
59
+ if (!namespace)
60
+ continue;
61
+ if (pattern.source.includes('`')) {
62
+ // handle template string
63
+ const templateStr = match[2];
64
+ // extract static part (the part before the variable)
65
+ const staticPart = templateStr.split(/\${(?:id|key)}/)[0].trim();
66
+ if (staticPart && !staticPart.includes('/')) {
67
+ // for tags.${id}这样的形式,记录整个 tags 命名空间
68
+ const segments = staticPart.split('.');
69
+ if (segments.length > 0) {
70
+ // record the base path
71
+ result.keys.push(`${namespace}.${segments[0]}`);
72
+ // if it is multi-level, also record the full path
73
+ if (segments.length > 1) {
74
+ result.keys.push(`${namespace}.${segments.join('.')}`);
75
+ }
76
+ // special handling for tags namespace
77
+ if (segments[0] === 'tags') {
78
+ // add all known tag keys
79
+ ['productUpdates', 'tutorials', 'makeMoney', 'roadOverSea', 'insights'].forEach(tag => {
80
+ result.keys.push(`${namespace}.tags.${tag}`);
81
+ });
82
+ }
83
+ }
84
+ }
85
+ }
86
+ else if (pattern.source.includes('\\w+\\.\\w+')) {
87
+ // handle variable key t(item.key)
88
+ const varName = match[2];
89
+ match[3];
90
+ // find the possible value of the variable in the file content
91
+ const varPattern = new RegExp(`${varName}\\s*=\\s*{[^}]*key:\\s*['"]([^'"]+)['"]`);
92
+ const varMatch = content.match(varPattern);
93
+ if (varMatch) {
94
+ // if the variable definition is found, add the actual key
95
+ result.keys.push(`${namespace}.${varMatch[1]}`);
96
+ }
97
+ else {
98
+ // if the specific definition is not found, try to infer from the context
99
+ // check if it is used in an array or object of MenuItem type
100
+ if (content.includes('MenuItem[]') || content.includes('MenuItem}')) {
101
+ // add all possible menu keys
102
+ ['journey'].forEach(menuKey => {
103
+ result.keys.push(`${namespace}.${menuKey}`);
104
+ });
105
+ }
106
+ }
107
+ }
108
+ else {
109
+ // handle normal string key
110
+ const key = match[2];
111
+ if (!key.includes('/') && key !== '') {
112
+ result.keys.push(`${namespace}.${key}`);
113
+ }
114
+ }
115
+ }
116
+ }
117
+ }
118
+ // match <FormattedMessage id="key" />
119
+ const formattedMessagePattern = /<FormattedMessage[^>]*id=['"]([^'"]+)['"]/g;
120
+ while ((match = formattedMessagePattern.exec(content)) !== null) {
121
+ const key = match[1];
122
+ if (!key.includes('/') && key !== '') {
123
+ // for FormattedMessage, we need to guess the namespace
124
+ // usually we can find useTranslations call in the same file
125
+ if (result.namespaces.size > 0) {
126
+ const namespace = Array.from(result.namespaces.values())[0];
127
+ result.keys.push(`${namespace}.${key}`);
128
+ }
129
+ else {
130
+ // if the namespace is not found, try to infer from the file path
131
+ const pathMatch = filePath.match(/\[locale\]\/(?:\([^)]+\)\/)?([^/]+)/);
132
+ if (pathMatch && pathMatch[1]) {
133
+ const possibleNamespace = pathMatch[1];
134
+ result.keys.push(`${possibleNamespace}.${key}`);
135
+ }
136
+ }
137
+ }
138
+ }
139
+ return result;
140
+ }
141
+ /**
142
+ * get all keys from an object (including nested keys)
143
+ */
144
+ function getAllKeys(obj, prefix = '') {
145
+ let keys = [];
146
+ for (const key in obj) {
147
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
148
+ const newKey = prefix ? `${prefix}.${key}` : key;
149
+ if (typeof obj[key] === 'object' && obj[key] !== null) {
150
+ keys = [...keys, ...getAllKeys(obj[key], newKey)];
151
+ }
152
+ else {
153
+ keys.push(newKey);
154
+ }
155
+ }
156
+ }
157
+ return keys;
158
+ }
159
+ /**
160
+ * check if the key exists in the translation file
161
+ */
162
+ function checkKeyExists(key, translations) {
163
+ const parts = key.split('.');
164
+ let current = translations;
165
+ for (const part of parts) {
166
+ if (current[part] === undefined) {
167
+ return false;
168
+ }
169
+ current = current[part];
170
+ }
171
+ return true;
172
+ }
173
+ /**
174
+ * check if the namespace exists in the translation file
175
+ */
176
+ function checkNamespaceExists(namespace, translations) {
177
+ return translations[namespace] !== undefined;
178
+ }
179
+ /**
180
+ * remove the specified key from the translation object
181
+ */
182
+ function removeKeyFromTranslations(key, translations) {
183
+ const parts = key.split('.');
184
+ const lastPart = parts.pop();
185
+ if (!lastPart)
186
+ return false;
187
+ let current = translations;
188
+ // navigate to the parent object of the last level
189
+ for (const part of parts) {
190
+ if (current[part] === undefined || typeof current[part] !== 'object') {
191
+ return false;
192
+ }
193
+ current = current[part];
194
+ }
195
+ // delete the key
196
+ if (current[lastPart] !== undefined) {
197
+ delete current[lastPart];
198
+ return true;
199
+ }
200
+ return false;
201
+ }
202
+ /**
203
+ * clean empty objects (recursively)
204
+ */
205
+ function cleanEmptyObjects(obj) {
206
+ for (const key in obj) {
207
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
208
+ if (typeof obj[key] === 'object' && obj[key] !== null) {
209
+ obj[key] = cleanEmptyObjects(obj[key]);
210
+ // if the object is empty, delete it
211
+ if (Object.keys(obj[key]).length === 0) {
212
+ delete obj[key];
213
+ }
214
+ }
215
+ }
216
+ }
217
+ return obj;
218
+ }
219
+
220
+ exports.checkKeyExists = checkKeyExists;
221
+ exports.checkNamespaceExists = checkNamespaceExists;
222
+ exports.cleanEmptyObjects = cleanEmptyObjects;
223
+ exports.extractTranslationsInfo = extractTranslationsInfo;
224
+ exports.getAllKeys = getAllKeys;
225
+ exports.removeKeyFromTranslations = removeKeyFromTranslations;