gt 2.14.57 → 2.14.58
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 +11 -0
- package/dist/api/downloadFileBatch.js +29 -0
- package/dist/api/downloadFileBatch.js.map +1 -1
- package/dist/cli/commands/enqueue.js +2 -0
- package/dist/cli/commands/enqueue.js.map +1 -1
- package/dist/cli/commands/stage.js +2 -0
- package/dist/cli/commands/stage.js.map +1 -1
- package/dist/config/generateSettings.js +4 -1
- package/dist/config/generateSettings.js.map +1 -1
- package/dist/console/displayTranslateSummary.js +2 -0
- package/dist/console/displayTranslateSummary.js.map +1 -1
- package/dist/console/index.d.ts +1 -0
- package/dist/console/index.js +2 -1
- package/dist/console/index.js.map +1 -1
- package/dist/extraction/postProcess.js +1 -0
- package/dist/extraction/postProcess.js.map +1 -1
- package/dist/formats/files/aggregateFiles.js +6 -5
- package/dist/formats/files/aggregateFiles.js.map +1 -1
- package/dist/formats/files/collectFiles.js +7 -1
- package/dist/formats/files/collectFiles.js.map +1 -1
- package/dist/formats/files/convertToFileTranslationData.js +12 -1
- package/dist/formats/files/convertToFileTranslationData.js.map +1 -1
- package/dist/fs/config/parseFilesConfig.d.ts +1 -1
- package/dist/fs/config/parseFilesConfig.js +41 -1
- package/dist/fs/config/parseFilesConfig.js.map +1 -1
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/generated/version.js.map +1 -1
- package/dist/react/jsx/utils/constants.d.ts +2 -2
- package/dist/react/jsx/utils/constants.js +3 -1
- package/dist/react/jsx/utils/constants.js.map +1 -1
- package/dist/react/jsx/utils/jsxParsing/parseTProps.js +14 -1
- package/dist/react/jsx/utils/jsxParsing/parseTProps.js.map +1 -1
- package/dist/react/jsx/utils/mapAttributeName.d.ts +1 -1
- package/dist/react/jsx/utils/mapAttributeName.js +1 -0
- package/dist/react/jsx/utils/mapAttributeName.js.map +1 -1
- package/dist/react/jsx/utils/stringParsing/processTranslationCall/extractStringEntryMetadata.d.ts +1 -0
- package/dist/react/jsx/utils/stringParsing/processTranslationCall/extractStringEntryMetadata.js +4 -2
- package/dist/react/jsx/utils/stringParsing/processTranslationCall/extractStringEntryMetadata.js.map +1 -1
- package/dist/state/translateWarnings.d.ts +1 -1
- package/dist/state/translateWarnings.js.map +1 -1
- package/dist/translation/reviewSetupWarning.d.ts +16 -0
- package/dist/translation/reviewSetupWarning.js +52 -0
- package/dist/translation/reviewSetupWarning.js.map +1 -0
- package/dist/types/files.d.ts +2 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/utils/hash.d.ts +8 -0
- package/dist/utils/hash.js +11 -1
- package/dist/utils/hash.js.map +1 -1
- package/dist/workflows/download.d.ts +2 -0
- package/dist/workflows/download.js +2 -1
- package/dist/workflows/download.js.map +1 -1
- package/dist/workflows/steps/DownloadStep.js +20 -0
- package/dist/workflows/steps/DownloadStep.js.map +1 -1
- package/dist/workflows/steps/EnqueueStep.js +0 -1
- package/dist/workflows/steps/EnqueueStep.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# gtx-cli
|
|
2
2
|
|
|
3
|
+
## 2.14.58
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1786](https://github.com/generaltranslation/gt/pull/1786) [`6945a98`](https://github.com/generaltranslation/gt/commit/6945a9871ea260dd999dcb2246c48b21134721f6) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Add `requiresReview`
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`6945a98`](https://github.com/generaltranslation/gt/commit/6945a9871ea260dd999dcb2246c48b21134721f6)]:
|
|
10
|
+
- generaltranslation@8.2.19
|
|
11
|
+
- @generaltranslation/python-extractor@0.2.25
|
|
12
|
+
- @generaltranslation/supported-locales@2.1.5
|
|
13
|
+
|
|
3
14
|
## 2.14.57
|
|
4
15
|
|
|
5
16
|
## 2.14.56
|
|
@@ -17,6 +17,33 @@ import * as fs$1 from "fs";
|
|
|
17
17
|
import * as path$1 from "path";
|
|
18
18
|
import stringify from "fast-json-stable-stringify";
|
|
19
19
|
//#region src/api/downloadFileBatch.ts
|
|
20
|
+
/**
|
|
21
|
+
* The platform withholds unapproved review-gated GTJSON components from
|
|
22
|
+
* served output. Reports the delta between the source component count and
|
|
23
|
+
* the served content so partially-served files don't read as fully
|
|
24
|
+
* translated — for both fresh downloads and already-downloaded skips.
|
|
25
|
+
*/
|
|
26
|
+
function reportWithheldGtJsonComponents(fileFormat, servedContent, componentCount, locale) {
|
|
27
|
+
if (fileFormat !== "GTJSON" || componentCount == null) return;
|
|
28
|
+
const received = countGtJsonEntries(servedContent);
|
|
29
|
+
if (received == null) return;
|
|
30
|
+
const withheld = componentCount - received;
|
|
31
|
+
if (withheld > 0) recordWarning("pending_review", "<React Elements>", `${withheld} component translation(s) for locale ${locale} require review and are not approved yet`);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Counts entries in downloaded GTJSON output, tolerating both the flat
|
|
35
|
+
* public shape and a wrapped { type, data } shape.
|
|
36
|
+
*/
|
|
37
|
+
function countGtJsonEntries(content) {
|
|
38
|
+
try {
|
|
39
|
+
const parsed = JSON.parse(content);
|
|
40
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return;
|
|
41
|
+
const entries = parsed.type === "GTJSON" && parsed.data && typeof parsed.data === "object" && !Array.isArray(parsed.data) ? parsed.data : parsed;
|
|
42
|
+
return Object.keys(entries).length;
|
|
43
|
+
} catch {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
20
47
|
function sortJsonString(data) {
|
|
21
48
|
const sortedData = stringify(JSON.parse(data));
|
|
22
49
|
return JSON.stringify(JSON.parse(sortedData), null, 2);
|
|
@@ -123,6 +150,7 @@ async function downloadFileBatch(fileTracker, files, options, forceDownload = fa
|
|
|
123
150
|
} catch (error) {
|
|
124
151
|
logger.warn(`Failed to re-merge existing translation for ${outputPath} (${locale}): ${error}`);
|
|
125
152
|
}
|
|
153
|
+
reportWithheldGtJsonComponents(file.fileFormat, file.data, fileProperties.componentCount, locale);
|
|
126
154
|
result.skipped.push(requestedFile);
|
|
127
155
|
continue;
|
|
128
156
|
}
|
|
@@ -140,6 +168,7 @@ async function downloadFileBatch(fileTracker, files, options, forceDownload = fa
|
|
|
140
168
|
locale,
|
|
141
169
|
inputPath
|
|
142
170
|
});
|
|
171
|
+
reportWithheldGtJsonComponents(file.fileFormat, data, fileProperties.componentCount, locale);
|
|
143
172
|
result.successful.push(requestedFile);
|
|
144
173
|
if (fileId && versionId && locale) {
|
|
145
174
|
const entry = findOrCreateEntry(entryMap, downloadedVersions.entries, fileId, versionId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downloadFileBatch.js","names":["fs","path"],"sources":["../../src/api/downloadFileBatch.ts"],"sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { logger } from '../console/logger.js';\nimport { gt } from '../utils/gt.js';\nimport { Settings } from '../types/index.js';\nimport { validateJsonSchema } from '../formats/json/utils.js';\nimport { validateYamlSchema } from '../formats/yaml/utils.js';\nimport { mergeJson } from '../formats/json/mergeJson.js';\nimport { extractJson } from '../formats/json/extractJson.js';\nimport mergeYaml from '../formats/yaml/mergeYaml.js';\nimport { extractYaml } from '../formats/yaml/extractYaml.js';\nimport {\n resolveMintlifyRefs,\n shouldResolveRefs,\n} from '../utils/resolveMintlifyRefs.js';\nimport {\n readLockfile,\n writeLockfile,\n findOrCreateEntry,\n} from '../fs/config/downloadedVersions.js';\nimport { recordDownloaded, recordRemerged } from '../state/recentDownloads.js';\nimport { recordWarning } from '../state/translateWarnings.js';\nimport stringify from 'fast-json-stable-stringify';\nimport type { FileStatusTracker } from '../workflows/steps/PollJobsStep.js';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../formats/files/supportedFiles.js';\nimport { hasNonIdentityFileFormatTransformForType } from '../formats/files/transformFormat.js';\nimport { getRelative } from '../fs/findFilepath.js';\n\nfunction sortJsonString(data: string): string {\n const sortedData = stringify(JSON.parse(data));\n return JSON.stringify(JSON.parse(sortedData), null, 2);\n}\n\n/**\n * Merges translated content with the current source file for schema-based formats.\n */\nfunction mergeWithSource(\n translatedContent: string,\n locale: string,\n inputPath: string,\n options: Settings\n): string {\n if (shouldSkipSourceFormatMerge(inputPath, options)) {\n return translatedContent;\n }\n if (!options.options) return translatedContent;\n\n const jsonSchema = options.options.jsonSchema\n ? validateJsonSchema(options.options, inputPath)\n : null;\n const yamlSchema =\n !jsonSchema && options.options.yamlSchema\n ? validateYamlSchema(options.options, inputPath)\n : null;\n\n if (!jsonSchema && !yamlSchema) return translatedContent;\n\n const sourceContent = fs.readFileSync(inputPath, 'utf8');\n if (!sourceContent) return translatedContent;\n\n if (jsonSchema) {\n // Resolve $ref before merging if configured\n let resolvedSourceContent = sourceContent;\n if (shouldResolveRefs(inputPath, options.options)) {\n try {\n const json = JSON.parse(sourceContent);\n const { resolved } = resolveMintlifyRefs(json, inputPath);\n resolvedSourceContent = JSON.stringify(resolved, null, 2);\n } catch {\n // Fall through with original content\n }\n }\n return mergeJson(\n resolvedSourceContent,\n inputPath,\n options.options,\n [{ translatedContent, targetLocale: locale }],\n options.defaultLocale,\n options.locales\n )[0];\n } else {\n return mergeYaml(\n sourceContent,\n inputPath,\n options.options,\n [{ translatedContent, targetLocale: locale }],\n options.defaultLocale\n )[0];\n }\n}\n\n/**\n * Determines whether a source file should be skipped for schema re-merging.\n * @param inputPath - The path of the source file\n * @param options - The settings for the project\n * @returns True if the source file should be skipped for schema re-merging, false otherwise\n */\nfunction shouldSkipSourceFormatMerge(\n inputPath: string,\n options: Settings\n): boolean {\n for (const fileType of SUPPORTED_FILE_EXTENSIONS) {\n if (!hasNonIdentityFileFormatTransformForType(options, fileType)) continue;\n\n const transformedSourcePaths = options.files.resolvedPaths[fileType] || [];\n if (\n transformedSourcePaths.some(\n (sourcePath) => getRelative(sourcePath) === inputPath\n )\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nexport type BatchedFiles = {\n branchId: string;\n fileId: string;\n versionId: string;\n locale: string;\n outputPath: string;\n inputPath: string;\n}[];\n\nexport type DownloadFileBatchResult = {\n successful: BatchedFiles;\n failed: BatchedFiles;\n skipped: BatchedFiles;\n};\n/**\n * Downloads multiple translation files in a single batch request\n * @param files - Array of files to download with their output paths\n * @param maxRetries - Maximum number of retry attempts\n * @param retryDelay - Delay between retries in milliseconds\n * @returns Object containing successful and failed file IDs\n */\nexport async function downloadFileBatch(\n fileTracker: FileStatusTracker,\n files: BatchedFiles,\n options: Settings,\n forceDownload: boolean = false\n): Promise<DownloadFileBatchResult> {\n // Local record of what version was last downloaded for each fileName:locale\n const {\n data: downloadedVersions,\n entryMap,\n originalV1,\n } = readLockfile(options);\n let didUpdateDownloadedLock = false;\n\n // Create a map of requested file keys to the file object\n const requestedFileMap = new Map(\n files.map((file) => [\n `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`,\n file,\n ])\n );\n const result: DownloadFileBatchResult = {\n successful: [],\n failed: [],\n skipped: [],\n };\n\n // Create a map of translationId to outputPath for easier lookup\n const outputPathMap = new Map(\n files.map((file) => [\n `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`,\n file.outputPath,\n ])\n );\n\n try {\n // Download the files\n const responseData = await gt.downloadFileBatch(\n files.map((file) => ({\n fileId: file.fileId,\n branchId: file.branchId,\n versionId: file.versionId,\n locale: file.locale,\n }))\n );\n const downloadedFiles = responseData.files || [];\n\n // Process each file in the response\n for (const file of downloadedFiles) {\n const fileKey = `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`;\n const requestedFile = requestedFileMap.get(fileKey);\n if (!requestedFile) {\n continue;\n }\n try {\n const outputPath = outputPathMap.get(fileKey);\n const fileProperties = fileTracker.completed.get(fileKey);\n\n if (!outputPath || !fileProperties) {\n logger.warn(`No input/output path found for file: ${fileKey}`);\n recordWarning(\n 'failed_download',\n fileKey,\n 'No input/output path found'\n );\n result.failed.push(requestedFile);\n continue;\n }\n\n const {\n fileId,\n versionId,\n locale,\n branchId,\n fileName: inputPath,\n } = fileProperties;\n\n // Ensure the directory exists\n const dir = path.dirname(outputPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n // If a local translation already exists for the same source version, skip overwrite\n const existingEntry = entryMap.get(fileId);\n const downloadedTranslation =\n existingEntry?.versionId === versionId\n ? existingEntry.translations[locale]\n : undefined;\n const fileExists = fs.existsSync(outputPath);\n\n // Composite schema files merge translations into the source file itself,\n // so outputPath always exists and the lock can't tell whether derived\n // split outputs (e.g. {locale}/docs.json) are still on disk. Always\n // merge fresh API data so derived files are regenerated every run;\n // local edits to translated output are preserved via `gt save-local`.\n const isInPlaceComposite = options.options?.jsonSchema\n ? !!validateJsonSchema(options.options, inputPath)?.composite\n : false;\n\n if (\n !forceDownload &&\n fileExists &&\n downloadedTranslation &&\n !isInPlaceComposite\n ) {\n // For schema-based files, re-merge with current source in case\n // non-translatable fields changed (skip the API download, not the merge)\n try {\n let existingContent = fs.readFileSync(outputPath, 'utf8');\n // Resolve $ref before extraction if configured\n if (shouldResolveRefs(outputPath, options.options)) {\n try {\n const json = JSON.parse(existingContent);\n const { resolved } = resolveMintlifyRefs(json, outputPath);\n existingContent = JSON.stringify(resolved, null, 2);\n } catch {\n // Fall through with original content\n }\n }\n const jsonExtracted = options.options?.jsonSchema\n ? extractJson(\n existingContent,\n inputPath,\n options.options,\n locale,\n options.defaultLocale\n )\n : null;\n const extracted =\n jsonExtracted ??\n (options.options?.yamlSchema\n ? extractYaml(existingContent, inputPath, options.options)\n : null);\n if (extracted) {\n const remerged = mergeWithSource(\n extracted,\n locale,\n inputPath,\n options\n );\n let remergedData = remerged;\n if (outputPath.endsWith('.json')) {\n try {\n remergedData = sortJsonString(remergedData);\n } catch {\n // Fall through with unsorted content\n }\n }\n if (remergedData !== existingContent) {\n await fs.promises.writeFile(outputPath, remergedData);\n }\n // Track for postprocessing (e.g. openapi path localization)\n // even when the API download was skipped\n recordRemerged(outputPath);\n }\n } catch (error) {\n // If re-merge fails, still count as skipped — not worth failing\n // the download, but surface it so missing output is diagnosable\n logger.warn(\n `Failed to re-merge existing translation for ${outputPath} (${locale}): ${error}`\n );\n }\n result.skipped.push(requestedFile);\n continue;\n }\n let data = mergeWithSource(file.data, locale, inputPath, options);\n\n // Stable sort JSON keys for deterministic output\n if (file.fileFormat === 'GTJSON' || outputPath.endsWith('.json')) {\n try {\n data = sortJsonString(data);\n } catch (error) {\n logger.warn(`Failed to sort JSON file: ${file.id}: ` + error);\n }\n }\n\n // Write the file to disk\n await fs.promises.writeFile(outputPath, data);\n // Track as downloaded with metadata for downstream postprocessing\n recordDownloaded(outputPath, {\n branchId,\n fileId,\n versionId,\n locale,\n inputPath,\n });\n\n result.successful.push(requestedFile);\n if (fileId && versionId && locale) {\n const entry = findOrCreateEntry(\n entryMap,\n downloadedVersions.entries,\n fileId,\n versionId\n );\n entry.fileName = inputPath;\n entry.staged = false;\n entry.translations[locale] = {\n updatedAt: new Date().toISOString(),\n fileName: getRelative(outputPath),\n };\n didUpdateDownloadedLock = true;\n }\n } catch (error) {\n logger.error(`Error saving file ${fileKey}: ` + error);\n recordWarning(\n 'failed_download',\n fileKey,\n `Error saving file: ${error}`\n );\n result.failed.push(requestedFile);\n }\n }\n\n // Add any files that weren't in the response to the failed list\n const downloadedFileKeys = new Set(\n downloadedFiles.map(\n (file) =>\n `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`\n )\n );\n for (const [fileKey, requestedFile] of requestedFileMap.entries()) {\n if (!downloadedFileKeys.has(fileKey)) {\n result.failed.push(requestedFile);\n }\n }\n\n // Persist any updates to the downloaded map at the end of a successful cycle\n if (didUpdateDownloadedLock) {\n writeLockfile(downloadedVersions, originalV1);\n didUpdateDownloadedLock = false;\n }\n return result;\n } catch (error) {\n logger.error(\n `An unexpected error occurred while downloading files: ` + error\n );\n }\n\n // Mark all files as failed if we get here\n result.failed = [...requestedFileMap.values()];\n if (didUpdateDownloadedLock) {\n writeLockfile(downloadedVersions, originalV1);\n }\n return result;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"downloadFileBatch.js","names":["fs","path"],"sources":["../../src/api/downloadFileBatch.ts"],"sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { logger } from '../console/logger.js';\nimport { gt } from '../utils/gt.js';\nimport { Settings } from '../types/index.js';\nimport { validateJsonSchema } from '../formats/json/utils.js';\nimport { validateYamlSchema } from '../formats/yaml/utils.js';\nimport { mergeJson } from '../formats/json/mergeJson.js';\nimport { extractJson } from '../formats/json/extractJson.js';\nimport mergeYaml from '../formats/yaml/mergeYaml.js';\nimport { extractYaml } from '../formats/yaml/extractYaml.js';\nimport {\n resolveMintlifyRefs,\n shouldResolveRefs,\n} from '../utils/resolveMintlifyRefs.js';\nimport {\n readLockfile,\n writeLockfile,\n findOrCreateEntry,\n} from '../fs/config/downloadedVersions.js';\nimport { recordDownloaded, recordRemerged } from '../state/recentDownloads.js';\nimport { recordWarning } from '../state/translateWarnings.js';\nimport stringify from 'fast-json-stable-stringify';\nimport type { FileStatusTracker } from '../workflows/steps/PollJobsStep.js';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../formats/files/supportedFiles.js';\nimport { hasNonIdentityFileFormatTransformForType } from '../formats/files/transformFormat.js';\nimport { getRelative } from '../fs/findFilepath.js';\n\n/**\n * The platform withholds unapproved review-gated GTJSON components from\n * served output. Reports the delta between the source component count and\n * the served content so partially-served files don't read as fully\n * translated — for both fresh downloads and already-downloaded skips.\n */\nfunction reportWithheldGtJsonComponents(\n fileFormat: string | undefined,\n servedContent: string,\n componentCount: number | undefined,\n locale: string\n): void {\n if (fileFormat !== 'GTJSON' || componentCount == null) return;\n const received = countGtJsonEntries(servedContent);\n if (received == null) return;\n const withheld = componentCount - received;\n if (withheld > 0) {\n recordWarning(\n 'pending_review',\n '<React Elements>',\n `${withheld} component translation(s) for locale ${locale} require review and are not approved yet`\n );\n }\n}\n\n/**\n * Counts entries in downloaded GTJSON output, tolerating both the flat\n * public shape and a wrapped { type, data } shape.\n */\nfunction countGtJsonEntries(content: string): number | undefined {\n try {\n const parsed = JSON.parse(content);\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return undefined;\n }\n const entries =\n parsed.type === 'GTJSON' &&\n parsed.data &&\n typeof parsed.data === 'object' &&\n !Array.isArray(parsed.data)\n ? parsed.data\n : parsed;\n return Object.keys(entries).length;\n } catch {\n return undefined;\n }\n}\n\nfunction sortJsonString(data: string): string {\n const sortedData = stringify(JSON.parse(data));\n return JSON.stringify(JSON.parse(sortedData), null, 2);\n}\n\n/**\n * Merges translated content with the current source file for schema-based formats.\n */\nfunction mergeWithSource(\n translatedContent: string,\n locale: string,\n inputPath: string,\n options: Settings\n): string {\n if (shouldSkipSourceFormatMerge(inputPath, options)) {\n return translatedContent;\n }\n if (!options.options) return translatedContent;\n\n const jsonSchema = options.options.jsonSchema\n ? validateJsonSchema(options.options, inputPath)\n : null;\n const yamlSchema =\n !jsonSchema && options.options.yamlSchema\n ? validateYamlSchema(options.options, inputPath)\n : null;\n\n if (!jsonSchema && !yamlSchema) return translatedContent;\n\n const sourceContent = fs.readFileSync(inputPath, 'utf8');\n if (!sourceContent) return translatedContent;\n\n if (jsonSchema) {\n // Resolve $ref before merging if configured\n let resolvedSourceContent = sourceContent;\n if (shouldResolveRefs(inputPath, options.options)) {\n try {\n const json = JSON.parse(sourceContent);\n const { resolved } = resolveMintlifyRefs(json, inputPath);\n resolvedSourceContent = JSON.stringify(resolved, null, 2);\n } catch {\n // Fall through with original content\n }\n }\n return mergeJson(\n resolvedSourceContent,\n inputPath,\n options.options,\n [{ translatedContent, targetLocale: locale }],\n options.defaultLocale,\n options.locales\n )[0];\n } else {\n return mergeYaml(\n sourceContent,\n inputPath,\n options.options,\n [{ translatedContent, targetLocale: locale }],\n options.defaultLocale\n )[0];\n }\n}\n\n/**\n * Determines whether a source file should be skipped for schema re-merging.\n * @param inputPath - The path of the source file\n * @param options - The settings for the project\n * @returns True if the source file should be skipped for schema re-merging, false otherwise\n */\nfunction shouldSkipSourceFormatMerge(\n inputPath: string,\n options: Settings\n): boolean {\n for (const fileType of SUPPORTED_FILE_EXTENSIONS) {\n if (!hasNonIdentityFileFormatTransformForType(options, fileType)) continue;\n\n const transformedSourcePaths = options.files.resolvedPaths[fileType] || [];\n if (\n transformedSourcePaths.some(\n (sourcePath) => getRelative(sourcePath) === inputPath\n )\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nexport type BatchedFiles = {\n branchId: string;\n fileId: string;\n versionId: string;\n locale: string;\n outputPath: string;\n inputPath: string;\n}[];\n\nexport type DownloadFileBatchResult = {\n successful: BatchedFiles;\n failed: BatchedFiles;\n skipped: BatchedFiles;\n};\n/**\n * Downloads multiple translation files in a single batch request\n * @param files - Array of files to download with their output paths\n * @param maxRetries - Maximum number of retry attempts\n * @param retryDelay - Delay between retries in milliseconds\n * @returns Object containing successful and failed file IDs\n */\nexport async function downloadFileBatch(\n fileTracker: FileStatusTracker,\n files: BatchedFiles,\n options: Settings,\n forceDownload: boolean = false\n): Promise<DownloadFileBatchResult> {\n // Local record of what version was last downloaded for each fileName:locale\n const {\n data: downloadedVersions,\n entryMap,\n originalV1,\n } = readLockfile(options);\n let didUpdateDownloadedLock = false;\n\n // Create a map of requested file keys to the file object\n const requestedFileMap = new Map(\n files.map((file) => [\n `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`,\n file,\n ])\n );\n const result: DownloadFileBatchResult = {\n successful: [],\n failed: [],\n skipped: [],\n };\n\n // Create a map of translationId to outputPath for easier lookup\n const outputPathMap = new Map(\n files.map((file) => [\n `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`,\n file.outputPath,\n ])\n );\n\n try {\n // Download the files\n const responseData = await gt.downloadFileBatch(\n files.map((file) => ({\n fileId: file.fileId,\n branchId: file.branchId,\n versionId: file.versionId,\n locale: file.locale,\n }))\n );\n const downloadedFiles = responseData.files || [];\n\n // Process each file in the response\n for (const file of downloadedFiles) {\n const fileKey = `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`;\n const requestedFile = requestedFileMap.get(fileKey);\n if (!requestedFile) {\n continue;\n }\n try {\n const outputPath = outputPathMap.get(fileKey);\n const fileProperties = fileTracker.completed.get(fileKey);\n\n if (!outputPath || !fileProperties) {\n logger.warn(`No input/output path found for file: ${fileKey}`);\n recordWarning(\n 'failed_download',\n fileKey,\n 'No input/output path found'\n );\n result.failed.push(requestedFile);\n continue;\n }\n\n const {\n fileId,\n versionId,\n locale,\n branchId,\n fileName: inputPath,\n } = fileProperties;\n\n // Ensure the directory exists\n const dir = path.dirname(outputPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n // If a local translation already exists for the same source version, skip overwrite\n const existingEntry = entryMap.get(fileId);\n const downloadedTranslation =\n existingEntry?.versionId === versionId\n ? existingEntry.translations[locale]\n : undefined;\n const fileExists = fs.existsSync(outputPath);\n\n // Composite schema files merge translations into the source file itself,\n // so outputPath always exists and the lock can't tell whether derived\n // split outputs (e.g. {locale}/docs.json) are still on disk. Always\n // merge fresh API data so derived files are regenerated every run;\n // local edits to translated output are preserved via `gt save-local`.\n const isInPlaceComposite = options.options?.jsonSchema\n ? !!validateJsonSchema(options.options, inputPath)?.composite\n : false;\n\n if (\n !forceDownload &&\n fileExists &&\n downloadedTranslation &&\n !isInPlaceComposite\n ) {\n // For schema-based files, re-merge with current source in case\n // non-translatable fields changed (skip the API download, not the merge)\n try {\n let existingContent = fs.readFileSync(outputPath, 'utf8');\n // Resolve $ref before extraction if configured\n if (shouldResolveRefs(outputPath, options.options)) {\n try {\n const json = JSON.parse(existingContent);\n const { resolved } = resolveMintlifyRefs(json, outputPath);\n existingContent = JSON.stringify(resolved, null, 2);\n } catch {\n // Fall through with original content\n }\n }\n const jsonExtracted = options.options?.jsonSchema\n ? extractJson(\n existingContent,\n inputPath,\n options.options,\n locale,\n options.defaultLocale\n )\n : null;\n const extracted =\n jsonExtracted ??\n (options.options?.yamlSchema\n ? extractYaml(existingContent, inputPath, options.options)\n : null);\n if (extracted) {\n const remerged = mergeWithSource(\n extracted,\n locale,\n inputPath,\n options\n );\n let remergedData = remerged;\n if (outputPath.endsWith('.json')) {\n try {\n remergedData = sortJsonString(remergedData);\n } catch {\n // Fall through with unsorted content\n }\n }\n if (remergedData !== existingContent) {\n await fs.promises.writeFile(outputPath, remergedData);\n }\n // Track for postprocessing (e.g. openapi path localization)\n // even when the API download was skipped\n recordRemerged(outputPath);\n }\n } catch (error) {\n // If re-merge fails, still count as skipped — not worth failing\n // the download, but surface it so missing output is diagnosable\n logger.warn(\n `Failed to re-merge existing translation for ${outputPath} (${locale}): ${error}`\n );\n }\n // Even when the local copy is current, the served content may\n // still be missing review-withheld components — keep reporting\n reportWithheldGtJsonComponents(\n file.fileFormat,\n file.data,\n fileProperties.componentCount,\n locale\n );\n result.skipped.push(requestedFile);\n continue;\n }\n let data = mergeWithSource(file.data, locale, inputPath, options);\n\n // Stable sort JSON keys for deterministic output\n if (file.fileFormat === 'GTJSON' || outputPath.endsWith('.json')) {\n try {\n data = sortJsonString(data);\n } catch (error) {\n logger.warn(`Failed to sort JSON file: ${file.id}: ` + error);\n }\n }\n\n // Write the file to disk\n await fs.promises.writeFile(outputPath, data);\n // Track as downloaded with metadata for downstream postprocessing\n recordDownloaded(outputPath, {\n branchId,\n fileId,\n versionId,\n locale,\n inputPath,\n });\n\n reportWithheldGtJsonComponents(\n file.fileFormat,\n data,\n fileProperties.componentCount,\n locale\n );\n\n result.successful.push(requestedFile);\n if (fileId && versionId && locale) {\n const entry = findOrCreateEntry(\n entryMap,\n downloadedVersions.entries,\n fileId,\n versionId\n );\n entry.fileName = inputPath;\n entry.staged = false;\n entry.translations[locale] = {\n updatedAt: new Date().toISOString(),\n fileName: getRelative(outputPath),\n };\n didUpdateDownloadedLock = true;\n }\n } catch (error) {\n logger.error(`Error saving file ${fileKey}: ` + error);\n recordWarning(\n 'failed_download',\n fileKey,\n `Error saving file: ${error}`\n );\n result.failed.push(requestedFile);\n }\n }\n\n // Add any files that weren't in the response to the failed list\n const downloadedFileKeys = new Set(\n downloadedFiles.map(\n (file) =>\n `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`\n )\n );\n for (const [fileKey, requestedFile] of requestedFileMap.entries()) {\n if (!downloadedFileKeys.has(fileKey)) {\n result.failed.push(requestedFile);\n }\n }\n\n // Persist any updates to the downloaded map at the end of a successful cycle\n if (didUpdateDownloadedLock) {\n writeLockfile(downloadedVersions, originalV1);\n didUpdateDownloadedLock = false;\n }\n return result;\n } catch (error) {\n logger.error(\n `An unexpected error occurred while downloading files: ` + error\n );\n }\n\n // Mark all files as failed if we get here\n result.failed = [...requestedFileMap.values()];\n if (didUpdateDownloadedLock) {\n writeLockfile(downloadedVersions, originalV1);\n }\n return result;\n}\n"],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import { exitSync, logErrorAndExit } from "../../console/logging.js";
|
|
|
2
2
|
import { noFilesError, noVersionIdError } from "../../console/index.js";
|
|
3
3
|
import { hasValidCredentials, hasValidLocales } from "./utils/validation.js";
|
|
4
4
|
import { collectFiles } from "../../formats/files/collectFiles.js";
|
|
5
|
+
import { warnManualReviewSetup } from "../../translation/reviewSetupWarning.js";
|
|
5
6
|
import { runEnqueueWorkflow } from "../../workflows/enqueue.js";
|
|
6
7
|
//#region src/cli/commands/enqueue.ts
|
|
7
8
|
/**
|
|
@@ -16,6 +17,7 @@ async function handleEnqueue(options, settings, library) {
|
|
|
16
17
|
if (!settings._versionId) return logErrorAndExit(noVersionIdError);
|
|
17
18
|
if (!settings.files) return logErrorAndExit(noFilesError);
|
|
18
19
|
const { files } = await collectFiles(options, settings, library);
|
|
20
|
+
await warnManualReviewSetup(settings, files);
|
|
19
21
|
return runEnqueueWorkflow({
|
|
20
22
|
files,
|
|
21
23
|
options,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enqueue.js","names":[],"sources":["../../../src/cli/commands/enqueue.ts"],"sourcesContent":["import { EnqueueFilesResult } from 'generaltranslation/types';\nimport {\n Settings,\n SupportedLibraries,\n TranslateFlags,\n} from '../../types/index.js';\nimport { runEnqueueWorkflow } from '../../workflows/enqueue.js';\nimport { collectFiles } from '../../formats/files/collectFiles.js';\nimport { noFilesError, noVersionIdError } from '../../console/index.js';\nimport { hasValidCredentials, hasValidLocales } from './utils/validation.js';\nimport { exitSync, logErrorAndExit } from '../../console/logging.js';\n\n/**\n * Enqueues translations for a given set of files\n * @param options - The options for the enqueue operation\n * @param settings - The settings for the enqueue operation\n * @returns {Promise<EnqueueFilesResult>} The enqueue result\n */\nexport async function handleEnqueue(\n options: TranslateFlags,\n settings: Settings,\n library: SupportedLibraries\n): Promise<EnqueueFilesResult> {\n if (!hasValidLocales(settings)) return exitSync(1);\n // Validate credentials if not in dry run\n if (!options.dryRun && !hasValidCredentials(settings)) return exitSync(1);\n if (!settings._versionId) {\n return logErrorAndExit(noVersionIdError);\n }\n if (!settings.files) {\n return logErrorAndExit(noFilesError);\n }\n\n // Collect the data for all files we need to enqueue\n const { files } = await collectFiles(options, settings, library);\n\n return runEnqueueWorkflow({ files, options, settings });\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"enqueue.js","names":[],"sources":["../../../src/cli/commands/enqueue.ts"],"sourcesContent":["import { EnqueueFilesResult } from 'generaltranslation/types';\nimport {\n Settings,\n SupportedLibraries,\n TranslateFlags,\n} from '../../types/index.js';\nimport { runEnqueueWorkflow } from '../../workflows/enqueue.js';\nimport { collectFiles } from '../../formats/files/collectFiles.js';\nimport { noFilesError, noVersionIdError } from '../../console/index.js';\nimport { hasValidCredentials, hasValidLocales } from './utils/validation.js';\nimport { exitSync, logErrorAndExit } from '../../console/logging.js';\nimport { warnManualReviewSetup } from '../../translation/reviewSetupWarning.js';\n\n/**\n * Enqueues translations for a given set of files\n * @param options - The options for the enqueue operation\n * @param settings - The settings for the enqueue operation\n * @returns {Promise<EnqueueFilesResult>} The enqueue result\n */\nexport async function handleEnqueue(\n options: TranslateFlags,\n settings: Settings,\n library: SupportedLibraries\n): Promise<EnqueueFilesResult> {\n if (!hasValidLocales(settings)) return exitSync(1);\n // Validate credentials if not in dry run\n if (!options.dryRun && !hasValidCredentials(settings)) return exitSync(1);\n if (!settings._versionId) {\n return logErrorAndExit(noVersionIdError);\n }\n if (!settings.files) {\n return logErrorAndExit(noFilesError);\n }\n\n // Collect the data for all files we need to enqueue\n const { files } = await collectFiles(options, settings, library);\n\n // Point at dashboard review setup when uploading review-gated content\n await warnManualReviewSetup(settings, files);\n\n return runEnqueueWorkflow({ files, options, settings });\n}\n"],"mappings":";;;;;;;;;;;;;AAmBA,eAAsB,cACpB,SACA,UACA,SAC6B;AAC7B,KAAI,CAAC,gBAAgB,SAAS,CAAE,QAAO,SAAS,EAAE;AAElD,KAAI,CAAC,QAAQ,UAAU,CAAC,oBAAoB,SAAS,CAAE,QAAO,SAAS,EAAE;AACzE,KAAI,CAAC,SAAS,WACZ,QAAO,gBAAgB,iBAAiB;AAE1C,KAAI,CAAC,SAAS,MACZ,QAAO,gBAAgB,aAAa;CAItC,MAAM,EAAE,UAAU,MAAM,aAAa,SAAS,UAAU,QAAQ;AAGhE,OAAM,sBAAsB,UAAU,MAAM;AAE5C,QAAO,mBAAmB;EAAE;EAAO;EAAS;EAAU,CAAC"}
|
|
@@ -7,11 +7,13 @@ import { runStageFilesWorkflow } from "../../workflows/stage.js";
|
|
|
7
7
|
import updateConfig from "../../fs/config/updateConfig.js";
|
|
8
8
|
import { collectFiles } from "../../formats/files/collectFiles.js";
|
|
9
9
|
import { convertToFileTranslationData } from "../../formats/files/convertToFileTranslationData.js";
|
|
10
|
+
import { warnManualReviewSetup } from "../../translation/reviewSetupWarning.js";
|
|
10
11
|
//#region src/cli/commands/stage.ts
|
|
11
12
|
async function handleStage(options, settings, library, stage) {
|
|
12
13
|
if (!hasValidLocales(settings)) return exitSync(1);
|
|
13
14
|
if (!options.dryRun && !hasValidCredentials(settings)) return exitSync(1);
|
|
14
15
|
const { files: allFiles, reactComponents, publishMap } = await collectFiles(options, settings, library);
|
|
16
|
+
await warnManualReviewSetup(settings, allFiles);
|
|
15
17
|
if (options.dryRun) {
|
|
16
18
|
logger.success(`Dry run: No files were sent to General Translation.`);
|
|
17
19
|
logCollectedFiles(allFiles, reactComponents);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stage.js","names":[],"sources":["../../../src/cli/commands/stage.ts"],"sourcesContent":["import { logger } from '../../console/logger.js';\nimport { exitSync, logCollectedFiles } from '../../console/logging.js';\nimport {\n Settings,\n SupportedLibraries,\n TranslateFlags,\n} from '../../types/index.js';\nimport { runStageFilesWorkflow } from '../../workflows/stage.js';\nimport { writeStagedEntries } from '../../fs/config/downloadedVersions.js';\nimport type { EnqueueFilesResult } from 'generaltranslation/types';\nimport updateConfig from '../../fs/config/updateConfig.js';\nimport { FileTranslationData } from '../../workflows/download.js';\nimport { BranchData } from '../../types/branch.js';\nimport { TEMPLATE_FILE_ID } from '../../utils/constants.js';\nimport { collectFiles } from '../../formats/files/collectFiles.js';\nimport { convertToFileTranslationData } from '../../formats/files/convertToFileTranslationData.js';\nimport { hasValidCredentials, hasValidLocales } from './utils/validation.js';\n\nexport async function handleStage(\n options: TranslateFlags,\n settings: Settings,\n library: SupportedLibraries,\n stage: boolean\n): Promise<{\n fileVersionData: FileTranslationData | undefined;\n jobData: EnqueueFilesResult | undefined;\n branchData: BranchData | undefined;\n publishMap: Map<string, boolean>;\n} | null> {\n if (!hasValidLocales(settings)) return exitSync(1);\n // Validate credentials if not in dry run\n if (!options.dryRun && !hasValidCredentials(settings)) return exitSync(1);\n\n const {\n files: allFiles,\n reactComponents,\n publishMap,\n } = await collectFiles(options, settings, library);\n\n // Dry run\n if (options.dryRun) {\n logger.success(`Dry run: No files were sent to General Translation.`);\n logCollectedFiles(allFiles, reactComponents);\n return null;\n }\n\n if (allFiles.length === 0 && !settings.publish) {\n logger.error(\n 'No files to translate were found. Check your configuration and try again.'\n );\n }\n\n // Send translations to General Translation\n let fileVersionData: FileTranslationData | undefined;\n let jobData: EnqueueFilesResult | undefined;\n let branchData: BranchData | undefined;\n if (allFiles.length > 0) {\n const { branchData: branchDataResult, enqueueResult } =\n await runStageFilesWorkflow({ files: allFiles, options, settings });\n jobData = enqueueResult;\n branchData = branchDataResult;\n\n fileVersionData = convertToFileTranslationData(allFiles);\n\n // Write staged entries to the lockfile\n if (stage) {\n const stagedFiles = Object.entries(fileVersionData).map(\n ([fileId, data]) => ({\n fileId,\n versionId: data.versionId,\n fileName: data.fileName,\n })\n );\n writeStagedEntries(settings, stagedFiles, branchData.currentBranch.id);\n }\n const templateData = allFiles.find(\n (file) => file.fileId === TEMPLATE_FILE_ID\n );\n if (templateData?.versionId) {\n await updateConfig(settings.config, {\n _versionId: templateData.versionId,\n _branchId: branchData.currentBranch.id,\n });\n }\n }\n\n // Always delete branch id from config if branching is disabled\n // Avoids incorrect CDN queries at runtime\n if (!settings.branchOptions.enabled) {\n await updateConfig(settings.config, {\n _branchId: null,\n });\n }\n\n return {\n fileVersionData,\n jobData,\n branchData,\n publishMap,\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"stage.js","names":[],"sources":["../../../src/cli/commands/stage.ts"],"sourcesContent":["import { logger } from '../../console/logger.js';\nimport { exitSync, logCollectedFiles } from '../../console/logging.js';\nimport {\n Settings,\n SupportedLibraries,\n TranslateFlags,\n} from '../../types/index.js';\nimport { runStageFilesWorkflow } from '../../workflows/stage.js';\nimport { writeStagedEntries } from '../../fs/config/downloadedVersions.js';\nimport type { EnqueueFilesResult } from 'generaltranslation/types';\nimport updateConfig from '../../fs/config/updateConfig.js';\nimport { FileTranslationData } from '../../workflows/download.js';\nimport { BranchData } from '../../types/branch.js';\nimport { TEMPLATE_FILE_ID } from '../../utils/constants.js';\nimport { collectFiles } from '../../formats/files/collectFiles.js';\nimport { convertToFileTranslationData } from '../../formats/files/convertToFileTranslationData.js';\nimport { hasValidCredentials, hasValidLocales } from './utils/validation.js';\nimport { warnManualReviewSetup } from '../../translation/reviewSetupWarning.js';\n\nexport async function handleStage(\n options: TranslateFlags,\n settings: Settings,\n library: SupportedLibraries,\n stage: boolean\n): Promise<{\n fileVersionData: FileTranslationData | undefined;\n jobData: EnqueueFilesResult | undefined;\n branchData: BranchData | undefined;\n publishMap: Map<string, boolean>;\n} | null> {\n if (!hasValidLocales(settings)) return exitSync(1);\n // Validate credentials if not in dry run\n if (!options.dryRun && !hasValidCredentials(settings)) return exitSync(1);\n\n const {\n files: allFiles,\n reactComponents,\n publishMap,\n } = await collectFiles(options, settings, library);\n\n // Point at dashboard review setup when uploading review-gated content\n await warnManualReviewSetup(settings, allFiles);\n\n // Dry run\n if (options.dryRun) {\n logger.success(`Dry run: No files were sent to General Translation.`);\n logCollectedFiles(allFiles, reactComponents);\n return null;\n }\n\n if (allFiles.length === 0 && !settings.publish) {\n logger.error(\n 'No files to translate were found. Check your configuration and try again.'\n );\n }\n\n // Send translations to General Translation\n let fileVersionData: FileTranslationData | undefined;\n let jobData: EnqueueFilesResult | undefined;\n let branchData: BranchData | undefined;\n if (allFiles.length > 0) {\n const { branchData: branchDataResult, enqueueResult } =\n await runStageFilesWorkflow({ files: allFiles, options, settings });\n jobData = enqueueResult;\n branchData = branchDataResult;\n\n fileVersionData = convertToFileTranslationData(allFiles);\n\n // Write staged entries to the lockfile\n if (stage) {\n const stagedFiles = Object.entries(fileVersionData).map(\n ([fileId, data]) => ({\n fileId,\n versionId: data.versionId,\n fileName: data.fileName,\n })\n );\n writeStagedEntries(settings, stagedFiles, branchData.currentBranch.id);\n }\n const templateData = allFiles.find(\n (file) => file.fileId === TEMPLATE_FILE_ID\n );\n if (templateData?.versionId) {\n await updateConfig(settings.config, {\n _versionId: templateData.versionId,\n _branchId: branchData.currentBranch.id,\n });\n }\n }\n\n // Always delete branch id from config if branching is disabled\n // Avoids incorrect CDN queries at runtime\n if (!settings.branchOptions.enabled) {\n await updateConfig(settings.config, {\n _branchId: null,\n });\n }\n\n return {\n fileVersionData,\n jobData,\n branchData,\n publishMap,\n };\n}\n"],"mappings":";;;;;;;;;;;AAmBA,eAAsB,YACpB,SACA,UACA,SACA,OAMQ;AACR,KAAI,CAAC,gBAAgB,SAAS,CAAE,QAAO,SAAS,EAAE;AAElD,KAAI,CAAC,QAAQ,UAAU,CAAC,oBAAoB,SAAS,CAAE,QAAO,SAAS,EAAE;CAEzE,MAAM,EACJ,OAAO,UACP,iBACA,eACE,MAAM,aAAa,SAAS,UAAU,QAAQ;AAGlD,OAAM,sBAAsB,UAAU,SAAS;AAG/C,KAAI,QAAQ,QAAQ;AAClB,SAAO,QAAQ,sDAAsD;AACrE,oBAAkB,UAAU,gBAAgB;AAC5C,SAAO;;AAGT,KAAI,SAAS,WAAW,KAAK,CAAC,SAAS,QACrC,QAAO,MACL,4EACD;CAIH,IAAI;CACJ,IAAI;CACJ,IAAI;AACJ,KAAI,SAAS,SAAS,GAAG;EACvB,MAAM,EAAE,YAAY,kBAAkB,kBACpC,MAAM,sBAAsB;GAAE,OAAO;GAAU;GAAS;GAAU,CAAC;AACrE,YAAU;AACV,eAAa;AAEb,oBAAkB,6BAA6B,SAAS;AAGxD,MAAI,MAQF,oBAAmB,UAPC,OAAO,QAAQ,gBAAgB,CAAC,KACjD,CAAC,QAAQ,WAAW;GACnB;GACA,WAAW,KAAK;GAChB,UAAU,KAAK;GAChB,EAEqC,EAAE,WAAW,cAAc,GAAG;EAExE,MAAM,eAAe,SAAS,MAC3B,SAAS,KAAK,WAAW,iBAC3B;AACD,MAAI,cAAc,UAChB,OAAM,aAAa,SAAS,QAAQ;GAClC,YAAY,aAAa;GACzB,WAAW,WAAW,cAAc;GACrC,CAAC;;AAMN,KAAI,CAAC,SAAS,cAAc,QAC1B,OAAM,aAAa,SAAS,QAAQ,EAClC,WAAW,MACZ,CAAC;AAGJ,QAAO;EACL;EACA;EACA;EACA;EACD"}
|
|
@@ -88,17 +88,20 @@ async function generateSettings(flags, cwd = process.cwd(), options) {
|
|
|
88
88
|
if (!mergedOptions.config) mergedOptions.config = "";
|
|
89
89
|
if (mergedOptions.projectId) displayProjectId(mergedOptions.projectId);
|
|
90
90
|
mergedOptions.stageTranslations = mergedOptions.stageTranslations ?? false;
|
|
91
|
+
if (mergedOptions.requiresReview !== void 0 && typeof mergedOptions.requiresReview !== "boolean") logErrorAndExit("requiresReview in gt.config.json must be a boolean. Use files.<type>.requiresReview for glob-scoped overrides.");
|
|
92
|
+
mergedOptions.requiresReview = mergedOptions.requiresReview ?? false;
|
|
91
93
|
if (flags.publish) mergedOptions.publish = true;
|
|
92
94
|
else if (gtConfig.publish !== void 0) mergedOptions.publish = gtConfig.publish;
|
|
93
95
|
else mergedOptions.publish = void 0;
|
|
94
96
|
const compositePatterns = Object.entries(mergedOptions.options?.jsonSchema || {}).filter(([, schema]) => schema.composite).map(([key]) => key);
|
|
95
|
-
mergedOptions.files = mergedOptions.files ? resolveFiles(normalizeFilesOptions(mergedOptions.files), mergedOptions.defaultLocale, mergedOptions.locales, cwd, compositePatterns) : {
|
|
97
|
+
mergedOptions.files = mergedOptions.files ? resolveFiles(normalizeFilesOptions(mergedOptions.files), mergedOptions.defaultLocale, mergedOptions.locales, cwd, compositePatterns, mergedOptions.requiresReview) : {
|
|
96
98
|
resolvedPaths: {},
|
|
97
99
|
placeholderPaths: {},
|
|
98
100
|
transformPaths: {},
|
|
99
101
|
transformFormats: {},
|
|
100
102
|
publishPaths: /* @__PURE__ */ new Set(),
|
|
101
103
|
unpublishPaths: /* @__PURE__ */ new Set(),
|
|
104
|
+
requiresReviewPaths: /* @__PURE__ */ new Set(),
|
|
102
105
|
parsingFlags: {},
|
|
103
106
|
gtJson: { parsingFlags: GT_PARSING_FLAGS_DEFAULT }
|
|
104
107
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateSettings.js","names":[],"sources":["../../src/config/generateSettings.ts"],"sourcesContent":["import {\n displayProjectId,\n exitSync,\n logErrorAndExit,\n warnApiKeyInConfig,\n warnDeprecatedField,\n} from '../console/logging.js';\nimport { loadConfig } from '../fs/config/loadConfig.js';\nimport { FilesOptions, Settings } from '../types/index.js';\nimport {\n defaultBaseUrl,\n libraryDefaultLocale,\n} from 'generaltranslation/internal';\nimport { resolveFiles } from '../fs/config/parseFilesConfig.js';\nimport { validateSettings } from './validateSettings.js';\nimport {\n DEFAULT_GIT_REMOTE_NAME,\n GT_DASHBOARD_URL,\n} from '../utils/constants.js';\nimport { resolveProjectId } from '../fs/utils.js';\nimport crypto from 'node:crypto';\nimport { execSync } from 'node:child_process';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { resolveConfig } from './resolveConfig.js';\nimport { gt } from '../utils/gt.js';\nimport { generatePreset } from './optionPresets.js';\nimport { GT_PARSING_FLAGS_DEFAULT } from './defaults.js';\nimport { normalizeFilesOptions } from '../formats/files/transformFormat.js';\nimport { determineLibrary } from '../fs/determineFramework/index.js';\nimport { logger } from '../console/logger.js';\n\nexport const DEFAULT_SRC_PATTERNS = [\n 'src/**/*.{js,jsx,ts,tsx}',\n 'app/**/*.{js,jsx,ts,tsx}',\n 'pages/**/*.{js,jsx,ts,tsx}',\n 'components/**/*.{js,jsx,ts,tsx}',\n];\n\nexport const DEFAULT_PYTHON_SRC_PATTERNS = ['**/*.py'];\nexport const DEFAULT_PYTHON_SRC_EXCLUDES = [\n 'venv/**',\n '.venv/**',\n '__pycache__/**',\n '**/migrations/**',\n '**/tests/**',\n '**/test_*.py',\n '**/*_test.py',\n];\n\ntype GenerateSettingsInput = Partial<Omit<Settings, 'tag'>> & {\n config?: string;\n projectId?: string;\n locales?: string[];\n options?: Settings['options'];\n files?: unknown;\n publish?: boolean;\n tag?: string | boolean;\n message?: string;\n branch?: string;\n enableBranching?: boolean;\n disableBranchDetection?: boolean;\n remoteName?: string;\n experimentalLocalizeStaticImports?: boolean;\n experimentalLocalizeStaticUrls?: boolean;\n experimentalLocalizeRelativeAssets?: boolean;\n experimentalHideDefaultLocale?: boolean;\n experimentalFlattenJsonFiles?: boolean;\n experimentalClearLocaleDirs?: boolean;\n clearLocaleDirsExclude?: string[];\n [key: string]: unknown;\n};\n\nfunction hasConfiguredTranslationFiles(files: unknown): boolean {\n if (!files || typeof files !== 'object' || Array.isArray(files)) {\n return false;\n }\n return Object.keys(files).some((key) => key !== 'gt');\n}\n\n/**\n * Generates settings from any\n * @param flags - The CLI flags to generate settings from\n * @param cwd - The current working directory\n * @param options - Additional options\n * @param options.requireConfig - If true, exit with an error when no config file is found\n * @returns The generated settings\n */\nexport async function generateSettings(\n flags: GenerateSettingsInput,\n cwd: string = process.cwd(),\n options?: { requireConfig?: boolean }\n): Promise<Settings> {\n // Load config file\n let gtConfig: GenerateSettingsInput = {};\n\n if (flags.config && !flags.config.endsWith('.json')) {\n flags.config = `${flags.config}.json`;\n }\n if (flags.config) {\n gtConfig = loadConfig(flags.config);\n } else {\n const config = resolveConfig(cwd);\n if (config) {\n gtConfig = config.config as GenerateSettingsInput;\n flags.config = config.path;\n } else {\n if (options?.requireConfig) {\n return logErrorAndExit(\n 'No gt.config.json file was found. Run `npx gt init` to create one, pass --config, or run this command from your project root.'\n );\n }\n gtConfig = {};\n }\n }\n\n // Warn if apiKey is present in gt.config.json\n if (gtConfig.apiKey) {\n warnApiKeyInConfig(flags.config ?? 'gt.config.json');\n exitSync(1);\n }\n const projectIdEnv = resolveProjectId();\n // Resolve mismatched projectIds\n if (\n gtConfig.projectId &&\n flags.projectId &&\n gtConfig.projectId !== flags.projectId\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but the CLI flag uses ${chalk.green(flags.projectId)}. Use the same projectId in all configs.`\n );\n } else if (\n gtConfig.projectId &&\n projectIdEnv &&\n gtConfig.projectId !== projectIdEnv\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but GT_PROJECT_ID uses ${chalk.green(projectIdEnv)}. Use the same projectId in all configs.`\n );\n }\n\n if (\n flags.options?.docsUrlPattern &&\n !flags.options?.docsUrlPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static URLs could not be localized because the URL pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated URL.'\n );\n }\n\n if (\n flags.options?.docsImportPattern &&\n !flags.options?.docsImportPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static imports could not be localized because the import pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated import path.'\n );\n }\n\n if (flags.options?.copyFiles) {\n for (const file of flags.options.copyFiles) {\n if (!file.includes('[locale]')) {\n logErrorAndExit(\n 'Files could not be copied because the file path is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the copied path.'\n );\n }\n }\n }\n\n // Warn on deprecated includeSourceCodeContext\n const configuredFiles = gtConfig.files as FilesOptions | undefined;\n if (configuredFiles?.gt?.includeSourceCodeContext != null) {\n warnDeprecatedField(\n 'files.gt.includeSourceCodeContext',\n 'files.gt.parsingFlags.includeSourceCodeContext'\n );\n }\n\n // merge options\n const mergedOptions: Settings = { ...gtConfig, ...flags } as Settings;\n\n if (\n determineLibrary().library === 'base' &&\n !hasConfiguredTranslationFiles(mergedOptions.files)\n ) {\n logger.warn(\n chalk.yellow(\n 'No package.json or Python project file found in the current directory. Run this command from the root of your project.'\n )\n );\n }\n\n // Add defaultLocale if not provided\n mergedOptions.defaultLocale =\n mergedOptions.defaultLocale || libraryDefaultLocale;\n\n // merge locales\n mergedOptions.locales = Array.from(\n new Set([...(gtConfig.locales || []), ...(flags.locales || [])])\n );\n // Separate defaultLocale from locales\n mergedOptions.locales = mergedOptions.locales.filter(\n (locale) => locale !== mergedOptions.defaultLocale\n );\n\n // Add apiKey if not provided\n mergedOptions.apiKey = mergedOptions.apiKey || process.env.GT_API_KEY;\n\n // Add projectId if not provided\n mergedOptions.projectId = mergedOptions.projectId || resolveProjectId();\n\n // Add baseUrl if not provided\n mergedOptions.baseUrl = mergedOptions.baseUrl || defaultBaseUrl;\n\n // Add dashboardUrl if not provided\n mergedOptions.dashboardUrl = mergedOptions.dashboardUrl || GT_DASHBOARD_URL;\n\n // Add locales if not provided\n mergedOptions.locales = mergedOptions.locales || [];\n\n // Only set config path if one was actually found or explicitly provided.\n // Do not default to a phantom path — that would cause downstream writes\n // (e.g. updateConfig) to silently create a config file.\n if (!mergedOptions.config) {\n mergedOptions.config = '';\n }\n\n // Display projectId if present\n if (mergedOptions.projectId) displayProjectId(mergedOptions.projectId);\n\n // Add stageTranslations if not provided\n // For human review, always stage the project\n mergedOptions.stageTranslations = mergedOptions.stageTranslations ?? false;\n\n // Add publish — only set if explicitly configured or passed via flag.\n // When neither is set, leave undefined so the publish step knows\n // there is no global publish intent.\n if (flags.publish) {\n mergedOptions.publish = true;\n } else if (gtConfig.publish !== undefined) {\n mergedOptions.publish = gtConfig.publish;\n } else {\n mergedOptions.publish = undefined;\n }\n\n // Don't default src here — each pipeline (JS/Python) has its own defaults.\n // Only set src if the user explicitly provided it via flags or config.\n\n // Resolve all glob patterns in the files object\n const compositePatterns = Object.entries(\n mergedOptions.options?.jsonSchema || {}\n )\n .filter(([, schema]) => schema.composite)\n .map(([key]) => key);\n mergedOptions.files = mergedOptions.files\n ? resolveFiles(\n normalizeFilesOptions(mergedOptions.files as FilesOptions),\n mergedOptions.defaultLocale,\n mergedOptions.locales,\n cwd,\n compositePatterns\n )\n : {\n resolvedPaths: {},\n placeholderPaths: {},\n transformPaths: {},\n transformFormats: {},\n publishPaths: new Set<string>(),\n unpublishPaths: new Set<string>(),\n parsingFlags: {},\n gtJson: {\n parsingFlags: GT_PARSING_FLAGS_DEFAULT,\n },\n };\n\n mergedOptions.options = {\n ...mergedOptions.options,\n mintlify: {\n ...mergedOptions.options?.mintlify,\n inferTitleFromFilename:\n gtConfig.options?.mintlify?.inferTitleFromFilename ||\n mergedOptions.options?.mintlify?.inferTitleFromFilename,\n },\n experimentalLocalizeStaticImports:\n gtConfig.options?.experimentalLocalizeStaticImports ||\n flags.experimentalLocalizeStaticImports,\n experimentalLocalizeStaticUrls:\n gtConfig.options?.experimentalLocalizeStaticUrls ||\n flags.experimentalLocalizeStaticUrls,\n experimentalLocalizeRelativeAssets:\n gtConfig.options?.experimentalLocalizeRelativeAssets ||\n flags.experimentalLocalizeRelativeAssets,\n experimentalHideDefaultLocale:\n gtConfig.options?.experimentalHideDefaultLocale ||\n flags.experimentalHideDefaultLocale,\n experimentalFlattenJsonFiles:\n gtConfig.options?.experimentalFlattenJsonFiles ||\n flags.experimentalFlattenJsonFiles,\n experimentalClearLocaleDirs:\n gtConfig.options?.experimentalClearLocaleDirs ||\n flags.experimentalClearLocaleDirs,\n clearLocaleDirsExclude:\n gtConfig.options?.clearLocaleDirsExclude || flags.clearLocaleDirsExclude,\n };\n\n // Add additional options if provided\n if (mergedOptions.options) {\n if (mergedOptions.options.jsonSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.jsonSchema)) {\n const jsonSchema = mergedOptions.options.jsonSchema[fileGlob];\n if (jsonSchema.preset) {\n mergedOptions.options.jsonSchema[fileGlob] = {\n ...generatePreset(jsonSchema.preset, 'json'),\n ...jsonSchema,\n };\n }\n }\n }\n if (mergedOptions.options.yamlSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.yamlSchema)) {\n const yamlSchema = mergedOptions.options.yamlSchema[fileGlob];\n if (yamlSchema.preset) {\n mergedOptions.options.yamlSchema[fileGlob] = {\n ...generatePreset(yamlSchema.preset, 'yaml'),\n ...yamlSchema,\n };\n }\n }\n }\n }\n\n // Add parsing options if not provided\n mergedOptions.parsingOptions = mergedOptions.parsingOptions || {};\n mergedOptions.parsingOptions.conditionNames = mergedOptions.parsingOptions\n .conditionNames || [\n 'development',\n 'browser',\n 'module',\n 'import',\n 'require',\n 'default',\n ];\n\n // Add branch options if not provided\n const branchOptions = mergedOptions.branchOptions || {};\n // If --branch is set, enable branching\n branchOptions.enabled =\n flags.enableBranching ??\n gtConfig.branchOptions?.enabled ??\n (flags.branch ? true : false);\n branchOptions.currentBranch =\n flags.branch ?? gtConfig.branchOptions?.currentBranch ?? undefined;\n branchOptions.autoDetectBranches = flags.disableBranchDetection\n ? false\n : (gtConfig.branchOptions?.autoDetectBranches ?? true);\n branchOptions.remoteName =\n flags.remoteName ??\n gtConfig.branchOptions?.remoteName ??\n DEFAULT_GIT_REMOTE_NAME;\n mergedOptions.branchOptions = branchOptions;\n\n // Map -m/--message flag to tagMessage\n if (flags.message) {\n mergedOptions.tagMessage = flags.message;\n }\n\n // Resolve tag:\n // --tag (bare) or -m without --tag: try git SHA, fall back to random hex\n // --tag <value>: use as-is\n // No flags: no tag\n if (flags.tag === true || (!flags.tag && mergedOptions.tagMessage)) {\n try {\n mergedOptions.tag = execSync('git rev-parse --short HEAD', {\n encoding: 'utf-8',\n }).trim();\n // If no message provided, use git commit message\n if (!mergedOptions.tagMessage) {\n mergedOptions.tagMessage = execSync('git log -1 --format=%s', {\n encoding: 'utf-8',\n }).trim();\n }\n } catch {\n // Not in a git repo or git unavailable — fall back to random hex\n mergedOptions.tag = crypto.randomBytes(4).toString('hex');\n }\n }\n\n mergedOptions.configDirectory = path.join(cwd, '.gt');\n\n validateSettings(mergedOptions);\n\n // Set up GT instance\n gt.setConfig({\n projectId: mergedOptions.projectId,\n apiKey: mergedOptions.apiKey,\n baseUrl: mergedOptions.baseUrl,\n sourceLocale: mergedOptions.defaultLocale,\n customMapping: mergedOptions.customMapping,\n });\n\n return mergedOptions;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generateSettings.js","names":[],"sources":["../../src/config/generateSettings.ts"],"sourcesContent":["import {\n displayProjectId,\n exitSync,\n logErrorAndExit,\n warnApiKeyInConfig,\n warnDeprecatedField,\n} from '../console/logging.js';\nimport { loadConfig } from '../fs/config/loadConfig.js';\nimport { FilesOptions, Settings } from '../types/index.js';\nimport {\n defaultBaseUrl,\n libraryDefaultLocale,\n} from 'generaltranslation/internal';\nimport { resolveFiles } from '../fs/config/parseFilesConfig.js';\nimport { validateSettings } from './validateSettings.js';\nimport {\n DEFAULT_GIT_REMOTE_NAME,\n GT_DASHBOARD_URL,\n} from '../utils/constants.js';\nimport { resolveProjectId } from '../fs/utils.js';\nimport crypto from 'node:crypto';\nimport { execSync } from 'node:child_process';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { resolveConfig } from './resolveConfig.js';\nimport { gt } from '../utils/gt.js';\nimport { generatePreset } from './optionPresets.js';\nimport { GT_PARSING_FLAGS_DEFAULT } from './defaults.js';\nimport { normalizeFilesOptions } from '../formats/files/transformFormat.js';\nimport { determineLibrary } from '../fs/determineFramework/index.js';\nimport { logger } from '../console/logger.js';\n\nexport const DEFAULT_SRC_PATTERNS = [\n 'src/**/*.{js,jsx,ts,tsx}',\n 'app/**/*.{js,jsx,ts,tsx}',\n 'pages/**/*.{js,jsx,ts,tsx}',\n 'components/**/*.{js,jsx,ts,tsx}',\n];\n\nexport const DEFAULT_PYTHON_SRC_PATTERNS = ['**/*.py'];\nexport const DEFAULT_PYTHON_SRC_EXCLUDES = [\n 'venv/**',\n '.venv/**',\n '__pycache__/**',\n '**/migrations/**',\n '**/tests/**',\n '**/test_*.py',\n '**/*_test.py',\n];\n\ntype GenerateSettingsInput = Partial<Omit<Settings, 'tag'>> & {\n config?: string;\n projectId?: string;\n locales?: string[];\n options?: Settings['options'];\n files?: unknown;\n publish?: boolean;\n tag?: string | boolean;\n message?: string;\n branch?: string;\n enableBranching?: boolean;\n disableBranchDetection?: boolean;\n remoteName?: string;\n experimentalLocalizeStaticImports?: boolean;\n experimentalLocalizeStaticUrls?: boolean;\n experimentalLocalizeRelativeAssets?: boolean;\n experimentalHideDefaultLocale?: boolean;\n experimentalFlattenJsonFiles?: boolean;\n experimentalClearLocaleDirs?: boolean;\n clearLocaleDirsExclude?: string[];\n [key: string]: unknown;\n};\n\nfunction hasConfiguredTranslationFiles(files: unknown): boolean {\n if (!files || typeof files !== 'object' || Array.isArray(files)) {\n return false;\n }\n return Object.keys(files).some((key) => key !== 'gt');\n}\n\n/**\n * Generates settings from any\n * @param flags - The CLI flags to generate settings from\n * @param cwd - The current working directory\n * @param options - Additional options\n * @param options.requireConfig - If true, exit with an error when no config file is found\n * @returns The generated settings\n */\nexport async function generateSettings(\n flags: GenerateSettingsInput,\n cwd: string = process.cwd(),\n options?: { requireConfig?: boolean }\n): Promise<Settings> {\n // Load config file\n let gtConfig: GenerateSettingsInput = {};\n\n if (flags.config && !flags.config.endsWith('.json')) {\n flags.config = `${flags.config}.json`;\n }\n if (flags.config) {\n gtConfig = loadConfig(flags.config);\n } else {\n const config = resolveConfig(cwd);\n if (config) {\n gtConfig = config.config as GenerateSettingsInput;\n flags.config = config.path;\n } else {\n if (options?.requireConfig) {\n return logErrorAndExit(\n 'No gt.config.json file was found. Run `npx gt init` to create one, pass --config, or run this command from your project root.'\n );\n }\n gtConfig = {};\n }\n }\n\n // Warn if apiKey is present in gt.config.json\n if (gtConfig.apiKey) {\n warnApiKeyInConfig(flags.config ?? 'gt.config.json');\n exitSync(1);\n }\n const projectIdEnv = resolveProjectId();\n // Resolve mismatched projectIds\n if (\n gtConfig.projectId &&\n flags.projectId &&\n gtConfig.projectId !== flags.projectId\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but the CLI flag uses ${chalk.green(flags.projectId)}. Use the same projectId in all configs.`\n );\n } else if (\n gtConfig.projectId &&\n projectIdEnv &&\n gtConfig.projectId !== projectIdEnv\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but GT_PROJECT_ID uses ${chalk.green(projectIdEnv)}. Use the same projectId in all configs.`\n );\n }\n\n if (\n flags.options?.docsUrlPattern &&\n !flags.options?.docsUrlPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static URLs could not be localized because the URL pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated URL.'\n );\n }\n\n if (\n flags.options?.docsImportPattern &&\n !flags.options?.docsImportPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static imports could not be localized because the import pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated import path.'\n );\n }\n\n if (flags.options?.copyFiles) {\n for (const file of flags.options.copyFiles) {\n if (!file.includes('[locale]')) {\n logErrorAndExit(\n 'Files could not be copied because the file path is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the copied path.'\n );\n }\n }\n }\n\n // Warn on deprecated includeSourceCodeContext\n const configuredFiles = gtConfig.files as FilesOptions | undefined;\n if (configuredFiles?.gt?.includeSourceCodeContext != null) {\n warnDeprecatedField(\n 'files.gt.includeSourceCodeContext',\n 'files.gt.parsingFlags.includeSourceCodeContext'\n );\n }\n\n // merge options\n const mergedOptions: Settings = { ...gtConfig, ...flags } as Settings;\n\n if (\n determineLibrary().library === 'base' &&\n !hasConfiguredTranslationFiles(mergedOptions.files)\n ) {\n logger.warn(\n chalk.yellow(\n 'No package.json or Python project file found in the current directory. Run this command from the root of your project.'\n )\n );\n }\n\n // Add defaultLocale if not provided\n mergedOptions.defaultLocale =\n mergedOptions.defaultLocale || libraryDefaultLocale;\n\n // merge locales\n mergedOptions.locales = Array.from(\n new Set([...(gtConfig.locales || []), ...(flags.locales || [])])\n );\n // Separate defaultLocale from locales\n mergedOptions.locales = mergedOptions.locales.filter(\n (locale) => locale !== mergedOptions.defaultLocale\n );\n\n // Add apiKey if not provided\n mergedOptions.apiKey = mergedOptions.apiKey || process.env.GT_API_KEY;\n\n // Add projectId if not provided\n mergedOptions.projectId = mergedOptions.projectId || resolveProjectId();\n\n // Add baseUrl if not provided\n mergedOptions.baseUrl = mergedOptions.baseUrl || defaultBaseUrl;\n\n // Add dashboardUrl if not provided\n mergedOptions.dashboardUrl = mergedOptions.dashboardUrl || GT_DASHBOARD_URL;\n\n // Add locales if not provided\n mergedOptions.locales = mergedOptions.locales || [];\n\n // Only set config path if one was actually found or explicitly provided.\n // Do not default to a phantom path — that would cause downstream writes\n // (e.g. updateConfig) to silently create a config file.\n if (!mergedOptions.config) {\n mergedOptions.config = '';\n }\n\n // Display projectId if present\n if (mergedOptions.projectId) displayProjectId(mergedOptions.projectId);\n\n // Add stageTranslations if not provided\n // For human review, always stage the project\n mergedOptions.stageTranslations = mergedOptions.stageTranslations ?? false;\n\n // Top-level default for whether translated files require approval before use.\n // Effective policy is hash-changing, so reject anything but a real boolean.\n if (\n mergedOptions.requiresReview !== undefined &&\n typeof mergedOptions.requiresReview !== 'boolean'\n ) {\n logErrorAndExit(\n 'requiresReview in gt.config.json must be a boolean. Use files.<type>.requiresReview for glob-scoped overrides.'\n );\n }\n mergedOptions.requiresReview = mergedOptions.requiresReview ?? false;\n\n // Add publish — only set if explicitly configured or passed via flag.\n // When neither is set, leave undefined so the publish step knows\n // there is no global publish intent.\n if (flags.publish) {\n mergedOptions.publish = true;\n } else if (gtConfig.publish !== undefined) {\n mergedOptions.publish = gtConfig.publish;\n } else {\n mergedOptions.publish = undefined;\n }\n\n // Don't default src here — each pipeline (JS/Python) has its own defaults.\n // Only set src if the user explicitly provided it via flags or config.\n\n // Resolve all glob patterns in the files object\n const compositePatterns = Object.entries(\n mergedOptions.options?.jsonSchema || {}\n )\n .filter(([, schema]) => schema.composite)\n .map(([key]) => key);\n mergedOptions.files = mergedOptions.files\n ? resolveFiles(\n normalizeFilesOptions(mergedOptions.files as FilesOptions),\n mergedOptions.defaultLocale,\n mergedOptions.locales,\n cwd,\n compositePatterns,\n mergedOptions.requiresReview\n )\n : {\n resolvedPaths: {},\n placeholderPaths: {},\n transformPaths: {},\n transformFormats: {},\n publishPaths: new Set<string>(),\n unpublishPaths: new Set<string>(),\n requiresReviewPaths: new Set<string>(),\n parsingFlags: {},\n gtJson: {\n parsingFlags: GT_PARSING_FLAGS_DEFAULT,\n },\n };\n\n mergedOptions.options = {\n ...mergedOptions.options,\n mintlify: {\n ...mergedOptions.options?.mintlify,\n inferTitleFromFilename:\n gtConfig.options?.mintlify?.inferTitleFromFilename ||\n mergedOptions.options?.mintlify?.inferTitleFromFilename,\n },\n experimentalLocalizeStaticImports:\n gtConfig.options?.experimentalLocalizeStaticImports ||\n flags.experimentalLocalizeStaticImports,\n experimentalLocalizeStaticUrls:\n gtConfig.options?.experimentalLocalizeStaticUrls ||\n flags.experimentalLocalizeStaticUrls,\n experimentalLocalizeRelativeAssets:\n gtConfig.options?.experimentalLocalizeRelativeAssets ||\n flags.experimentalLocalizeRelativeAssets,\n experimentalHideDefaultLocale:\n gtConfig.options?.experimentalHideDefaultLocale ||\n flags.experimentalHideDefaultLocale,\n experimentalFlattenJsonFiles:\n gtConfig.options?.experimentalFlattenJsonFiles ||\n flags.experimentalFlattenJsonFiles,\n experimentalClearLocaleDirs:\n gtConfig.options?.experimentalClearLocaleDirs ||\n flags.experimentalClearLocaleDirs,\n clearLocaleDirsExclude:\n gtConfig.options?.clearLocaleDirsExclude || flags.clearLocaleDirsExclude,\n };\n\n // Add additional options if provided\n if (mergedOptions.options) {\n if (mergedOptions.options.jsonSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.jsonSchema)) {\n const jsonSchema = mergedOptions.options.jsonSchema[fileGlob];\n if (jsonSchema.preset) {\n mergedOptions.options.jsonSchema[fileGlob] = {\n ...generatePreset(jsonSchema.preset, 'json'),\n ...jsonSchema,\n };\n }\n }\n }\n if (mergedOptions.options.yamlSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.yamlSchema)) {\n const yamlSchema = mergedOptions.options.yamlSchema[fileGlob];\n if (yamlSchema.preset) {\n mergedOptions.options.yamlSchema[fileGlob] = {\n ...generatePreset(yamlSchema.preset, 'yaml'),\n ...yamlSchema,\n };\n }\n }\n }\n }\n\n // Add parsing options if not provided\n mergedOptions.parsingOptions = mergedOptions.parsingOptions || {};\n mergedOptions.parsingOptions.conditionNames = mergedOptions.parsingOptions\n .conditionNames || [\n 'development',\n 'browser',\n 'module',\n 'import',\n 'require',\n 'default',\n ];\n\n // Add branch options if not provided\n const branchOptions = mergedOptions.branchOptions || {};\n // If --branch is set, enable branching\n branchOptions.enabled =\n flags.enableBranching ??\n gtConfig.branchOptions?.enabled ??\n (flags.branch ? true : false);\n branchOptions.currentBranch =\n flags.branch ?? gtConfig.branchOptions?.currentBranch ?? undefined;\n branchOptions.autoDetectBranches = flags.disableBranchDetection\n ? false\n : (gtConfig.branchOptions?.autoDetectBranches ?? true);\n branchOptions.remoteName =\n flags.remoteName ??\n gtConfig.branchOptions?.remoteName ??\n DEFAULT_GIT_REMOTE_NAME;\n mergedOptions.branchOptions = branchOptions;\n\n // Map -m/--message flag to tagMessage\n if (flags.message) {\n mergedOptions.tagMessage = flags.message;\n }\n\n // Resolve tag:\n // --tag (bare) or -m without --tag: try git SHA, fall back to random hex\n // --tag <value>: use as-is\n // No flags: no tag\n if (flags.tag === true || (!flags.tag && mergedOptions.tagMessage)) {\n try {\n mergedOptions.tag = execSync('git rev-parse --short HEAD', {\n encoding: 'utf-8',\n }).trim();\n // If no message provided, use git commit message\n if (!mergedOptions.tagMessage) {\n mergedOptions.tagMessage = execSync('git log -1 --format=%s', {\n encoding: 'utf-8',\n }).trim();\n }\n } catch {\n // Not in a git repo or git unavailable — fall back to random hex\n mergedOptions.tag = crypto.randomBytes(4).toString('hex');\n }\n }\n\n mergedOptions.configDirectory = path.join(cwd, '.gt');\n\n validateSettings(mergedOptions);\n\n // Set up GT instance\n gt.setConfig({\n projectId: mergedOptions.projectId,\n apiKey: mergedOptions.apiKey,\n baseUrl: mergedOptions.baseUrl,\n sourceLocale: mergedOptions.defaultLocale,\n customMapping: mergedOptions.customMapping,\n });\n\n return mergedOptions;\n}\n"],"mappings":"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"}
|
|
@@ -4,12 +4,14 @@ import chalk from "chalk";
|
|
|
4
4
|
//#region src/console/displayTranslateSummary.ts
|
|
5
5
|
const CATEGORY_LABELS = {
|
|
6
6
|
skipped_file: "Files skipped",
|
|
7
|
+
pending_review: "Awaiting review approval",
|
|
7
8
|
failed_move: "File moves failed",
|
|
8
9
|
failed_translation: "Translations failed",
|
|
9
10
|
failed_download: "Downloads failed"
|
|
10
11
|
};
|
|
11
12
|
const CATEGORY_ORDER = [
|
|
12
13
|
"skipped_file",
|
|
14
|
+
"pending_review",
|
|
13
15
|
"failed_move",
|
|
14
16
|
"failed_translation",
|
|
15
17
|
"failed_download"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"displayTranslateSummary.js","names":[],"sources":["../../src/console/displayTranslateSummary.ts"],"sourcesContent":["import chalk from 'chalk';\nimport { logger } from './logger.js';\nimport {\n getWarnings,\n hasWarnings,\n type WarningCategory,\n} from '../state/translateWarnings.js';\n\nconst CATEGORY_LABELS: Record<WarningCategory, string> = {\n skipped_file: 'Files skipped',\n failed_move: 'File moves failed',\n failed_translation: 'Translations failed',\n failed_download: 'Downloads failed',\n};\n\nconst CATEGORY_ORDER: WarningCategory[] = [\n 'skipped_file',\n 'failed_move',\n 'failed_translation',\n 'failed_download',\n];\n\nexport function displayTranslateSummary() {\n if (!hasWarnings()) return;\n\n const warnings = getWarnings();\n\n // Group by category\n const grouped = new Map<\n WarningCategory,\n { fileName: string; reason: string }[]\n >();\n for (const w of warnings) {\n let list = grouped.get(w.category);\n if (!list) {\n list = [];\n grouped.set(w.category, list);\n }\n list.push({ fileName: w.fileName, reason: w.reason });\n }\n\n const lines: string[] = [chalk.yellow('⚠ Warnings:'), ''];\n\n for (const category of CATEGORY_ORDER) {\n const items = grouped.get(category);\n if (!items) continue;\n lines.push(` ${CATEGORY_LABELS[category]} (${items.length}):`);\n for (const item of items) {\n lines.push(` - ${item.fileName}: ${item.reason}`);\n }\n lines.push('');\n }\n\n logger.warn(lines.join('\\n'));\n}\n"],"mappings":";;;;AAQA,MAAM,kBAAmD;CACvD,cAAc;CACd,aAAa;CACb,oBAAoB;CACpB,iBAAiB;CAClB;AAED,MAAM,iBAAoC;CACxC;CACA;CACA;CACA;CACD;AAED,SAAgB,0BAA0B;AACxC,KAAI,CAAC,aAAa,CAAE;CAEpB,MAAM,WAAW,aAAa;CAG9B,MAAM,0BAAU,IAAI,KAGjB;AACH,MAAK,MAAM,KAAK,UAAU;EACxB,IAAI,OAAO,QAAQ,IAAI,EAAE,SAAS;AAClC,MAAI,CAAC,MAAM;AACT,UAAO,EAAE;AACT,WAAQ,IAAI,EAAE,UAAU,KAAK;;AAE/B,OAAK,KAAK;GAAE,UAAU,EAAE;GAAU,QAAQ,EAAE;GAAQ,CAAC;;CAGvD,MAAM,QAAkB,CAAC,MAAM,OAAO,eAAe,EAAE,GAAG;AAE1D,MAAK,MAAM,YAAY,gBAAgB;EACrC,MAAM,QAAQ,QAAQ,IAAI,SAAS;AACnC,MAAI,CAAC,MAAO;AACZ,QAAM,KAAK,OAAO,gBAAgB,UAAU,IAAI,MAAM,OAAO,IAAI;AACjE,OAAK,MAAM,QAAQ,MACjB,OAAM,KAAK,WAAW,KAAK,SAAS,IAAI,KAAK,SAAS;AAExD,QAAM,KAAK,GAAG;;AAGhB,QAAO,KAAK,MAAM,KAAK,KAAK,CAAC"}
|
|
1
|
+
{"version":3,"file":"displayTranslateSummary.js","names":[],"sources":["../../src/console/displayTranslateSummary.ts"],"sourcesContent":["import chalk from 'chalk';\nimport { logger } from './logger.js';\nimport {\n getWarnings,\n hasWarnings,\n type WarningCategory,\n} from '../state/translateWarnings.js';\n\nconst CATEGORY_LABELS: Record<WarningCategory, string> = {\n skipped_file: 'Files skipped',\n pending_review: 'Awaiting review approval',\n failed_move: 'File moves failed',\n failed_translation: 'Translations failed',\n failed_download: 'Downloads failed',\n};\n\nconst CATEGORY_ORDER: WarningCategory[] = [\n 'skipped_file',\n 'pending_review',\n 'failed_move',\n 'failed_translation',\n 'failed_download',\n];\n\nexport function displayTranslateSummary() {\n if (!hasWarnings()) return;\n\n const warnings = getWarnings();\n\n // Group by category\n const grouped = new Map<\n WarningCategory,\n { fileName: string; reason: string }[]\n >();\n for (const w of warnings) {\n let list = grouped.get(w.category);\n if (!list) {\n list = [];\n grouped.set(w.category, list);\n }\n list.push({ fileName: w.fileName, reason: w.reason });\n }\n\n const lines: string[] = [chalk.yellow('⚠ Warnings:'), ''];\n\n for (const category of CATEGORY_ORDER) {\n const items = grouped.get(category);\n if (!items) continue;\n lines.push(` ${CATEGORY_LABELS[category]} (${items.length}):`);\n for (const item of items) {\n lines.push(` - ${item.fileName}: ${item.reason}`);\n }\n lines.push('');\n }\n\n logger.warn(lines.join('\\n'));\n}\n"],"mappings":";;;;AAQA,MAAM,kBAAmD;CACvD,cAAc;CACd,gBAAgB;CAChB,aAAa;CACb,oBAAoB;CACpB,iBAAiB;CAClB;AAED,MAAM,iBAAoC;CACxC;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,0BAA0B;AACxC,KAAI,CAAC,aAAa,CAAE;CAEpB,MAAM,WAAW,aAAa;CAG9B,MAAM,0BAAU,IAAI,KAGjB;AACH,MAAK,MAAM,KAAK,UAAU;EACxB,IAAI,OAAO,QAAQ,IAAI,EAAE,SAAS;AAClC,MAAI,CAAC,MAAM;AACT,UAAO,EAAE;AACT,WAAQ,IAAI,EAAE,UAAU,KAAK;;AAE/B,OAAK,KAAK;GAAE,UAAU,EAAE;GAAU,QAAQ,EAAE;GAAQ,CAAC;;CAGvD,MAAM,QAAkB,CAAC,MAAM,OAAO,eAAe,EAAE,GAAG;AAE1D,MAAK,MAAM,YAAY,gBAAgB;EACrC,MAAM,QAAQ,QAAQ,IAAI,SAAS;AACnC,MAAI,CAAC,MAAO;AACZ,QAAM,KAAK,OAAO,gBAAgB,UAAU,IAAI,MAAM,OAAO,IAAI;AACjE,OAAK,MAAM,QAAQ,MACjB,OAAM,KAAK,WAAW,KAAK,SAAS,IAAI,KAAK,SAAS;AAExD,QAAM,KAAK,GAAG;;AAGhB,QAAO,KAAK,MAAM,KAAK,KAAK,CAAC"}
|
package/dist/console/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export declare const warnNestedInternalTComponent: (file: string, location?: str
|
|
|
9
9
|
export declare const warnNonStaticExpressionSync: (file: string, attrName: string, value: string, location?: string) => string;
|
|
10
10
|
export declare const warnInvalidMaxCharsSync: (file: string, value: string, location?: string) => string;
|
|
11
11
|
export declare const warnInvalidFormatSync: (file: string, value: string, location?: string) => string;
|
|
12
|
+
export declare const warnInvalidRequiresReviewSync: (file: string, value: string, location?: string) => string;
|
|
12
13
|
export declare const warnInvalidIcuSync: (file: string, value: string, error: string, location?: string) => string;
|
|
13
14
|
export declare const warnTemplateLiteralSync: (file: string, value: string, location?: string) => string;
|
|
14
15
|
export declare const warnNonStringSync: (file: string, value: string, location?: string) => string;
|
package/dist/console/index.js
CHANGED
|
@@ -16,6 +16,7 @@ const warnNestedInternalTComponent = (file, location) => withLocation(file, `DEB
|
|
|
16
16
|
const warnNonStaticExpressionSync = (file, attrName, value, location) => withLocation(file, `Found non-static expression for attribute ${colorizeIdString(attrName)}: ${colorizeContent(value)}. Change "${colorizeIdString(attrName)}" to ensure this content is translated.`, location);
|
|
17
17
|
const warnInvalidMaxCharsSync = (file, value, location) => withLocation(file, `Found invalid maxChars value: ${colorizeContent(value)}. Change the value to a valid number to ensure this content is translated.`, location);
|
|
18
18
|
const warnInvalidFormatSync = (file, value, location) => withLocation(file, `Found invalid $format value: ${colorizeContent(value)}. Must be one of: 'ICU', 'STRING', 'I18NEXT'.`, location);
|
|
19
|
+
const warnInvalidRequiresReviewSync = (file, value, location) => withLocation(file, `Found invalid requiresReview value: ${colorizeContent(value)}. Must be a boolean literal (true or false) — strings and dynamic expressions are not allowed.`, location);
|
|
19
20
|
const warnInvalidIcuSync = (file, value, error, location) => withWillErrorInNextVersion(withLocation(file, `Found invalid ICU string: ${colorizeContent(value)}. Change the value to a valid ICU to ensure this content is translated. Error message: ${error}.`, location));
|
|
20
21
|
const warnTemplateLiteralSync = (file, value, location) => withLocation(file, `Found template literal with quasis (${colorizeContent(value)}). Change the template literal to a string to ensure this content is translated.`, location);
|
|
21
22
|
const warnNonStringSync = (file, value, location) => withLocation(file, `Found non-string literal (${colorizeContent(value)}). Change the value to a string literal to ensure this content is translated.`, location);
|
|
@@ -53,6 +54,6 @@ const invalidConfigurationError = `The files configuration cannot be used for th
|
|
|
53
54
|
const branchResolutionError = `The current git branch could not be resolved. Specify a branch explicitly or run the command from a git worktree with branch metadata available.`;
|
|
54
55
|
const withOriginalError = (message, error) => error != null ? `${message} Original error: ${String(error)}` : message;
|
|
55
56
|
//#endregion
|
|
56
|
-
export { branchResolutionError, devApiKeyError, invalidConfigurationError, noApiKeyError, noDefaultLocaleError, noFilesError, noLocalesError, noProjectIdError, noSourceFileError, noSupportedFormatError, noVersionIdError, warnApiKeyInConfigSync, warnAsyncUseGT, warnAutoderiveNoResultsSync, warnDataAttrOnBranch, warnDeriveCircularSpreadSync, warnDeriveDestructuringSync, warnDeriveFunctionNoResultsSync, warnDeriveFunctionNotWrappedSync, warnDeriveNonConstVariableSync, warnDeriveOptionalChainingSync, warnDeriveUnresolvableValueSync, warnDuplicateFunctionDefinitionSync, warnFailedToConstructJsxTreeSync, warnFunctionNotFoundSync, warnHasUnwrappedExpressionSync, warnInvalidDeclareVarNameSync, warnInvalidDeriveInitSync, warnInvalidFormatSync, warnInvalidIcuSync, warnInvalidMaxCharsSync, warnInvalidReturnSync, warnMissingReturnSync, warnNestedInternalTComponent, warnNestedTComponent, warnNonStaticExpressionSync, warnNonStringSync, warnRecursiveFunctionCallSync, warnSyncGetGT, warnTemplateLiteralSync, warnTernarySync, warnUnresolvedImportSync, warnVariablePropSync, withLocation, withOriginalError };
|
|
57
|
+
export { branchResolutionError, devApiKeyError, invalidConfigurationError, noApiKeyError, noDefaultLocaleError, noFilesError, noLocalesError, noProjectIdError, noSourceFileError, noSupportedFormatError, noVersionIdError, warnApiKeyInConfigSync, warnAsyncUseGT, warnAutoderiveNoResultsSync, warnDataAttrOnBranch, warnDeriveCircularSpreadSync, warnDeriveDestructuringSync, warnDeriveFunctionNoResultsSync, warnDeriveFunctionNotWrappedSync, warnDeriveNonConstVariableSync, warnDeriveOptionalChainingSync, warnDeriveUnresolvableValueSync, warnDuplicateFunctionDefinitionSync, warnFailedToConstructJsxTreeSync, warnFunctionNotFoundSync, warnHasUnwrappedExpressionSync, warnInvalidDeclareVarNameSync, warnInvalidDeriveInitSync, warnInvalidFormatSync, warnInvalidIcuSync, warnInvalidMaxCharsSync, warnInvalidRequiresReviewSync, warnInvalidReturnSync, warnMissingReturnSync, warnNestedInternalTComponent, warnNestedTComponent, warnNonStaticExpressionSync, warnNonStringSync, warnRecursiveFunctionCallSync, warnSyncGetGT, warnTemplateLiteralSync, warnTernarySync, warnUnresolvedImportSync, warnVariablePropSync, withLocation, withOriginalError };
|
|
57
58
|
|
|
58
59
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/console/index.ts"],"sourcesContent":["import { BRANCH_COMPONENT } from '../react/jsx/utils/constants.js';\nimport {\n colorizeFilepath,\n colorizeComponent,\n colorizeIdString,\n colorizeContent,\n colorizeLine,\n colorizeFunctionName,\n} from './colors.js';\nimport { formatCodeClamp } from './formatting.js';\n\nconst withWillErrorInNextVersion = (message: string): string =>\n `${message} (This will become an error in the next major version of the CLI.)`;\n\n// Derive function related errors\nconst withDeriveComponentError = (message: string): string =>\n `<Derive> rules violation: ${message}`;\n\nconst withDeriveFunctionError = (message: string): string =>\n `derive() rules violation: ${message}`;\n// Synchronous wrappers for backward compatibility\nexport const warnApiKeyInConfigSync = (optionsFilepath: string): string =>\n `${colorizeFilepath(\n optionsFilepath\n )}: Your API key is exposed! Remove it from the file and include it as an environment variable.`;\n\nexport const warnVariablePropSync = (\n file: string,\n attrName: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `${colorizeComponent('<T>')} component has dynamic attribute ${colorizeIdString(attrName)} with value: ${colorizeContent(\n value\n )}. Change ${colorizeIdString(attrName)} to ensure this content is translated.`,\n location\n );\n\nexport const warnInvalidReturnSync = (\n file: string,\n functionName: string,\n expression: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveComponentError(\n `Function ${colorizeFunctionName(functionName)} does not return a derivable (statically analyzable) expression. ${colorizeFunctionName(functionName)} must return either (1) a derivable string literal, (2) another derivable function invocation, (3) derivable JSX content, or (4) a ternary expression. Instead got:\\n${colorizeContent(expression)}`\n ),\n location\n );\n\n// TODO: this is temporary until we handle implicit returns\nexport const warnMissingReturnSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Function ${colorizeFunctionName(functionName)} is wrapped in ${colorizeComponent('<Derive>')} (formerly ${colorizeComponent('<Static>')}) tags but does not have an explicit return statement. Derivable functions must have an explicit return statement.`,\n location\n );\n\nexport const warnHasUnwrappedExpressionSync = (\n file: string,\n unwrappedExpressions: string[],\n id?: string,\n location?: string\n): string =>\n withLocation(\n file,\n `${colorizeComponent('<T>')} component${\n id ? ` with id ${colorizeIdString(id)}` : ''\n } has children that could change at runtime. Use a variable component like ${colorizeComponent(\n '<Var>'\n )} to ensure this content is translated.\\n${colorizeContent(\n unwrappedExpressions.join('\\n')\n )}`,\n location\n );\n\nexport const warnFailedToConstructJsxTreeSync = (\n file: string,\n code: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Failed to construct JsxTree! Call expression is not a valid createElement call: ${colorizeContent(code)}`,\n location\n );\n\nexport const warnNestedTComponent = (file: string, location?: string): string =>\n withLocation(\n file,\n `Found nested <T> component. <T> components cannot be directly nested.`,\n location\n );\n\nexport const warnNestedInternalTComponent = (\n file: string,\n location?: string\n): string =>\n withLocation(\n file,\n `DEBUG: Found nested <GtInternalTranslateJsx> component. <GtInternalTranslateJsx> components cannot be directly nested.`,\n location\n );\n\nexport const warnNonStaticExpressionSync = (\n file: string,\n attrName: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Found non-static expression for attribute ${colorizeIdString(\n attrName\n )}: ${colorizeContent(value)}. Change \"${colorizeIdString(attrName)}\" to ensure this content is translated.`,\n location\n );\n\nexport const warnInvalidMaxCharsSync = (\n file: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Found invalid maxChars value: ${colorizeContent(value)}. Change the value to a valid number to ensure this content is translated.`,\n location\n );\n\nexport const warnInvalidFormatSync = (\n file: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Found invalid $format value: ${colorizeContent(value)}. Must be one of: 'ICU', 'STRING', 'I18NEXT'.`,\n location\n );\n\nexport const warnInvalidIcuSync = (\n file: string,\n value: string,\n error: string,\n location?: string\n): string =>\n withWillErrorInNextVersion(\n withLocation(\n file,\n `Found invalid ICU string: ${colorizeContent(value)}. Change the value to a valid ICU to ensure this content is translated. Error message: ${error}.`,\n location\n )\n );\n\nexport const warnTemplateLiteralSync = (\n file: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Found template literal with quasis (${colorizeContent(value)}). Change the template literal to a string to ensure this content is translated.`,\n location\n );\n\nexport const warnNonStringSync = (\n file: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Found non-string literal (${colorizeContent(value)}). Change the value to a string literal to ensure this content is translated.`,\n location\n );\n\nexport const warnAsyncUseGT = (file: string, location?: string): string =>\n withLocation(\n file,\n `Found useGT() in an async function. Use getGT() instead, or make the function synchronous.`,\n location\n );\n\nexport const warnSyncGetGT = (file: string, location?: string): string =>\n withLocation(\n file,\n `Found getGT() in a synchronous function. Use useGT() instead, or make the function async.`,\n location\n );\n\nexport const warnTernarySync = (file: string, location?: string): string =>\n withLocation(\n file,\n 'Found ternary expression. A Branch component may be more appropriate here.',\n location\n );\n\nexport const withLocation = (\n file: string,\n message: string,\n location?: string\n): string =>\n `${colorizeFilepath(file)}${location ? ` (${colorizeLine(location)})` : ''}: ${message}`;\n\nexport const warnFunctionNotFoundSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Function ${colorizeFunctionName(functionName)} definition could not be resolved. This might affect translation resolution for this ${colorizeComponent('<T>')} component.`,\n location\n );\n\nexport const warnInvalidDeclareVarNameSync = (\n file: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Found invalid declareVar() $name tag. Must be a derivable (statically analyzable) expression. Received: ${colorizeContent(value)}.`,\n location\n );\n\nexport const warnDuplicateFunctionDefinitionSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Function ${colorizeFunctionName(functionName)} is defined multiple times. Only the first definition will be used.`,\n location\n );\n\nexport const warnInvalidDeriveInitSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveComponentError(\n `The definition for ${colorizeFunctionName(functionName)} could not be resolved. When using arrow syntax to define a derivable (statically analyzable) function, the right hand side or the assignment MUST only contain the arrow function itself and no other expressions.\nExample: ${colorizeContent(`const ${colorizeFunctionName(functionName)} = () => { ... }`)}\nInvalid: ${colorizeContent(`const ${colorizeFunctionName(functionName)} = [() => { ... }][0]`)}`\n ),\n location\n );\n\nexport const warnDataAttrOnBranch = (\n file: string,\n attrName: string,\n location?: string\n): string =>\n withLocation(\n file,\n `${colorizeComponent(`<${BRANCH_COMPONENT}>`)} component ignores attributes prefixed with ${colorizeIdString('\"data-\"')}. Found ${colorizeIdString(attrName)}. Remove it or use a different attribute name.`,\n location\n );\n\nexport const warnRecursiveFunctionCallSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveComponentError(\n `Recursive function call detected: ${colorizeFunctionName(functionName)}. A derivable (statically analyzable) function cannot use recursive calls to construct its result.`\n ),\n location\n );\n\nexport const warnDeriveFunctionNotWrappedSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Could not resolve ${colorizeFunctionName(formatCodeClamp(functionName))}. This call is not wrapped in derive() (formerly declareStatic()). Ensure the function is properly wrapped with derive() and does not have circular import dependencies.`\n ),\n location\n );\n\nexport const warnDeriveNonConstVariableSync = (\n file: string,\n varName: string,\n kind: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Variable ${colorizeFunctionName(varName)} is declared with '${kind}' but only 'const' declarations can be resolved statically. Change it to 'const'.`\n ),\n location\n );\n\nexport const warnDeriveFunctionNoResultsSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Could not resolve ${colorizeFunctionName(formatCodeClamp(functionName))}. derive() (formerly declareStatic()) can only receive function invocations and cannot use undefined values or looped calls to construct its result.`\n ),\n location\n );\n\nexport const warnAutoderiveNoResultsSync = (\n file: string,\n expression: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Autoderive could not resolve ${colorizeFunctionName(formatCodeClamp(expression))}. Only function calls with statically determinable return values can be used directly in t(). Consider wrapping with derive() for explicit derivation, or use an interpolation variable instead.`,\n location\n );\n\nexport const warnDeriveUnresolvableValueSync = (\n file: string,\n key: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Object property ${colorizeFunctionName(formatCodeClamp(key))} could not be resolved to a static string value. Only string literals, template literals, conditionals, and function calls returning strings are supported.`\n ),\n location\n );\n\nexport const warnDeriveCircularSpreadSync = (\n file: string,\n varName: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Circular spread detected involving ${colorizeFunctionName(varName)}. Spread references that form a cycle cannot be resolved statically.`\n ),\n location\n );\n\nexport const warnDeriveDestructuringSync = (\n file: string,\n varName: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Variable ${colorizeFunctionName(varName)} uses destructuring syntax, which is not yet supported in derive(). Assign the value to a const variable directly instead.`\n ),\n location\n );\n\nexport const warnDeriveOptionalChainingSync = (\n file: string,\n code: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Optional chaining (${colorizeFunctionName(formatCodeClamp(code))}) is not supported in derive(). Optional chaining implies the value could be undefined, which cannot be resolved statically. Use a non-optional access instead.`\n ),\n location\n );\n\nexport const warnUnresolvedImportSync = (\n file: string,\n functionName: string,\n importPath: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Could not resolve import ${colorizeFunctionName(importPath)} for function ${colorizeFunctionName(functionName)}. Translation strings inside this function will not be extracted.`,\n location\n );\n\n// Re-export error messages\nexport const noLocalesError = `No target locales were found. Add locales to gt.config.json or pass them with the --locales flag.`;\nexport const noDefaultLocaleError = `No default locale was found. Add defaultLocale to gt.config.json or pass it with --default-locale.`;\nexport const noFilesError = `The files configuration is missing or invalid. Check the files section in gt.config.json and try again.`;\nexport const noSourceFileError = `No source translation file was found. Check your translations directory and default locale configuration.`;\nexport const noSupportedFormatError = `The translation file format is not supported. Use a supported file extension in translationsDir.`;\nexport const noApiKeyError = `No API key was found. Pass --api-key or set the GT_API_KEY environment variable.`;\nexport const devApiKeyError = `Development API keys cannot be used with the General Translation API. Use a production API key instead.\\nGenerate a production API key with: npx gt auth -t production`;\nexport const noProjectIdError = `No project ID was found. Pass --project-id, add projectId to gt.config.json, or set the GT_PROJECT_ID environment variable.`;\nexport const noVersionIdError = `No version ID was found. Pass --version-id or add _versionId to gt.config.json.`;\nexport const invalidConfigurationError = `The files configuration cannot be used for this operation. Provide a valid download configuration or set --publish true to upload translations to the CDN.`;\nexport const branchResolutionError = `The current git branch could not be resolved. Specify a branch explicitly or run the command from a git worktree with branch metadata available.`;\n\nexport const withOriginalError = (message: string, error: unknown): string =>\n error != null ? `${message} Original error: ${String(error)}` : message;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/console/index.ts"],"sourcesContent":["import { BRANCH_COMPONENT } from '../react/jsx/utils/constants.js';\nimport {\n colorizeFilepath,\n colorizeComponent,\n colorizeIdString,\n colorizeContent,\n colorizeLine,\n colorizeFunctionName,\n} from './colors.js';\nimport { formatCodeClamp } from './formatting.js';\n\nconst withWillErrorInNextVersion = (message: string): string =>\n `${message} (This will become an error in the next major version of the CLI.)`;\n\n// Derive function related errors\nconst withDeriveComponentError = (message: string): string =>\n `<Derive> rules violation: ${message}`;\n\nconst withDeriveFunctionError = (message: string): string =>\n `derive() rules violation: ${message}`;\n// Synchronous wrappers for backward compatibility\nexport const warnApiKeyInConfigSync = (optionsFilepath: string): string =>\n `${colorizeFilepath(\n optionsFilepath\n )}: Your API key is exposed! Remove it from the file and include it as an environment variable.`;\n\nexport const warnVariablePropSync = (\n file: string,\n attrName: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `${colorizeComponent('<T>')} component has dynamic attribute ${colorizeIdString(attrName)} with value: ${colorizeContent(\n value\n )}. Change ${colorizeIdString(attrName)} to ensure this content is translated.`,\n location\n );\n\nexport const warnInvalidReturnSync = (\n file: string,\n functionName: string,\n expression: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveComponentError(\n `Function ${colorizeFunctionName(functionName)} does not return a derivable (statically analyzable) expression. ${colorizeFunctionName(functionName)} must return either (1) a derivable string literal, (2) another derivable function invocation, (3) derivable JSX content, or (4) a ternary expression. Instead got:\\n${colorizeContent(expression)}`\n ),\n location\n );\n\n// TODO: this is temporary until we handle implicit returns\nexport const warnMissingReturnSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Function ${colorizeFunctionName(functionName)} is wrapped in ${colorizeComponent('<Derive>')} (formerly ${colorizeComponent('<Static>')}) tags but does not have an explicit return statement. Derivable functions must have an explicit return statement.`,\n location\n );\n\nexport const warnHasUnwrappedExpressionSync = (\n file: string,\n unwrappedExpressions: string[],\n id?: string,\n location?: string\n): string =>\n withLocation(\n file,\n `${colorizeComponent('<T>')} component${\n id ? ` with id ${colorizeIdString(id)}` : ''\n } has children that could change at runtime. Use a variable component like ${colorizeComponent(\n '<Var>'\n )} to ensure this content is translated.\\n${colorizeContent(\n unwrappedExpressions.join('\\n')\n )}`,\n location\n );\n\nexport const warnFailedToConstructJsxTreeSync = (\n file: string,\n code: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Failed to construct JsxTree! Call expression is not a valid createElement call: ${colorizeContent(code)}`,\n location\n );\n\nexport const warnNestedTComponent = (file: string, location?: string): string =>\n withLocation(\n file,\n `Found nested <T> component. <T> components cannot be directly nested.`,\n location\n );\n\nexport const warnNestedInternalTComponent = (\n file: string,\n location?: string\n): string =>\n withLocation(\n file,\n `DEBUG: Found nested <GtInternalTranslateJsx> component. <GtInternalTranslateJsx> components cannot be directly nested.`,\n location\n );\n\nexport const warnNonStaticExpressionSync = (\n file: string,\n attrName: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Found non-static expression for attribute ${colorizeIdString(\n attrName\n )}: ${colorizeContent(value)}. Change \"${colorizeIdString(attrName)}\" to ensure this content is translated.`,\n location\n );\n\nexport const warnInvalidMaxCharsSync = (\n file: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Found invalid maxChars value: ${colorizeContent(value)}. Change the value to a valid number to ensure this content is translated.`,\n location\n );\n\nexport const warnInvalidFormatSync = (\n file: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Found invalid $format value: ${colorizeContent(value)}. Must be one of: 'ICU', 'STRING', 'I18NEXT'.`,\n location\n );\n\nexport const warnInvalidRequiresReviewSync = (\n file: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Found invalid requiresReview value: ${colorizeContent(value)}. Must be a boolean literal (true or false) — strings and dynamic expressions are not allowed.`,\n location\n );\n\nexport const warnInvalidIcuSync = (\n file: string,\n value: string,\n error: string,\n location?: string\n): string =>\n withWillErrorInNextVersion(\n withLocation(\n file,\n `Found invalid ICU string: ${colorizeContent(value)}. Change the value to a valid ICU to ensure this content is translated. Error message: ${error}.`,\n location\n )\n );\n\nexport const warnTemplateLiteralSync = (\n file: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Found template literal with quasis (${colorizeContent(value)}). Change the template literal to a string to ensure this content is translated.`,\n location\n );\n\nexport const warnNonStringSync = (\n file: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Found non-string literal (${colorizeContent(value)}). Change the value to a string literal to ensure this content is translated.`,\n location\n );\n\nexport const warnAsyncUseGT = (file: string, location?: string): string =>\n withLocation(\n file,\n `Found useGT() in an async function. Use getGT() instead, or make the function synchronous.`,\n location\n );\n\nexport const warnSyncGetGT = (file: string, location?: string): string =>\n withLocation(\n file,\n `Found getGT() in a synchronous function. Use useGT() instead, or make the function async.`,\n location\n );\n\nexport const warnTernarySync = (file: string, location?: string): string =>\n withLocation(\n file,\n 'Found ternary expression. A Branch component may be more appropriate here.',\n location\n );\n\nexport const withLocation = (\n file: string,\n message: string,\n location?: string\n): string =>\n `${colorizeFilepath(file)}${location ? ` (${colorizeLine(location)})` : ''}: ${message}`;\n\nexport const warnFunctionNotFoundSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Function ${colorizeFunctionName(functionName)} definition could not be resolved. This might affect translation resolution for this ${colorizeComponent('<T>')} component.`,\n location\n );\n\nexport const warnInvalidDeclareVarNameSync = (\n file: string,\n value: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Found invalid declareVar() $name tag. Must be a derivable (statically analyzable) expression. Received: ${colorizeContent(value)}.`,\n location\n );\n\nexport const warnDuplicateFunctionDefinitionSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Function ${colorizeFunctionName(functionName)} is defined multiple times. Only the first definition will be used.`,\n location\n );\n\nexport const warnInvalidDeriveInitSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveComponentError(\n `The definition for ${colorizeFunctionName(functionName)} could not be resolved. When using arrow syntax to define a derivable (statically analyzable) function, the right hand side or the assignment MUST only contain the arrow function itself and no other expressions.\nExample: ${colorizeContent(`const ${colorizeFunctionName(functionName)} = () => { ... }`)}\nInvalid: ${colorizeContent(`const ${colorizeFunctionName(functionName)} = [() => { ... }][0]`)}`\n ),\n location\n );\n\nexport const warnDataAttrOnBranch = (\n file: string,\n attrName: string,\n location?: string\n): string =>\n withLocation(\n file,\n `${colorizeComponent(`<${BRANCH_COMPONENT}>`)} component ignores attributes prefixed with ${colorizeIdString('\"data-\"')}. Found ${colorizeIdString(attrName)}. Remove it or use a different attribute name.`,\n location\n );\n\nexport const warnRecursiveFunctionCallSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveComponentError(\n `Recursive function call detected: ${colorizeFunctionName(functionName)}. A derivable (statically analyzable) function cannot use recursive calls to construct its result.`\n ),\n location\n );\n\nexport const warnDeriveFunctionNotWrappedSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Could not resolve ${colorizeFunctionName(formatCodeClamp(functionName))}. This call is not wrapped in derive() (formerly declareStatic()). Ensure the function is properly wrapped with derive() and does not have circular import dependencies.`\n ),\n location\n );\n\nexport const warnDeriveNonConstVariableSync = (\n file: string,\n varName: string,\n kind: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Variable ${colorizeFunctionName(varName)} is declared with '${kind}' but only 'const' declarations can be resolved statically. Change it to 'const'.`\n ),\n location\n );\n\nexport const warnDeriveFunctionNoResultsSync = (\n file: string,\n functionName: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Could not resolve ${colorizeFunctionName(formatCodeClamp(functionName))}. derive() (formerly declareStatic()) can only receive function invocations and cannot use undefined values or looped calls to construct its result.`\n ),\n location\n );\n\nexport const warnAutoderiveNoResultsSync = (\n file: string,\n expression: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Autoderive could not resolve ${colorizeFunctionName(formatCodeClamp(expression))}. Only function calls with statically determinable return values can be used directly in t(). Consider wrapping with derive() for explicit derivation, or use an interpolation variable instead.`,\n location\n );\n\nexport const warnDeriveUnresolvableValueSync = (\n file: string,\n key: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Object property ${colorizeFunctionName(formatCodeClamp(key))} could not be resolved to a static string value. Only string literals, template literals, conditionals, and function calls returning strings are supported.`\n ),\n location\n );\n\nexport const warnDeriveCircularSpreadSync = (\n file: string,\n varName: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Circular spread detected involving ${colorizeFunctionName(varName)}. Spread references that form a cycle cannot be resolved statically.`\n ),\n location\n );\n\nexport const warnDeriveDestructuringSync = (\n file: string,\n varName: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Variable ${colorizeFunctionName(varName)} uses destructuring syntax, which is not yet supported in derive(). Assign the value to a const variable directly instead.`\n ),\n location\n );\n\nexport const warnDeriveOptionalChainingSync = (\n file: string,\n code: string,\n location?: string\n): string =>\n withLocation(\n file,\n withDeriveFunctionError(\n `Optional chaining (${colorizeFunctionName(formatCodeClamp(code))}) is not supported in derive(). Optional chaining implies the value could be undefined, which cannot be resolved statically. Use a non-optional access instead.`\n ),\n location\n );\n\nexport const warnUnresolvedImportSync = (\n file: string,\n functionName: string,\n importPath: string,\n location?: string\n): string =>\n withLocation(\n file,\n `Could not resolve import ${colorizeFunctionName(importPath)} for function ${colorizeFunctionName(functionName)}. Translation strings inside this function will not be extracted.`,\n location\n );\n\n// Re-export error messages\nexport const noLocalesError = `No target locales were found. Add locales to gt.config.json or pass them with the --locales flag.`;\nexport const noDefaultLocaleError = `No default locale was found. Add defaultLocale to gt.config.json or pass it with --default-locale.`;\nexport const noFilesError = `The files configuration is missing or invalid. Check the files section in gt.config.json and try again.`;\nexport const noSourceFileError = `No source translation file was found. Check your translations directory and default locale configuration.`;\nexport const noSupportedFormatError = `The translation file format is not supported. Use a supported file extension in translationsDir.`;\nexport const noApiKeyError = `No API key was found. Pass --api-key or set the GT_API_KEY environment variable.`;\nexport const devApiKeyError = `Development API keys cannot be used with the General Translation API. Use a production API key instead.\\nGenerate a production API key with: npx gt auth -t production`;\nexport const noProjectIdError = `No project ID was found. Pass --project-id, add projectId to gt.config.json, or set the GT_PROJECT_ID environment variable.`;\nexport const noVersionIdError = `No version ID was found. Pass --version-id or add _versionId to gt.config.json.`;\nexport const invalidConfigurationError = `The files configuration cannot be used for this operation. Provide a valid download configuration or set --publish true to upload translations to the CDN.`;\nexport const branchResolutionError = `The current git branch could not be resolved. Specify a branch explicitly or run the command from a git worktree with branch metadata available.`;\n\nexport const withOriginalError = (message: string, error: unknown): string =>\n error != null ? `${message} Original error: ${String(error)}` : message;\n"],"mappings":"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"}
|
|
@@ -10,6 +10,7 @@ async function calculateHashes(updates) {
|
|
|
10
10
|
...update.metadata.context && { context: update.metadata.context },
|
|
11
11
|
...update.metadata.id && { id: update.metadata.id },
|
|
12
12
|
...update.metadata.maxChars != null && { maxChars: update.metadata.maxChars },
|
|
13
|
+
...update.metadata.requiresReview === true && { requiresReview: true },
|
|
13
14
|
dataFormat: update.dataFormat
|
|
14
15
|
});
|
|
15
16
|
update.metadata.hash = hash;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postProcess.js","names":[],"sources":["../../src/extraction/postProcess.ts"],"sourcesContent":["import { Updates } from '../types/index.js';\nimport { hashSource, hashString } from 'generaltranslation/id';\nimport type { SourceCode } from '../react/jsx/utils/extractSourceCode.js';\n\n/**\n * Calculate hashes for all updates in parallel\n */\nexport async function calculateHashes(updates: Updates): Promise<void> {\n await Promise.all(\n updates.map(async (update) => {\n const hash = hashSource({\n source: update.source,\n ...(update.metadata.context && { context: update.metadata.context }),\n ...(update.metadata.id && { id: update.metadata.id }),\n ...(update.metadata.maxChars != null && {\n maxChars: update.metadata.maxChars,\n }),\n dataFormat: update.dataFormat,\n });\n update.metadata.hash = hash;\n })\n );\n}\n\n/**\n * Dedupe entries by hash, merging filePaths\n */\nexport function dedupeUpdates(updates: Updates): void {\n const mergedByHash = new Map<string, (typeof updates)[number]>();\n const noHashUpdates: (typeof updates)[number][] = [];\n\n for (const update of updates) {\n const hash = update.metadata.hash;\n if (!hash) {\n noHashUpdates.push(update);\n continue;\n }\n\n const existing = mergedByHash.get(hash);\n if (!existing) {\n mergedByHash.set(hash, update);\n continue;\n }\n\n const existingPaths = Array.isArray(existing.metadata.filePaths)\n ? existing.metadata.filePaths.slice()\n : [];\n const newPaths = Array.isArray(update.metadata.filePaths)\n ? update.metadata.filePaths\n : [];\n\n for (const p of newPaths) {\n if (!existingPaths.includes(p)) {\n existingPaths.push(p);\n }\n }\n\n if (existingPaths.length) {\n existing.metadata.filePaths = existingPaths;\n }\n\n // Merge sourceCode entries\n const newSourceCode = update.metadata.sourceCode as\n | Record<string, SourceCode[]>\n | undefined;\n if (newSourceCode && typeof newSourceCode === 'object') {\n if (!existing.metadata.sourceCode) {\n existing.metadata.sourceCode = {};\n }\n const existingSourceCode = existing.metadata.sourceCode as Record<\n string,\n SourceCode[]\n >;\n for (const [file, entries] of Object.entries(newSourceCode)) {\n if (!existingSourceCode[file]) {\n existingSourceCode[file] = [];\n }\n existingSourceCode[file].push(...entries);\n }\n }\n }\n\n const mergedUpdates = [...mergedByHash.values(), ...noHashUpdates];\n updates.splice(0, updates.length, ...mergedUpdates);\n}\n\n/**\n * Mark derive updates as related by attaching a shared id to derive content.\n * Id is calculated as the hash of the derive children's combined hashes.\n */\nexport function linkDeriveUpdates(updates: Updates): void {\n const temporaryDeriveIdToUpdates = updates.reduce(\n (acc: Record<string, Updates[number][]>, update: Updates[number]) => {\n if (update.metadata.staticId) {\n if (!acc[update.metadata.staticId]) {\n acc[update.metadata.staticId] = [];\n }\n acc[update.metadata.staticId].push(update);\n }\n return acc;\n },\n {} as Record<string, Updates[number][]>\n );\n\n Object.values(temporaryDeriveIdToUpdates).forEach((deriveUpdates) => {\n const hashes = deriveUpdates\n .map((update) => update.metadata.hash)\n .sort()\n .join('-');\n const sharedDeriveId = hashString(hashes);\n deriveUpdates.forEach((update) => {\n update.metadata.staticId = sharedDeriveId;\n });\n });\n}\n"],"mappings":";;;;;AAOA,eAAsB,gBAAgB,SAAiC;AACrE,OAAM,QAAQ,IACZ,QAAQ,IAAI,OAAO,WAAW;EAC5B,MAAM,OAAO,WAAW;GACtB,QAAQ,OAAO;GACf,GAAI,OAAO,SAAS,WAAW,EAAE,SAAS,OAAO,SAAS,SAAS;GACnE,GAAI,OAAO,SAAS,MAAM,EAAE,IAAI,OAAO,SAAS,IAAI;GACpD,GAAI,OAAO,SAAS,YAAY,QAAQ,EACtC,UAAU,OAAO,SAAS,UAC3B;GACD,YAAY,OAAO;GACpB,CAAC;AACF,SAAO,SAAS,OAAO;GACvB,CACH;;;;;AAMH,SAAgB,cAAc,SAAwB;CACpD,MAAM,+BAAe,IAAI,KAAuC;CAChE,MAAM,gBAA4C,EAAE;AAEpD,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,OAAO,OAAO,SAAS;AAC7B,MAAI,CAAC,MAAM;AACT,iBAAc,KAAK,OAAO;AAC1B;;EAGF,MAAM,WAAW,aAAa,IAAI,KAAK;AACvC,MAAI,CAAC,UAAU;AACb,gBAAa,IAAI,MAAM,OAAO;AAC9B;;EAGF,MAAM,gBAAgB,MAAM,QAAQ,SAAS,SAAS,UAAU,GAC5D,SAAS,SAAS,UAAU,OAAO,GACnC,EAAE;EACN,MAAM,WAAW,MAAM,QAAQ,OAAO,SAAS,UAAU,GACrD,OAAO,SAAS,YAChB,EAAE;AAEN,OAAK,MAAM,KAAK,SACd,KAAI,CAAC,cAAc,SAAS,EAAE,CAC5B,eAAc,KAAK,EAAE;AAIzB,MAAI,cAAc,OAChB,UAAS,SAAS,YAAY;EAIhC,MAAM,gBAAgB,OAAO,SAAS;AAGtC,MAAI,iBAAiB,OAAO,kBAAkB,UAAU;AACtD,OAAI,CAAC,SAAS,SAAS,WACrB,UAAS,SAAS,aAAa,EAAE;GAEnC,MAAM,qBAAqB,SAAS,SAAS;AAI7C,QAAK,MAAM,CAAC,MAAM,YAAY,OAAO,QAAQ,cAAc,EAAE;AAC3D,QAAI,CAAC,mBAAmB,MACtB,oBAAmB,QAAQ,EAAE;AAE/B,uBAAmB,MAAM,KAAK,GAAG,QAAQ;;;;CAK/C,MAAM,gBAAgB,CAAC,GAAG,aAAa,QAAQ,EAAE,GAAG,cAAc;AAClE,SAAQ,OAAO,GAAG,QAAQ,QAAQ,GAAG,cAAc;;;;;;AAOrD,SAAgB,kBAAkB,SAAwB;CACxD,MAAM,6BAA6B,QAAQ,QACxC,KAAwC,WAA4B;AACnE,MAAI,OAAO,SAAS,UAAU;AAC5B,OAAI,CAAC,IAAI,OAAO,SAAS,UACvB,KAAI,OAAO,SAAS,YAAY,EAAE;AAEpC,OAAI,OAAO,SAAS,UAAU,KAAK,OAAO;;AAE5C,SAAO;IAET,EAAE,CACH;AAED,QAAO,OAAO,2BAA2B,CAAC,SAAS,kBAAkB;EAKnE,MAAM,iBAAiB,WAJR,cACZ,KAAK,WAAW,OAAO,SAAS,KAAK,CACrC,MAAM,CACN,KAAK,IACgC,CAAC;AACzC,gBAAc,SAAS,WAAW;AAChC,UAAO,SAAS,WAAW;IAC3B;GACF"}
|
|
1
|
+
{"version":3,"file":"postProcess.js","names":[],"sources":["../../src/extraction/postProcess.ts"],"sourcesContent":["import { Updates } from '../types/index.js';\nimport { hashSource, hashString } from 'generaltranslation/id';\nimport type { SourceCode } from '../react/jsx/utils/extractSourceCode.js';\n\n/**\n * Calculate hashes for all updates in parallel\n */\nexport async function calculateHashes(updates: Updates): Promise<void> {\n await Promise.all(\n updates.map(async (update) => {\n const hash = hashSource({\n source: update.source,\n ...(update.metadata.context && { context: update.metadata.context }),\n ...(update.metadata.id && { id: update.metadata.id }),\n ...(update.metadata.maxChars != null && {\n maxChars: update.metadata.maxChars,\n }),\n // Only the explicit prop is hash-changing. Config-level review\n // defaults materialize into GTJSON metadata without touching hashes,\n // because the runtime computes lookup hashes from props alone.\n ...(update.metadata.requiresReview === true && {\n requiresReview: true,\n }),\n dataFormat: update.dataFormat,\n });\n update.metadata.hash = hash;\n })\n );\n}\n\n/**\n * Dedupe entries by hash, merging filePaths\n */\nexport function dedupeUpdates(updates: Updates): void {\n const mergedByHash = new Map<string, (typeof updates)[number]>();\n const noHashUpdates: (typeof updates)[number][] = [];\n\n for (const update of updates) {\n const hash = update.metadata.hash;\n if (!hash) {\n noHashUpdates.push(update);\n continue;\n }\n\n const existing = mergedByHash.get(hash);\n if (!existing) {\n mergedByHash.set(hash, update);\n continue;\n }\n\n const existingPaths = Array.isArray(existing.metadata.filePaths)\n ? existing.metadata.filePaths.slice()\n : [];\n const newPaths = Array.isArray(update.metadata.filePaths)\n ? update.metadata.filePaths\n : [];\n\n for (const p of newPaths) {\n if (!existingPaths.includes(p)) {\n existingPaths.push(p);\n }\n }\n\n if (existingPaths.length) {\n existing.metadata.filePaths = existingPaths;\n }\n\n // Merge sourceCode entries\n const newSourceCode = update.metadata.sourceCode as\n | Record<string, SourceCode[]>\n | undefined;\n if (newSourceCode && typeof newSourceCode === 'object') {\n if (!existing.metadata.sourceCode) {\n existing.metadata.sourceCode = {};\n }\n const existingSourceCode = existing.metadata.sourceCode as Record<\n string,\n SourceCode[]\n >;\n for (const [file, entries] of Object.entries(newSourceCode)) {\n if (!existingSourceCode[file]) {\n existingSourceCode[file] = [];\n }\n existingSourceCode[file].push(...entries);\n }\n }\n }\n\n const mergedUpdates = [...mergedByHash.values(), ...noHashUpdates];\n updates.splice(0, updates.length, ...mergedUpdates);\n}\n\n/**\n * Mark derive updates as related by attaching a shared id to derive content.\n * Id is calculated as the hash of the derive children's combined hashes.\n */\nexport function linkDeriveUpdates(updates: Updates): void {\n const temporaryDeriveIdToUpdates = updates.reduce(\n (acc: Record<string, Updates[number][]>, update: Updates[number]) => {\n if (update.metadata.staticId) {\n if (!acc[update.metadata.staticId]) {\n acc[update.metadata.staticId] = [];\n }\n acc[update.metadata.staticId].push(update);\n }\n return acc;\n },\n {} as Record<string, Updates[number][]>\n );\n\n Object.values(temporaryDeriveIdToUpdates).forEach((deriveUpdates) => {\n const hashes = deriveUpdates\n .map((update) => update.metadata.hash)\n .sort()\n .join('-');\n const sharedDeriveId = hashString(hashes);\n deriveUpdates.forEach((update) => {\n update.metadata.staticId = sharedDeriveId;\n });\n });\n}\n"],"mappings":";;;;;AAOA,eAAsB,gBAAgB,SAAiC;AACrE,OAAM,QAAQ,IACZ,QAAQ,IAAI,OAAO,WAAW;EAC5B,MAAM,OAAO,WAAW;GACtB,QAAQ,OAAO;GACf,GAAI,OAAO,SAAS,WAAW,EAAE,SAAS,OAAO,SAAS,SAAS;GACnE,GAAI,OAAO,SAAS,MAAM,EAAE,IAAI,OAAO,SAAS,IAAI;GACpD,GAAI,OAAO,SAAS,YAAY,QAAQ,EACtC,UAAU,OAAO,SAAS,UAC3B;GAID,GAAI,OAAO,SAAS,mBAAmB,QAAQ,EAC7C,gBAAgB,MACjB;GACD,YAAY,OAAO;GACpB,CAAC;AACF,SAAO,SAAS,OAAO;GACvB,CACH;;;;;AAMH,SAAgB,cAAc,SAAwB;CACpD,MAAM,+BAAe,IAAI,KAAuC;CAChE,MAAM,gBAA4C,EAAE;AAEpD,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,OAAO,OAAO,SAAS;AAC7B,MAAI,CAAC,MAAM;AACT,iBAAc,KAAK,OAAO;AAC1B;;EAGF,MAAM,WAAW,aAAa,IAAI,KAAK;AACvC,MAAI,CAAC,UAAU;AACb,gBAAa,IAAI,MAAM,OAAO;AAC9B;;EAGF,MAAM,gBAAgB,MAAM,QAAQ,SAAS,SAAS,UAAU,GAC5D,SAAS,SAAS,UAAU,OAAO,GACnC,EAAE;EACN,MAAM,WAAW,MAAM,QAAQ,OAAO,SAAS,UAAU,GACrD,OAAO,SAAS,YAChB,EAAE;AAEN,OAAK,MAAM,KAAK,SACd,KAAI,CAAC,cAAc,SAAS,EAAE,CAC5B,eAAc,KAAK,EAAE;AAIzB,MAAI,cAAc,OAChB,UAAS,SAAS,YAAY;EAIhC,MAAM,gBAAgB,OAAO,SAAS;AAGtC,MAAI,iBAAiB,OAAO,kBAAkB,UAAU;AACtD,OAAI,CAAC,SAAS,SAAS,WACrB,UAAS,SAAS,aAAa,EAAE;GAEnC,MAAM,qBAAqB,SAAS,SAAS;AAI7C,QAAK,MAAM,CAAC,MAAM,YAAY,OAAO,QAAQ,cAAc,EAAE;AAC3D,QAAI,CAAC,mBAAmB,MACtB,oBAAmB,QAAQ,EAAE;AAE/B,uBAAmB,MAAM,KAAK,GAAG,QAAQ;;;;CAK/C,MAAM,gBAAgB,CAAC,GAAG,aAAa,QAAQ,EAAE,GAAG,cAAc;AAClE,SAAQ,OAAO,GAAG,QAAQ,QAAQ,GAAG,cAAc;;;;;;AAOrD,SAAgB,kBAAkB,SAAwB;CACxD,MAAM,6BAA6B,QAAQ,QACxC,KAAwC,WAA4B;AACnE,MAAI,OAAO,SAAS,UAAU;AAC5B,OAAI,CAAC,IAAI,OAAO,SAAS,UACvB,KAAI,OAAO,SAAS,YAAY,EAAE;AAEpC,OAAI,OAAO,SAAS,UAAU,KAAK,OAAO;;AAE5C,SAAO;IAET,EAAE,CACH;AAED,QAAO,OAAO,2BAA2B,CAAC,SAAS,kBAAkB;EAKnE,MAAM,iBAAiB,WAJR,cACZ,KAAK,WAAW,OAAO,SAAS,KAAK,CACrC,MAAM,CACN,KAAK,IACgC,CAAC;AACzC,gBAAc,SAAS,WAAW;AAChC,UAAO,SAAS,WAAW;IAC3B;GACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logger } from "../../console/logger.js";
|
|
2
|
-
import { hashStringSync } from "../../utils/hash.js";
|
|
2
|
+
import { hashStringSync, hashVersionId } from "../../utils/hash.js";
|
|
3
3
|
import { getRelative, readFile } from "../../fs/findFilepath.js";
|
|
4
4
|
import { SUPPORTED_FILE_EXTENSIONS } from "./supportedFiles.js";
|
|
5
5
|
import { getTransformFormatProperty } from "./transformFormat.js";
|
|
@@ -38,6 +38,7 @@ async function aggregateFiles(settings) {
|
|
|
38
38
|
publishMap: /* @__PURE__ */ new Map()
|
|
39
39
|
};
|
|
40
40
|
const { resolvedPaths: filePaths } = settings.files;
|
|
41
|
+
const requiresReviewPaths = settings.files.requiresReviewPaths ?? /* @__PURE__ */ new Set();
|
|
41
42
|
const skipValidation = settings.options?.skipFileValidation;
|
|
42
43
|
const publishMap = buildPublishMap(filePaths, settings);
|
|
43
44
|
if (filePaths.json) {
|
|
@@ -82,7 +83,7 @@ async function aggregateFiles(settings) {
|
|
|
82
83
|
}
|
|
83
84
|
return {
|
|
84
85
|
fileId: hashStringSync(relativePath),
|
|
85
|
-
versionId:
|
|
86
|
+
versionId: hashVersionId(parsedJson, requiresReviewPaths.has(filePath)),
|
|
86
87
|
content: parsedJson,
|
|
87
88
|
fileName: relativePath,
|
|
88
89
|
fileFormat: "JSON",
|
|
@@ -134,7 +135,7 @@ async function aggregateFiles(settings) {
|
|
|
134
135
|
fileFormat,
|
|
135
136
|
...getTransformFormatProperty(settings, "yaml"),
|
|
136
137
|
fileId: hashStringSync(relativePath),
|
|
137
|
-
versionId:
|
|
138
|
+
versionId: hashVersionId(parsedYaml, requiresReviewPaths.has(filePath)),
|
|
138
139
|
locale: settings.defaultLocale,
|
|
139
140
|
...keyedMetadata && { formatMetadata: { keyedMetadata } }
|
|
140
141
|
};
|
|
@@ -162,7 +163,7 @@ async function aggregateFiles(settings) {
|
|
|
162
163
|
const parsedJson = parseJson(content, filePath, settings.options || {}, settings.defaultLocale);
|
|
163
164
|
return {
|
|
164
165
|
fileId: hashStringSync(relativePath),
|
|
165
|
-
versionId:
|
|
166
|
+
versionId: hashVersionId(parsedJson, requiresReviewPaths.has(filePath)),
|
|
166
167
|
content: parsedJson,
|
|
167
168
|
fileName: relativePath,
|
|
168
169
|
fileFormat: "TWILIO_CONTENT_JSON",
|
|
@@ -199,7 +200,7 @@ async function aggregateFiles(settings) {
|
|
|
199
200
|
fileFormat: fileType.toUpperCase(),
|
|
200
201
|
...getTransformFormatProperty(settings, fileType),
|
|
201
202
|
fileId: hashStringSync(relativePath),
|
|
202
|
-
versionId:
|
|
203
|
+
versionId: hashVersionId(processed, requiresReviewPaths.has(filePath)),
|
|
203
204
|
locale: settings.defaultLocale
|
|
204
205
|
};
|
|
205
206
|
}).filter((file) => {
|