@windrun-huaiin/dev-scripts 6.8.2 → 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.
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +137 -1410
- package/dist/cli.mjs +145 -432
- package/dist/commands/check-translations.d.ts +3 -0
- package/dist/commands/check-translations.d.ts.map +1 -0
- package/dist/commands/check-translations.js +132 -0
- package/dist/commands/check-translations.mjs +130 -0
- package/dist/commands/clean-translations.d.ts +3 -0
- package/dist/commands/clean-translations.d.ts.map +1 -0
- package/dist/commands/clean-translations.js +148 -0
- package/dist/commands/clean-translations.mjs +146 -0
- package/dist/commands/create-diaomao-app.d.ts +2 -0
- package/dist/commands/create-diaomao-app.d.ts.map +1 -0
- package/dist/commands/create-diaomao-app.js +151 -0
- package/dist/commands/create-diaomao-app.mjs +149 -0
- package/dist/commands/deep-clean.d.ts +3 -0
- package/dist/commands/deep-clean.d.ts.map +1 -0
- package/dist/commands/deep-clean.js +119 -0
- package/dist/commands/deep-clean.mjs +117 -0
- package/dist/commands/easy-changeset.d.ts +2 -0
- package/dist/commands/easy-changeset.d.ts.map +1 -0
- package/dist/commands/easy-changeset.js +39 -0
- package/dist/commands/easy-changeset.mjs +37 -0
- package/dist/commands/generate-blog-index.d.ts +3 -0
- package/dist/commands/generate-blog-index.d.ts.map +1 -0
- package/dist/commands/generate-blog-index.js +302 -0
- package/dist/commands/generate-blog-index.mjs +300 -0
- package/dist/commands/generate-nextjs-architecture.d.ts +3 -0
- package/dist/commands/generate-nextjs-architecture.d.ts.map +1 -0
- package/dist/commands/generate-nextjs-architecture.js +84 -0
- package/dist/commands/generate-nextjs-architecture.mjs +82 -0
- package/dist/config/index.d.ts +10 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +173 -0
- package/dist/config/index.mjs +170 -0
- package/dist/config/schema.d.ts +34 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +80 -0
- package/dist/config/schema.mjs +78 -0
- package/dist/index.d.ts +6 -49
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -996
- package/dist/index.mjs +4 -3
- package/dist/utils/file-scanner.d.ts +22 -0
- package/dist/utils/file-scanner.d.ts.map +1 -0
- package/dist/utils/file-scanner.js +70 -0
- package/dist/utils/file-scanner.mjs +65 -0
- package/dist/utils/logger.d.ts +24 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +63 -0
- package/dist/utils/logger.mjs +61 -0
- package/dist/utils/translation-parser.d.ts +29 -0
- package/dist/utils/translation-parser.d.ts.map +1 -0
- package/dist/utils/translation-parser.js +225 -0
- package/dist/utils/translation-parser.mjs +218 -0
- package/package.json +5 -5
- package/dist/chunk-GVR6HFHM.mjs +0 -989
- package/dist/chunk-GVR6HFHM.mjs.map +0 -1
- package/dist/cli.d.mts +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cli.mjs.map +0 -1
- package/dist/index.d.mts +0 -49
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { checkTranslations
|
|
2
|
-
|
|
3
|
-
|
|
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;
|