gtx-cli 2.0.17 → 2.0.19
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/CHANGELOG.md +14 -0
- package/dist/api/downloadFile.js +16 -0
- package/dist/api/downloadFileBatch.js +16 -0
- package/dist/config/generateSettings.js +22 -9
- package/dist/config/optionPresets.d.ts +2 -2
- package/dist/config/optionPresets.js +49 -37
- package/dist/formats/files/supportedFiles.d.ts +2 -1
- package/dist/formats/files/supportedFiles.js +2 -0
- package/dist/formats/files/translate.js +20 -1
- package/dist/formats/files/upload.js +21 -1
- package/dist/formats/yaml/mergeYaml.d.ts +5 -0
- package/dist/formats/yaml/mergeYaml.js +55 -0
- package/dist/formats/yaml/parseYaml.d.ts +2 -0
- package/dist/formats/yaml/parseYaml.js +23 -0
- package/dist/formats/yaml/utils.d.ts +2 -0
- package/dist/formats/yaml/utils.js +22 -0
- package/dist/fs/config/parseFilesConfig.d.ts +6 -2
- package/dist/fs/config/parseFilesConfig.js +6 -4
- package/dist/next/parse/handleInitGT.d.ts +7 -1
- package/dist/next/parse/handleInitGT.js +103 -12
- package/dist/setup/wizard.js +5 -1
- package/dist/types/index.d.ts +8 -1
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# gtx-cli
|
|
2
2
|
|
|
3
|
+
## 2.0.19
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#519](https://github.com/generaltranslation/gt/pull/519) [`2ba4848`](https://github.com/generaltranslation/gt/commit/2ba48486603ef5e8e4026d89dc36311ce6505b81) Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - feat: exclude files with the [locales] tag
|
|
8
|
+
|
|
9
|
+
## 2.0.18
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#507](https://github.com/generaltranslation/gt/pull/507) [`8f80795`](https://github.com/generaltranslation/gt/commit/8f80795daf862f769be728c044d48e5e28d7b126) Thanks [@SamEggert](https://github.com/SamEggert)! - Setup wizard detects and matches your module system when modifying the next.config
|
|
14
|
+
|
|
15
|
+
- [#517](https://github.com/generaltranslation/gt/pull/517) [`452da1e`](https://github.com/generaltranslation/gt/commit/452da1e1f0f6d7825b82bc66fb5d3dd6e7a6ad92) Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - feat: add support for yaml
|
|
16
|
+
|
|
3
17
|
## 2.0.17
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/dist/api/downloadFile.js
CHANGED
|
@@ -5,6 +5,8 @@ import { gt } from '../utils/gt.js';
|
|
|
5
5
|
import { validateJsonSchema } from '../formats/json/utils.js';
|
|
6
6
|
import { mergeJson } from '../formats/json/mergeJson.js';
|
|
7
7
|
import { TextDecoder } from 'node:util';
|
|
8
|
+
import mergeYaml from '../formats/yaml/mergeYaml.js';
|
|
9
|
+
import { validateYamlSchema } from '../formats/yaml/utils.js';
|
|
8
10
|
/**
|
|
9
11
|
* Downloads a file from the API and saves it to a local directory
|
|
10
12
|
* @param translationId - The ID of the translation to download
|
|
@@ -38,6 +40,20 @@ export async function downloadFile(translationId, outputPath, inputPath, locale,
|
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
42
|
}
|
|
43
|
+
if (options.options?.yamlSchema && locale) {
|
|
44
|
+
const yamlSchema = validateYamlSchema(options.options, outputPath);
|
|
45
|
+
if (yamlSchema) {
|
|
46
|
+
const originalContent = fs.readFileSync(outputPath, 'utf8');
|
|
47
|
+
if (originalContent) {
|
|
48
|
+
data = mergeYaml(originalContent, outputPath, options.options, [
|
|
49
|
+
{
|
|
50
|
+
translatedContent: data,
|
|
51
|
+
targetLocale: locale,
|
|
52
|
+
},
|
|
53
|
+
])[0];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
41
57
|
// Write the file to disk
|
|
42
58
|
await fs.promises.writeFile(outputPath, data);
|
|
43
59
|
return true;
|
|
@@ -3,7 +3,9 @@ import * as path from 'path';
|
|
|
3
3
|
import { logError, logWarning } from '../console/logging.js';
|
|
4
4
|
import { gt } from '../utils/gt.js';
|
|
5
5
|
import { validateJsonSchema } from '../formats/json/utils.js';
|
|
6
|
+
import { validateYamlSchema } from '../formats/yaml/utils.js';
|
|
6
7
|
import { mergeJson } from '../formats/json/mergeJson.js';
|
|
8
|
+
import mergeYaml from '../formats/yaml/mergeYaml.js';
|
|
7
9
|
/**
|
|
8
10
|
* Downloads multiple translation files in a single batch request
|
|
9
11
|
* @param files - Array of files to download with their output paths
|
|
@@ -56,6 +58,20 @@ export async function downloadFileBatch(files, options, maxRetries = 3, retryDel
|
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
60
|
}
|
|
61
|
+
if (options.options?.yamlSchema && locale) {
|
|
62
|
+
const yamlSchema = validateYamlSchema(options.options, inputPath);
|
|
63
|
+
if (yamlSchema) {
|
|
64
|
+
const originalContent = fs.readFileSync(inputPath, 'utf8');
|
|
65
|
+
if (originalContent) {
|
|
66
|
+
data = mergeYaml(originalContent, inputPath, options.options, [
|
|
67
|
+
{
|
|
68
|
+
translatedContent: file.data,
|
|
69
|
+
targetLocale: locale,
|
|
70
|
+
},
|
|
71
|
+
])[0];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
59
75
|
// Write the file to disk
|
|
60
76
|
await fs.promises.writeFile(outputPath, data);
|
|
61
77
|
result.successful.push(translationId);
|
|
@@ -105,17 +105,30 @@ export async function generateSettings(options, cwd = process.cwd()) {
|
|
|
105
105
|
mergedOptions.src = mergedOptions.src || DEFAULT_SRC_PATTERNS;
|
|
106
106
|
// Resolve all glob patterns in the files object
|
|
107
107
|
mergedOptions.files = mergedOptions.files
|
|
108
|
-
? resolveFiles(mergedOptions.files, mergedOptions.defaultLocale, cwd)
|
|
108
|
+
? resolveFiles(mergedOptions.files, mergedOptions.defaultLocale, mergedOptions.locales, cwd)
|
|
109
109
|
: undefined;
|
|
110
110
|
// Add additional options if provided
|
|
111
|
-
if (mergedOptions.options
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
111
|
+
if (mergedOptions.options) {
|
|
112
|
+
if (mergedOptions.options.jsonSchema) {
|
|
113
|
+
for (const fileGlob of Object.keys(mergedOptions.options.jsonSchema)) {
|
|
114
|
+
const jsonSchema = mergedOptions.options.jsonSchema[fileGlob];
|
|
115
|
+
if (jsonSchema.preset) {
|
|
116
|
+
mergedOptions.options.jsonSchema[fileGlob] = {
|
|
117
|
+
...generatePreset(jsonSchema.preset, 'json'),
|
|
118
|
+
...jsonSchema,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (mergedOptions.options.yamlSchema) {
|
|
124
|
+
for (const fileGlob of Object.keys(mergedOptions.options.yamlSchema)) {
|
|
125
|
+
const yamlSchema = mergedOptions.options.yamlSchema[fileGlob];
|
|
126
|
+
if (yamlSchema.preset) {
|
|
127
|
+
mergedOptions.options.yamlSchema[fileGlob] = {
|
|
128
|
+
...generatePreset(yamlSchema.preset, 'yaml'),
|
|
129
|
+
...yamlSchema,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
119
132
|
}
|
|
120
133
|
}
|
|
121
134
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { JsonSchema } from '../types/index.js';
|
|
2
|
-
export declare function generatePreset(preset: string): JsonSchema;
|
|
1
|
+
import { JsonSchema, YamlSchema } from '../types/index.js';
|
|
2
|
+
export declare function generatePreset(preset: string, type: 'json' | 'yaml'): JsonSchema | YamlSchema;
|
|
@@ -1,44 +1,56 @@
|
|
|
1
|
-
export function generatePreset(preset) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
export function generatePreset(preset, type) {
|
|
2
|
+
if (type === 'json') {
|
|
3
|
+
switch (preset) {
|
|
4
|
+
case 'mintlify':
|
|
5
|
+
// https://mintlify.com/docs/navigation
|
|
6
|
+
return {
|
|
7
|
+
composite: {
|
|
8
|
+
'$.navigation.languages': {
|
|
9
|
+
type: 'array',
|
|
10
|
+
key: '$.language',
|
|
11
|
+
include: [
|
|
12
|
+
'$..group',
|
|
13
|
+
'$..tab',
|
|
14
|
+
'$..item',
|
|
15
|
+
'$..anchor',
|
|
16
|
+
'$..dropdown',
|
|
17
|
+
],
|
|
18
|
+
transform: {
|
|
19
|
+
'$..pages[*]': {
|
|
20
|
+
match: '^{locale}/(.*)$',
|
|
21
|
+
replace: '{locale}/$1',
|
|
22
|
+
},
|
|
21
23
|
},
|
|
22
24
|
},
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
'$.redirects': {
|
|
26
|
+
type: 'array',
|
|
27
|
+
key: '$.language',
|
|
28
|
+
include: [],
|
|
29
|
+
transform: {
|
|
30
|
+
'$.source': {
|
|
31
|
+
match: '^/{locale}/(.*)$',
|
|
32
|
+
replace: '/{locale}/$1',
|
|
33
|
+
},
|
|
34
|
+
'$.destination': {
|
|
35
|
+
match: '^/{locale}/(.*)$',
|
|
36
|
+
replace: '/{locale}/$1',
|
|
37
|
+
},
|
|
36
38
|
},
|
|
37
39
|
},
|
|
38
40
|
},
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
};
|
|
42
|
+
default:
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
switch (preset) {
|
|
48
|
+
case 'mintlify':
|
|
49
|
+
return {
|
|
50
|
+
include: ['$..summary', '$..description'],
|
|
51
|
+
};
|
|
52
|
+
default:
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
43
55
|
}
|
|
44
56
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
export declare const SUPPORTED_FILE_EXTENSIONS: readonly ["json", "mdx", "md", "ts", "js"];
|
|
1
|
+
export declare const SUPPORTED_FILE_EXTENSIONS: readonly ["json", "mdx", "md", "ts", "js", "yaml"];
|
|
2
2
|
export declare const FILE_EXT_TO_EXT_LABEL: {
|
|
3
3
|
json: string;
|
|
4
4
|
mdx: string;
|
|
5
5
|
md: string;
|
|
6
6
|
ts: string;
|
|
7
7
|
js: string;
|
|
8
|
+
yaml: string;
|
|
8
9
|
};
|
|
@@ -4,6 +4,7 @@ export const SUPPORTED_FILE_EXTENSIONS = [
|
|
|
4
4
|
'md',
|
|
5
5
|
'ts',
|
|
6
6
|
'js',
|
|
7
|
+
'yaml',
|
|
7
8
|
];
|
|
8
9
|
export const FILE_EXT_TO_EXT_LABEL = {
|
|
9
10
|
json: 'JSON',
|
|
@@ -11,4 +12,5 @@ export const FILE_EXT_TO_EXT_LABEL = {
|
|
|
11
12
|
md: 'Markdown',
|
|
12
13
|
ts: 'TypeScript',
|
|
13
14
|
js: 'JavaScript',
|
|
15
|
+
yaml: 'YAML',
|
|
14
16
|
};
|
|
@@ -10,6 +10,7 @@ import { SUPPORTED_FILE_EXTENSIONS } from './supportedFiles.js';
|
|
|
10
10
|
import sanitizeFileContent from '../../utils/sanitizeFileContent.js';
|
|
11
11
|
import { parseJson } from '../json/parseJson.js';
|
|
12
12
|
import { createFileMapping } from './fileMapping.js';
|
|
13
|
+
import parseYaml from '../yaml/parseYaml.js';
|
|
13
14
|
const SUPPORTED_DATA_FORMATS = ['JSX', 'ICU', 'I18NEXT'];
|
|
14
15
|
/**
|
|
15
16
|
* Sends multiple files to the API for translation
|
|
@@ -42,8 +43,26 @@ export async function translateFiles(filePaths, placeholderPaths, transformPaths
|
|
|
42
43
|
});
|
|
43
44
|
allFiles.push(...jsonFiles);
|
|
44
45
|
}
|
|
46
|
+
// Process YAML files
|
|
47
|
+
if (filePaths.yaml) {
|
|
48
|
+
if (!SUPPORTED_DATA_FORMATS.includes(dataFormat)) {
|
|
49
|
+
logErrorAndExit(noSupportedFormatError);
|
|
50
|
+
}
|
|
51
|
+
const yamlFiles = filePaths.yaml.map((filePath) => {
|
|
52
|
+
const content = readFile(filePath);
|
|
53
|
+
const parsedYaml = parseYaml(content, filePath, additionalOptions);
|
|
54
|
+
const relativePath = getRelative(filePath);
|
|
55
|
+
return {
|
|
56
|
+
content: parsedYaml,
|
|
57
|
+
fileName: relativePath,
|
|
58
|
+
fileFormat: 'JSON', // Translate as a JSON file
|
|
59
|
+
dataFormat,
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
allFiles.push(...yamlFiles);
|
|
63
|
+
}
|
|
45
64
|
for (const fileType of SUPPORTED_FILE_EXTENSIONS) {
|
|
46
|
-
if (fileType === 'json')
|
|
65
|
+
if (fileType === 'json' || fileType === 'yaml')
|
|
47
66
|
continue;
|
|
48
67
|
if (filePaths[fileType]) {
|
|
49
68
|
const files = filePaths[fileType].map((filePath) => {
|
|
@@ -10,6 +10,7 @@ import { parseJson } from '../json/parseJson.js';
|
|
|
10
10
|
import { uploadFiles } from '../../api/uploadFiles.js';
|
|
11
11
|
import { existsSync, readFileSync } from 'node:fs';
|
|
12
12
|
import { createFileMapping } from './fileMapping.js';
|
|
13
|
+
import parseYaml from '../yaml/parseYaml.js';
|
|
13
14
|
const SUPPORTED_DATA_FORMATS = ['JSX', 'ICU', 'I18NEXT'];
|
|
14
15
|
/**
|
|
15
16
|
* Sends multiple files to the API for translation
|
|
@@ -43,8 +44,27 @@ export async function upload(filePaths, placeholderPaths, transformPaths, dataFo
|
|
|
43
44
|
});
|
|
44
45
|
allFiles.push(...jsonFiles);
|
|
45
46
|
}
|
|
47
|
+
// Process YAML files
|
|
48
|
+
if (filePaths.yaml) {
|
|
49
|
+
if (!SUPPORTED_DATA_FORMATS.includes(dataFormat)) {
|
|
50
|
+
logErrorAndExit(noSupportedFormatError);
|
|
51
|
+
}
|
|
52
|
+
const yamlFiles = filePaths.yaml.map((filePath) => {
|
|
53
|
+
const content = readFile(filePath);
|
|
54
|
+
const parsedYaml = parseYaml(content, filePath, additionalOptions);
|
|
55
|
+
const relativePath = getRelative(filePath);
|
|
56
|
+
return {
|
|
57
|
+
content: parsedYaml,
|
|
58
|
+
fileName: relativePath,
|
|
59
|
+
fileFormat: 'JSON', // Translate as a JSON file
|
|
60
|
+
dataFormat,
|
|
61
|
+
locale: options.defaultLocale,
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
allFiles.push(...yamlFiles);
|
|
65
|
+
}
|
|
46
66
|
for (const fileType of SUPPORTED_FILE_EXTENSIONS) {
|
|
47
|
-
if (fileType === 'json')
|
|
67
|
+
if (fileType === 'json' || fileType === 'yaml')
|
|
48
68
|
continue;
|
|
49
69
|
if (filePaths[fileType]) {
|
|
50
70
|
const files = filePaths[fileType].map((filePath) => {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import JSONPointer from 'jsonpointer';
|
|
2
|
+
import { exit, logError } from '../../console/logging.js';
|
|
3
|
+
import { validateYamlSchema } from './utils.js';
|
|
4
|
+
import YAML from 'yaml';
|
|
5
|
+
export default function mergeYaml(originalContent, inputPath, options, targets) {
|
|
6
|
+
const yamlSchema = validateYamlSchema(options, inputPath);
|
|
7
|
+
if (!yamlSchema) {
|
|
8
|
+
return targets.map((target) => target.translatedContent);
|
|
9
|
+
}
|
|
10
|
+
let originalYaml;
|
|
11
|
+
try {
|
|
12
|
+
originalYaml = YAML.parse(originalContent);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
logError(`Invalid YAML file: ${inputPath}`);
|
|
16
|
+
exit(1);
|
|
17
|
+
}
|
|
18
|
+
// Unreachable (validated in validateYamlSchema, included for type check)
|
|
19
|
+
if (!yamlSchema.include) {
|
|
20
|
+
logError('No include property found in YAML schema');
|
|
21
|
+
exit(1);
|
|
22
|
+
}
|
|
23
|
+
// Handle include
|
|
24
|
+
const output = [];
|
|
25
|
+
for (const target of targets) {
|
|
26
|
+
// Must clone the original YAML to avoid mutations
|
|
27
|
+
const mergedYaml = JSON.parse(JSON.stringify(originalYaml));
|
|
28
|
+
let translatedJson;
|
|
29
|
+
try {
|
|
30
|
+
translatedJson = JSON.parse(target.translatedContent);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// If parsing fails, treat as empty object to avoid crashes
|
|
34
|
+
translatedJson = {};
|
|
35
|
+
}
|
|
36
|
+
for (const [jsonPointer, translatedValue] of Object.entries(translatedJson)) {
|
|
37
|
+
try {
|
|
38
|
+
// Try to get the value - if this succeeds, the pointer exists
|
|
39
|
+
if (JSONPointer.get(mergedYaml, jsonPointer) == null) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
// Set the new value regardless of what the current value is (including null/falsy)
|
|
43
|
+
JSONPointer.set(mergedYaml, jsonPointer, translatedValue);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Silently ignore invalid or non-existent JSON pointers
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
output.push(YAML.stringify(mergedYaml));
|
|
50
|
+
}
|
|
51
|
+
if (!output.length) {
|
|
52
|
+
return [originalContent];
|
|
53
|
+
}
|
|
54
|
+
return output;
|
|
55
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { exit, logError } from '../../console/logging.js';
|
|
2
|
+
import YAML from 'yaml';
|
|
3
|
+
import { validateYamlSchema } from './utils.js';
|
|
4
|
+
import { flattenJsonWithStringFilter } from '../json/flattenJson.js';
|
|
5
|
+
export default function parseYaml(content, filePath, options) {
|
|
6
|
+
const yamlSchema = validateYamlSchema(options, filePath);
|
|
7
|
+
if (!yamlSchema) {
|
|
8
|
+
return content;
|
|
9
|
+
}
|
|
10
|
+
let yaml;
|
|
11
|
+
try {
|
|
12
|
+
yaml = YAML.parse(content);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
logError(`Invalid YAML file: ${filePath}`);
|
|
16
|
+
exit(1);
|
|
17
|
+
}
|
|
18
|
+
if (yamlSchema.include) {
|
|
19
|
+
const flattenedYaml = flattenJsonWithStringFilter(yaml, yamlSchema.include);
|
|
20
|
+
return JSON.stringify(flattenedYaml);
|
|
21
|
+
}
|
|
22
|
+
return content;
|
|
23
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { logError, exit } from '../../console/logging.js';
|
|
2
|
+
import micromatch from 'micromatch';
|
|
3
|
+
const { isMatch } = micromatch;
|
|
4
|
+
import path from 'path';
|
|
5
|
+
export function validateYamlSchema(options, filePath) {
|
|
6
|
+
if (!options.yamlSchema) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
// Check if the file matches any of the yaml schema globs
|
|
10
|
+
const fileGlobs = Object.keys(options.yamlSchema);
|
|
11
|
+
const matchingGlob = fileGlobs.find((fileGlob) => isMatch(path.relative(process.cwd(), filePath), fileGlob));
|
|
12
|
+
if (!matchingGlob || !options.yamlSchema[matchingGlob]) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
// Validate includes
|
|
16
|
+
const yamlSchema = options.yamlSchema[matchingGlob];
|
|
17
|
+
if (!yamlSchema.include) {
|
|
18
|
+
logError('No include property found in YAML schema');
|
|
19
|
+
exit(1);
|
|
20
|
+
}
|
|
21
|
+
return yamlSchema;
|
|
22
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FilesOptions, ResolvedFiles, TransformFiles } from '../../types/index.js';
|
|
1
|
+
import { FilesOptions, ResolvedFiles, TransformFiles, TransformOption } from '../../types/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Resolves the files from the files object
|
|
4
4
|
* Replaces [locale] with the actual locale in the files
|
|
@@ -16,8 +16,12 @@ export declare function resolveLocaleFiles(files: ResolvedFiles, locale: string)
|
|
|
16
16
|
* @param files - The files object
|
|
17
17
|
* @returns The resolved files
|
|
18
18
|
*/
|
|
19
|
-
export declare function resolveFiles(files: FilesOptions, locale: string, cwd: string): {
|
|
19
|
+
export declare function resolveFiles(files: FilesOptions, locale: string, locales: string[], cwd: string): {
|
|
20
20
|
resolvedPaths: ResolvedFiles;
|
|
21
21
|
placeholderPaths: ResolvedFiles;
|
|
22
22
|
transformPaths: TransformFiles;
|
|
23
23
|
};
|
|
24
|
+
export declare function expandGlobPatterns(cwd: string, includePatterns: string[], excludePatterns: string[], locale: string, locales: string[], transformPatterns?: TransformOption | string): {
|
|
25
|
+
resolvedPaths: string[];
|
|
26
|
+
placeholderPaths: string[];
|
|
27
|
+
};
|
|
@@ -28,7 +28,7 @@ export function resolveLocaleFiles(files, locale) {
|
|
|
28
28
|
* @param files - The files object
|
|
29
29
|
* @returns The resolved files
|
|
30
30
|
*/
|
|
31
|
-
export function resolveFiles(files, locale, cwd) {
|
|
31
|
+
export function resolveFiles(files, locale, locales, cwd) {
|
|
32
32
|
// Initialize result object with empty arrays for each file type
|
|
33
33
|
const result = {};
|
|
34
34
|
const placeholderResult = {};
|
|
@@ -47,7 +47,7 @@ export function resolveFiles(files, locale, cwd) {
|
|
|
47
47
|
}
|
|
48
48
|
// ==== PLACEHOLDERS ==== //
|
|
49
49
|
if (files[fileType]?.include) {
|
|
50
|
-
const filePaths = expandGlobPatterns(cwd, files[fileType].include, files[fileType]?.exclude || [], locale, transformPaths[fileType] || undefined);
|
|
50
|
+
const filePaths = expandGlobPatterns(cwd, files[fileType].include, files[fileType]?.exclude || [], locale, locales, transformPaths[fileType] || undefined);
|
|
51
51
|
result[fileType] = filePaths.resolvedPaths;
|
|
52
52
|
placeholderResult[fileType] = filePaths.placeholderPaths;
|
|
53
53
|
}
|
|
@@ -59,7 +59,7 @@ export function resolveFiles(files, locale, cwd) {
|
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
// Helper function to expand glob patterns
|
|
62
|
-
function expandGlobPatterns(cwd, includePatterns, excludePatterns, locale, transformPatterns) {
|
|
62
|
+
export function expandGlobPatterns(cwd, includePatterns, excludePatterns, locale, locales, transformPatterns) {
|
|
63
63
|
// Expand glob patterns to include all matching files
|
|
64
64
|
const resolvedPaths = [];
|
|
65
65
|
const placeholderPaths = [];
|
|
@@ -85,7 +85,9 @@ function expandGlobPatterns(cwd, includePatterns, excludePatterns, locale, trans
|
|
|
85
85
|
// Resolve the absolute pattern path
|
|
86
86
|
const absolutePattern = path.resolve(cwd, expandedPattern);
|
|
87
87
|
// Prepare exclude patterns with locale replaced
|
|
88
|
-
const expandedExcludePatterns = excludePatterns.
|
|
88
|
+
const expandedExcludePatterns = Array.from(new Set(excludePatterns.flatMap((p) => locales.map((targetLocale) => path.resolve(cwd, p
|
|
89
|
+
.replace(/\[locale\]/g, locale)
|
|
90
|
+
.replace(/\[locales\]/g, targetLocale))))));
|
|
89
91
|
// Use fast-glob to find all matching files, excluding the patterns
|
|
90
92
|
const matches = fg.sync(absolutePattern, {
|
|
91
93
|
absolute: true,
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
export declare function handleInitGT(filepath: string, errors: string[], warnings: string[], filesUpdated: string[]
|
|
1
|
+
export declare function handleInitGT(filepath: string, errors: string[], warnings: string[], filesUpdated: string[], packageJson?: {
|
|
2
|
+
type?: string;
|
|
3
|
+
}, tsconfigJson?: {
|
|
4
|
+
compilerOptions?: {
|
|
5
|
+
module?: string;
|
|
6
|
+
};
|
|
7
|
+
}): Promise<void>;
|
|
@@ -7,7 +7,7 @@ const traverse = traverseModule.default || traverseModule;
|
|
|
7
7
|
const generate = generateModule.default || generateModule;
|
|
8
8
|
import * as t from '@babel/types';
|
|
9
9
|
import { logError } from '../../console/logging.js';
|
|
10
|
-
export async function handleInitGT(filepath, errors, warnings, filesUpdated) {
|
|
10
|
+
export async function handleInitGT(filepath, errors, warnings, filesUpdated, packageJson, tsconfigJson) {
|
|
11
11
|
const code = await fs.promises.readFile(filepath, 'utf8');
|
|
12
12
|
let ast;
|
|
13
13
|
try {
|
|
@@ -17,7 +17,66 @@ export async function handleInitGT(filepath, errors, warnings, filesUpdated) {
|
|
|
17
17
|
tokens: true,
|
|
18
18
|
createParenthesizedExpressions: true,
|
|
19
19
|
});
|
|
20
|
-
|
|
20
|
+
// Analyze the actual file content to determine module system
|
|
21
|
+
let hasES6Imports = false;
|
|
22
|
+
let hasCommonJSRequire = false;
|
|
23
|
+
traverse(ast, {
|
|
24
|
+
ImportDeclaration() {
|
|
25
|
+
hasES6Imports = true;
|
|
26
|
+
},
|
|
27
|
+
CallExpression(path) {
|
|
28
|
+
if (t.isIdentifier(path.node.callee, { name: 'require' })) {
|
|
29
|
+
hasCommonJSRequire = true;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
// Determine if we need CommonJS based on actual file content and fallback to config-based logic
|
|
34
|
+
let needsCJS = false;
|
|
35
|
+
if (hasES6Imports && !hasCommonJSRequire) {
|
|
36
|
+
// File uses ES6 imports, so we should use ES6 imports
|
|
37
|
+
needsCJS = false;
|
|
38
|
+
}
|
|
39
|
+
else if (hasCommonJSRequire && !hasES6Imports) {
|
|
40
|
+
// File uses CommonJS require, so we should use CommonJS require
|
|
41
|
+
needsCJS = true;
|
|
42
|
+
}
|
|
43
|
+
else if (hasES6Imports && hasCommonJSRequire) {
|
|
44
|
+
// Mixed usage - this is unusual but we'll default to ES6 imports
|
|
45
|
+
warnings.push(`Mixed ES6 imports and CommonJS require detected in ${filepath}. Defaulting to ES6 imports.`);
|
|
46
|
+
needsCJS = false;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// No imports/requires found, fall back to configuration-based logic
|
|
50
|
+
if (filepath.endsWith('.ts') || filepath.endsWith('.tsx')) {
|
|
51
|
+
// For TypeScript files, check tsconfig.json compilerOptions.module
|
|
52
|
+
const moduleSetting = tsconfigJson?.compilerOptions?.module;
|
|
53
|
+
if (moduleSetting === 'commonjs' || moduleSetting === 'node') {
|
|
54
|
+
needsCJS = true;
|
|
55
|
+
}
|
|
56
|
+
else if (moduleSetting === 'esnext' ||
|
|
57
|
+
moduleSetting === 'es2022' ||
|
|
58
|
+
moduleSetting === 'es2020' ||
|
|
59
|
+
moduleSetting === 'es2015' ||
|
|
60
|
+
moduleSetting === 'es6' ||
|
|
61
|
+
moduleSetting === 'node16' ||
|
|
62
|
+
moduleSetting === 'nodenext') {
|
|
63
|
+
needsCJS = false;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Default to ESM for TypeScript files if no module setting is specified
|
|
67
|
+
needsCJS = false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (filepath.endsWith('.js')) {
|
|
71
|
+
// For JavaScript files, check package.json type
|
|
72
|
+
// If package.json has "type": "module", .js files are treated as ES modules
|
|
73
|
+
needsCJS = packageJson?.type !== 'module';
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// For other file extensions, default to ESM
|
|
77
|
+
needsCJS = false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
21
80
|
// Check if withGTConfig or initGT is already imported/required
|
|
22
81
|
let hasGTConfig = false;
|
|
23
82
|
let hasInitGT = false;
|
|
@@ -36,16 +95,48 @@ export async function handleInitGT(filepath, errors, warnings, filesUpdated) {
|
|
|
36
95
|
},
|
|
37
96
|
VariableDeclaration(path) {
|
|
38
97
|
path.node.declarations.forEach((dec) => {
|
|
39
|
-
if (t.isVariableDeclarator(dec)
|
|
40
|
-
|
|
41
|
-
t.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
98
|
+
if (t.isVariableDeclarator(dec)) {
|
|
99
|
+
// Handle destructuring: const { withGTConfig } = require('gt-next/config')
|
|
100
|
+
if (t.isCallExpression(dec.init) &&
|
|
101
|
+
t.isIdentifier(dec.init.callee, { name: 'require' }) &&
|
|
102
|
+
t.isStringLiteral(dec.init.arguments[0], {
|
|
103
|
+
value: 'gt-next/config',
|
|
104
|
+
})) {
|
|
105
|
+
// Handle simple assignment: const withGTConfig = require(...)
|
|
106
|
+
if (t.isIdentifier(dec.id, { name: 'withGTConfig' }))
|
|
107
|
+
hasGTConfig = true;
|
|
108
|
+
if (t.isIdentifier(dec.id, { name: 'initGT' }))
|
|
109
|
+
hasInitGT = true;
|
|
110
|
+
// Handle destructuring: const { withGTConfig } = require(...)
|
|
111
|
+
if (t.isObjectPattern(dec.id)) {
|
|
112
|
+
dec.id.properties.forEach((prop) => {
|
|
113
|
+
if (t.isObjectProperty(prop) &&
|
|
114
|
+
t.isIdentifier(prop.key) &&
|
|
115
|
+
t.isIdentifier(prop.value)) {
|
|
116
|
+
if (prop.key.name === 'withGTConfig')
|
|
117
|
+
hasGTConfig = true;
|
|
118
|
+
if (prop.key.name === 'initGT')
|
|
119
|
+
hasInitGT = true;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Handle member access: const withGTConfig = require('gt-next/config').withGTConfig
|
|
125
|
+
else if (t.isMemberExpression(dec.init) &&
|
|
126
|
+
t.isCallExpression(dec.init.object) &&
|
|
127
|
+
t.isIdentifier(dec.init.object.callee, { name: 'require' }) &&
|
|
128
|
+
t.isStringLiteral(dec.init.object.arguments[0], {
|
|
129
|
+
value: 'gt-next/config',
|
|
130
|
+
})) {
|
|
131
|
+
if (t.isIdentifier(dec.id, { name: 'withGTConfig' }) &&
|
|
132
|
+
t.isIdentifier(dec.init.property, { name: 'withGTConfig' })) {
|
|
133
|
+
hasGTConfig = true;
|
|
134
|
+
}
|
|
135
|
+
if (t.isIdentifier(dec.id, { name: 'initGT' }) &&
|
|
136
|
+
t.isIdentifier(dec.init.property, { name: 'initGT' })) {
|
|
137
|
+
hasInitGT = true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
49
140
|
}
|
|
50
141
|
});
|
|
51
142
|
},
|
package/dist/setup/wizard.js
CHANGED
|
@@ -12,6 +12,7 @@ import { wrapContentNext } from '../next/parse/wrapContent.js';
|
|
|
12
12
|
import { getPackageManager } from '../utils/packageManager.js';
|
|
13
13
|
import { installPackage } from '../utils/installPackage.js';
|
|
14
14
|
import { createOrUpdateConfig } from '../fs/config/setupConfig.js';
|
|
15
|
+
import { loadConfig } from '../fs/config/loadConfig.js';
|
|
15
16
|
export async function handleSetupReactCommand(options) {
|
|
16
17
|
// Ask user for confirmation using inquirer
|
|
17
18
|
const answer = await promptConfirm({
|
|
@@ -111,8 +112,11 @@ Please let us know what you would like to see supported at https://github.com/ge
|
|
|
111
112
|
filesUpdated = [...filesUpdated, ...filesUpdatedNext];
|
|
112
113
|
spinner.stop(chalk.green(`Success! Added <T> tags and updated ${chalk.bold.cyan(filesUpdated.length)} files:\n`) + filesUpdated.map((file) => `${chalk.green('-')} ${file}`).join('\n'));
|
|
113
114
|
if (addWithGTConfig) {
|
|
115
|
+
// Read tsconfig.json if it exists
|
|
116
|
+
const tsconfigPath = findFilepath(['tsconfig.json']);
|
|
117
|
+
const tsconfigJson = tsconfigPath ? loadConfig(tsconfigPath) : undefined;
|
|
114
118
|
// Add the withGTConfig() function to the next.config.js file
|
|
115
|
-
await handleInitGT(nextConfigPath, errors, warnings, filesUpdated);
|
|
119
|
+
await handleInitGT(nextConfigPath, errors, warnings, filesUpdated, packageJson, tsconfigJson);
|
|
116
120
|
logStep(chalk.green(`Added withGTConfig() to your ${nextConfigPath} file.`));
|
|
117
121
|
}
|
|
118
122
|
if (errors.length > 0) {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -71,7 +71,7 @@ export type FilesOptions = {
|
|
|
71
71
|
[K in SupportedFileExtension]?: {
|
|
72
72
|
include: string[];
|
|
73
73
|
exclude?: string[];
|
|
74
|
-
transform?: string;
|
|
74
|
+
transform?: string | TransformOption;
|
|
75
75
|
};
|
|
76
76
|
} & {
|
|
77
77
|
gt?: {
|
|
@@ -103,6 +103,9 @@ export type AdditionalOptions = {
|
|
|
103
103
|
jsonSchema?: {
|
|
104
104
|
[fileGlob: string]: JsonSchema;
|
|
105
105
|
};
|
|
106
|
+
yamlSchema?: {
|
|
107
|
+
[fileGlob: string]: YamlSchema;
|
|
108
|
+
};
|
|
106
109
|
docsUrlPattern?: string;
|
|
107
110
|
docsImportPattern?: string;
|
|
108
111
|
docsHideDefaultLocaleImport?: boolean;
|
|
@@ -115,6 +118,10 @@ export type JsonSchema = {
|
|
|
115
118
|
[sourceObjectPath: string]: SourceObjectOptions;
|
|
116
119
|
};
|
|
117
120
|
};
|
|
121
|
+
export type YamlSchema = {
|
|
122
|
+
preset?: 'mintlify';
|
|
123
|
+
include?: string[];
|
|
124
|
+
};
|
|
118
125
|
export type SourceObjectOptions = {
|
|
119
126
|
type: 'array' | 'object';
|
|
120
127
|
include: string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gtx-cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.19",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"bin": "dist/main.js",
|
|
6
6
|
"files": [
|
|
@@ -88,13 +88,15 @@
|
|
|
88
88
|
"fast-glob": "^3.3.3",
|
|
89
89
|
"form-data": "^4.0.2",
|
|
90
90
|
"generaltranslation": "^7.1.4",
|
|
91
|
+
"json-pointer": "^0.6.2",
|
|
91
92
|
"jsonpath-plus": "^10.3.0",
|
|
92
93
|
"jsonpointer": "^5.0.1",
|
|
93
94
|
"micromatch": "^4.0.8",
|
|
94
95
|
"open": "^10.1.1",
|
|
95
96
|
"ora": "^8.2.0",
|
|
96
97
|
"resolve": "^1.22.10",
|
|
97
|
-
"tsconfig-paths": "^4.2.0"
|
|
98
|
+
"tsconfig-paths": "^4.2.0",
|
|
99
|
+
"yaml": "^2.8.0"
|
|
98
100
|
},
|
|
99
101
|
"devDependencies": {
|
|
100
102
|
"@biomejs/biome": "^1.9.4",
|