gt 2.21.5 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/dist/api/collectUserEditDiffs.js +1 -1
- package/dist/auth/callbackPage.d.ts +1 -0
- package/dist/auth/callbackPage.js +77 -0
- package/dist/auth/callbackPage.js.map +1 -0
- package/dist/auth/credentialStore.d.ts +15 -0
- package/dist/auth/credentialStore.js +107 -0
- package/dist/auth/credentialStore.js.map +1 -0
- package/dist/auth/errors.d.ts +7 -0
- package/dist/auth/errors.js +23 -0
- package/dist/auth/errors.js.map +1 -0
- package/dist/auth/loopback.d.ts +9 -0
- package/dist/auth/loopback.js +73 -0
- package/dist/auth/loopback.js.map +1 -0
- package/dist/auth/loopback.test.d.ts +1 -0
- package/dist/auth/oauth.d.ts +39 -0
- package/dist/auth/oauth.js +260 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/bin/bin-entry.js +2 -10
- package/dist/bin/bin-entry.js.map +1 -1
- package/dist/cli/base.d.ts +4 -8
- package/dist/cli/base.js +119 -60
- package/dist/cli/base.js.map +1 -1
- package/dist/cli/commands/api.js +6 -2
- package/dist/cli/commands/api.js.map +1 -1
- package/dist/cli/commands/upload.js +2 -2
- package/dist/cli/commands/upload.js.map +1 -1
- package/dist/cli/commands/utils/validation.d.ts +7 -7
- package/dist/cli/commands/utils/validation.js +7 -20
- package/dist/cli/commands/utils/validation.js.map +1 -1
- package/dist/config/generateSettings.js +4 -9
- package/dist/config/generateSettings.js.map +1 -1
- package/dist/config/optionPresets.js +16 -6
- package/dist/config/optionPresets.js.map +1 -1
- package/dist/config/validateSettings.d.ts +4 -2
- package/dist/config/validateSettings.js +9 -0
- package/dist/config/validateSettings.js.map +1 -1
- package/dist/console/index.d.ts +0 -2
- package/dist/console/index.js +2 -4
- package/dist/console/index.js.map +1 -1
- package/dist/console/inkPrompts.js +8 -6
- package/dist/console/inkPrompts.js.map +1 -1
- package/dist/console/inkTypes.d.ts +3 -0
- package/dist/console/logger.d.ts +13 -0
- package/dist/console/logger.js +60 -20
- package/dist/console/logger.js.map +1 -1
- package/dist/console/logging.d.ts +5 -3
- package/dist/console/logging.js +8 -6
- package/dist/console/logging.js.map +1 -1
- package/dist/console/promptParsing.d.ts +4 -3
- package/dist/console/promptParsing.js +7 -7
- package/dist/console/promptParsing.js.map +1 -1
- package/dist/formats/files/aggregateFiles.js +4 -4
- package/dist/formats/files/aggregateFiles.js.map +1 -1
- package/dist/formats/files/localeContent.js +2 -2
- package/dist/formats/files/localeContent.js.map +1 -1
- package/dist/formats/files/transformFormat.d.ts +2 -0
- package/dist/formats/files/transformFormat.js +8 -2
- package/dist/formats/files/transformFormat.js.map +1 -1
- package/dist/formats/json/extractJson.js +3 -3
- package/dist/formats/json/extractJson.js.map +1 -1
- package/dist/formats/json/mergeJson.js +6 -6
- package/dist/formats/json/mergeJson.js.map +1 -1
- package/dist/formats/xcstrings/mergeXcstrings.js +2 -2
- package/dist/formats/xcstrings/mergeXcstrings.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/git/setupMergeDrivers.js +1 -1
- package/dist/git/setupMergeDrivers.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/main.js +2 -10
- package/dist/main.js.map +1 -1
- package/dist/react/jsx/utils/jsxParsing/addGTIdentifierToSyntaxTree.js +1 -1
- package/dist/react/jsx/utils/jsxParsing/parseJsx.js +1 -1
- package/dist/react/jsx/utils/parseString.js +1 -1
- package/dist/react/jsx/utils/stringParsing/derivation/index.js +1 -1
- package/dist/setup/developmentCredentials.d.ts +8 -0
- package/dist/setup/developmentCredentials.js +82 -0
- package/dist/setup/developmentCredentials.js.map +1 -0
- package/dist/setup/setupViteSPA.js +1 -1
- package/dist/setup/setupViteSPA.js.map +1 -1
- package/dist/setup/userInput.d.ts +1 -0
- package/dist/setup/userInput.js +7 -2
- package/dist/setup/userInput.js.map +1 -1
- package/dist/translation/extractInline.js +1 -1
- package/dist/translation/extractInline.js.map +1 -1
- package/dist/types/index.d.ts +4 -1
- package/dist/utils/api.d.ts +146 -67
- package/dist/utils/api.js +5 -94
- package/dist/utils/api.js.map +1 -1
- package/dist/utils/credentials.d.ts +37 -10
- package/dist/utils/credentials.js +267 -58
- package/dist/utils/credentials.js.map +1 -1
- package/dist/utils/loadEnv.d.ts +2 -0
- package/dist/utils/loadEnv.js +24 -0
- package/dist/utils/loadEnv.js.map +1 -0
- package/dist/utils/reactPackageCompatibility.js +1 -1
- package/dist/utils/reactPackageCompatibility.js.map +1 -1
- package/dist/workflows/steps/PollJobsStep.js +40 -53
- package/dist/workflows/steps/PollJobsStep.js.map +1 -1
- package/dist/workflows/utils/syncFonts.js +2 -2
- package/dist/workflows/utils/syncFonts.js.map +1 -1
- package/package.json +8 -7
- package/dist/next/config/parseNextConfig.d.ts +0 -10
- package/dist/next/config/parseNextConfig.js +0 -34
- package/dist/next/config/parseNextConfig.js.map +0 -1
- package/dist/utils/fetch.d.ts +0 -11
- package/dist/utils/fetch.js +0 -24
- package/dist/utils/fetch.js.map +0 -1
- package/dist/utils/gt.d.ts +0 -2
- package/dist/utils/gt.js +0 -7
- package/dist/utils/gt.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mergeJson.js","names":[],"sources":["../../../src/formats/json/mergeJson.ts"],"sourcesContent":["import { AdditionalOptions, SourceObjectOptions } from '../../types/index.js';\nimport { exitSync } from '../../console/logging.js';\nimport { logger } from '../../console/logger.js';\nimport {\n findMatchingItemArray,\n findMatchingItemObject,\n generateSourceObjectPointers,\n getIdentifyingLocaleProperty,\n getSourceObjectOptionsArray,\n validateJsonSchema,\n} from './utils.js';\nimport {\n getConfiguredLocaleProperties,\n replaceLocalePlaceholders,\n} from '../utils.js';\nimport { gt } from '../../utils/gt.js';\nimport {\n applyStructuralTransforms,\n unapplyStructuralTransforms,\n} from './transformJson.js';\nimport type { JSONObject, JSONValue } from '../../types/data/json.js';\nimport { getJSONPathMatches, getJSONPathValues } from './jsonPath.js';\nimport { getJSONPointerValue, setJSONPointerValue } from './jsonPointer.js';\n\ntype ParsedTarget = {\n translatedContent: string;\n targetLocale: string;\n parsedContent: JSONObject;\n};\n\nexport function mergeJson(\n originalContent: string,\n inputPath: string,\n options: AdditionalOptions,\n targets: {\n translatedContent: string;\n targetLocale: string;\n }[],\n defaultLocale: string,\n localeOrder: string[] = []\n): string[] {\n const jsonSchema = validateJsonSchema(options, inputPath);\n if (!jsonSchema) {\n return targets.map((target) => target.translatedContent);\n }\n\n let originalJson: JSONValue;\n try {\n originalJson = JSON.parse(originalContent);\n } catch {\n logger.error(`Invalid JSON file: ${inputPath}`);\n return exitSync(1);\n }\n\n const useCanonicalLocaleKeys =\n options?.experimentalCanonicalLocaleKeys ?? false;\n const canonicalDefaultLocale = useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(defaultLocale)\n : defaultLocale;\n const canonicalLocaleOrder = useCanonicalLocaleKeys\n ? localeOrder.map((locale) => gt.resolveCanonicalLocale(locale))\n : localeOrder;\n\n if (jsonSchema.structuralTransform && jsonSchema.composite) {\n applyStructuralTransforms(\n originalJson,\n jsonSchema.structuralTransform,\n jsonSchema.composite\n );\n }\n\n // Handle include\n if (jsonSchema.include) {\n const output: string[] = [];\n for (const target of targets) {\n // Must clone the original JSON to avoid mutations\n const mergedJson = structuredClone(originalJson);\n const translatedJson = JSON.parse(target.translatedContent) as JSONObject;\n for (const [jsonPointer, translatedValue] of Object.entries(\n translatedJson\n )) {\n try {\n const value = getJSONPointerValue(mergedJson, jsonPointer);\n if (!value) continue;\n setJSONPointerValue(mergedJson, jsonPointer, translatedValue);\n } catch {\n /* empty */\n }\n }\n output.push(JSON.stringify(mergedJson, null, 2));\n }\n return output;\n }\n\n if (!jsonSchema.composite) {\n logger.error('No composite property found in JSON schema');\n return exitSync(1);\n }\n\n // Handle composite\n // Create a deep copy of the original JSON to avoid mutations\n const mergedJson = structuredClone(originalJson);\n\n // Pre-parse all target contents ONCE (avoid re-parsing per pointer)\n const parsedTargets = targets.map((target) => ({\n ...target,\n parsedContent: JSON.parse(target.translatedContent) as JSONObject,\n })) satisfies ParsedTarget[];\n\n // Create mapping of sourceObjectPointer to SourceObjectOptions\n const sourceObjectPointers = generateSourceObjectPointers(\n jsonSchema.composite,\n originalJson\n );\n\n // Find the source object\n for (const [\n sourceObjectPointer,\n { sourceObjectValue, sourceObjectOptions },\n ] of Object.entries(sourceObjectPointers)) {\n // Find the source item\n if (sourceObjectOptions.type === 'array') {\n // Validate type\n if (!Array.isArray(sourceObjectValue)) {\n logger.error(\n `Source object value is not an array at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n\n // Get source item for default locale\n const matchingDefaultLocaleItems = findMatchingItemArray(\n canonicalDefaultLocale,\n sourceObjectOptions,\n sourceObjectPointer,\n sourceObjectValue\n );\n if (!Object.keys(matchingDefaultLocaleItems).length) {\n logger.warn(\n `Matching sourceItems not found at path: ${sourceObjectPointer}. Check that your JSON file includes the key field. Skipping this target`\n );\n continue;\n }\n\n const matchingDefaultLocaleItemKeys = new Set(\n Object.keys(matchingDefaultLocaleItems)\n );\n\n // For each target:\n // 1. Get the target items\n // 2. Track all array indecies to remove (will be overwritten)\n // 3. Merge matchingDefaultLocaleItems and targetItems\n // 4. Validate that the mergedItems is not empty\n // For each target item:\n // 5. Validate that all the array indecies are still present in the source json\n // 6. Override the source item with the translated values\n // 7. Apply additional mutations to the sourceItem\n // 8. Track all items to add\n // 9. Check that items to add is >= items to remove\n // 10. Remove all items for the target locale (they can be identified by the key)\n const indiciesToRemove = new Set<number>();\n const itemsToAdd: JSONValue[] = [];\n for (const target of parsedTargets) {\n let targetItems = target.parsedContent[sourceObjectPointer];\n // 1. Get the target items\n if (!targetItems) {\n // If no translation can be found, a transformation may need to happen still\n targetItems = {};\n }\n\n // 2. Track all array indecies to remove (will be overwritten)\n const targetItemsToRemove = findMatchingItemArray(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectOptions,\n sourceObjectPointer,\n sourceObjectValue\n );\n Object.values(targetItemsToRemove).forEach(({ index }) =>\n indiciesToRemove.add(index)\n );\n\n // Remap mismatched positional keys to current source positions\n const sourceKeys = [...matchingDefaultLocaleItemKeys];\n const remappedTargetItems: Record<string, JSONValue> = {};\n for (const [key, value] of Object.entries(targetItems as JSONObject)) {\n if (matchingDefaultLocaleItemKeys.has(key)) {\n remappedTargetItems[key] = value;\n } else if (\n sourceKeys.length === 1 &&\n !(sourceKeys[0] in remappedTargetItems)\n ) {\n remappedTargetItems[sourceKeys[0]] = value;\n } else {\n logger.warn(\n `Skipping translated item at ${key}: cannot map to source item at path ${sourceObjectPointer}`\n );\n }\n }\n\n // Merge matchingDefaultLocaleItems and remapped targetItems\n const mergedItems = {\n ...(sourceObjectOptions.transform ? matchingDefaultLocaleItems : {}),\n ...remappedTargetItems,\n };\n // 4. Validate that the mergedItems is not empty\n if (Object.keys(mergedItems).length === 0) {\n logger.warn(\n `Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`\n );\n continue;\n }\n\n for (const [sourceItemPointer, targetItem] of Object.entries(\n mergedItems\n )) {\n // 5. Validate that all the array indecies are still present in the source json\n if (!matchingDefaultLocaleItemKeys.has(sourceItemPointer)) {\n logger.warn(\n `Skipping translated item at ${sourceItemPointer}: not present in source json at path ${sourceObjectPointer}`\n );\n continue;\n }\n\n // 6. Override the source item with the translated values\n const defaultLocaleSourceItem =\n matchingDefaultLocaleItems[sourceItemPointer].sourceItem;\n const defaultLocaleKeyPointer =\n matchingDefaultLocaleItems[sourceItemPointer].keyPointer;\n const mutatedSourceItem = structuredClone(defaultLocaleSourceItem);\n const { identifyingLocaleProperty: targetLocaleKeyProperty } =\n getSourceObjectOptionsArray(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n setJSONPointerValue(\n mutatedSourceItem,\n defaultLocaleKeyPointer,\n targetLocaleKeyProperty\n );\n omitProperties(mutatedSourceItem, sourceObjectOptions.omitProperties);\n for (const [\n translatedKeyJsonPointer,\n translatedValue,\n ] of Object.entries((targetItem || {}) as JSONObject)) {\n const valueToSet =\n useCanonicalLocaleKeys &&\n defaultLocaleKeyPointer &&\n translatedKeyJsonPointer === defaultLocaleKeyPointer\n ? targetLocaleKeyProperty\n : translatedValue;\n try {\n const value = getJSONPointerValue(\n mutatedSourceItem,\n translatedKeyJsonPointer\n );\n if (!value) continue;\n setJSONPointerValue(\n mutatedSourceItem,\n translatedKeyJsonPointer,\n valueToSet\n );\n } catch {\n /* empty */\n }\n }\n\n // 7. Apply additional mutations to the sourceItem\n applyTransformations(\n mutatedSourceItem,\n sourceObjectOptions.transform,\n target.targetLocale,\n defaultLocale\n );\n\n itemsToAdd.push(mutatedSourceItem);\n }\n }\n\n // 8. Check that items to add is >= items to remove\n if (itemsToAdd.length < indiciesToRemove.size) {\n logger.warn(\n `Items to add (${itemsToAdd.length}) is less than items to remove (${indiciesToRemove.size}) at path: ${sourceObjectPointer}. Some translated items may have been skipped.`\n );\n }\n\n // 9. Remove all items for the target locale (they can be identified by the key)\n const filteredSourceObjectValue = sourceObjectValue.filter(\n (_, index: number) => !indiciesToRemove.has(index)\n );\n\n // 10. Add all items to the original JSON\n filteredSourceObjectValue.push(...itemsToAdd);\n\n setJSONPointerValue(\n mergedJson,\n sourceObjectPointer,\n sortByLocaleOrder(\n filteredSourceObjectValue,\n sourceObjectOptions,\n canonicalLocaleOrder,\n sourceObjectPointer,\n canonicalDefaultLocale\n )\n );\n } else {\n // Validate type\n if (typeof sourceObjectValue !== 'object' || sourceObjectValue === null) {\n logger.error(\n `Source object value is not an object at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n const sourceObjectRecord = sourceObjectValue as JSONObject;\n // Validate localeProperty\n const matchingDefaultLocaleItem = findMatchingItemObject(\n canonicalDefaultLocale,\n sourceObjectPointer,\n sourceObjectOptions,\n sourceObjectRecord\n );\n // Validate source item exists\n if (!matchingDefaultLocaleItem.sourceItem) {\n logger.error(\n `Source item not found at path: ${sourceObjectPointer}. You must specify a source item where its key matches the default locale`\n );\n return exitSync(1);\n }\n const { sourceItem: defaultLocaleSourceItem } = matchingDefaultLocaleItem;\n\n // For each target:\n // 1. Get the target items\n // 2. Find the source item for the target locale\n // 3. Merge the target items with the source item\n // 4. Validate that the mergedItems is not empty\n // 5. Override the source item with the translated values\n // 6. Apply additional mutations to the sourceItem\n // 7. Merge the source item with the original JSON (if the source item is not a new item)\n for (const target of parsedTargets) {\n // 1. Get the target items\n let targetItems = target.parsedContent[sourceObjectPointer];\n if (targetItems == null) {\n targetItems = {};\n }\n\n // 2. Find the source item for the target locale\n const matchingTargetItem = findMatchingItemObject(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectPointer,\n sourceObjectOptions,\n sourceObjectRecord\n );\n const mutateSourceItemKey = matchingTargetItem.keyParentProperty;\n\n // If the source item is a string, use the translated string directly\n if (typeof defaultLocaleSourceItem === 'string') {\n if (typeof targetItems === 'string') {\n sourceObjectRecord[mutateSourceItemKey] = targetItems;\n }\n // If no translation found, leave the locale slot unchanged\n continue;\n }\n\n // If the target locale has a matching source item, use it to mutate the source item\n // Otherwise, fallback to the default locale source item\n const mutateSourceItem = structuredClone(defaultLocaleSourceItem);\n omitProperties(mutateSourceItem, sourceObjectOptions.omitProperties);\n\n // 3. Merge the target items with the source item (if there are transformations to perform)\n const mergedItems: Record<string, JSONValue> = {\n ...(sourceObjectOptions.transform\n ? (defaultLocaleSourceItem as JSONObject)\n : {}),\n ...(targetItems as JSONObject),\n };\n\n // 4. Validate that the mergedItems is not empty\n if (Object.keys(mergedItems).length === 0) {\n logger.warn(\n `Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`\n );\n continue;\n }\n\n // 5. Override the source item with the translated values\n for (const [\n translatedKeyJsonPointer,\n translatedValue,\n ] of Object.entries(mergedItems || {})) {\n try {\n const value = getJSONPointerValue(\n mutateSourceItem,\n translatedKeyJsonPointer\n );\n if (!value) continue;\n setJSONPointerValue(\n mutateSourceItem,\n translatedKeyJsonPointer,\n translatedValue\n );\n } catch {\n /* empty */\n }\n }\n // 6. Apply additional mutations to the sourceItem\n applyTransformations(\n mutateSourceItem,\n sourceObjectOptions.transform,\n target.targetLocale,\n defaultLocale\n );\n\n // 7. Merge the source item with the original JSON\n sourceObjectRecord[mutateSourceItemKey] = mutateSourceItem;\n }\n setJSONPointerValue(mergedJson, sourceObjectPointer, sourceObjectValue);\n }\n }\n if (jsonSchema.structuralTransform && jsonSchema.composite) {\n unapplyStructuralTransforms(\n mergedJson,\n jsonSchema.structuralTransform,\n jsonSchema.composite\n );\n }\n\n return [JSON.stringify(mergedJson, null, 2)];\n}\n\nfunction sortByLocaleOrder(\n items: JSONValue[],\n sourceObjectOptions: SourceObjectOptions,\n localeOrder: string[],\n sourceObjectPointer: string,\n defaultLocale: string\n): JSONValue[] {\n const sortMode = sourceObjectOptions.experimentalSort;\n if (!sortMode || !sourceObjectOptions.key) {\n return items;\n }\n\n const itemsWithLocale = items.map((item) => {\n let localeValue: string | undefined;\n try {\n const values = getJSONPathValues(item, sourceObjectOptions.key as string);\n const value = values?.[0];\n if (typeof value === 'string') {\n localeValue = value;\n }\n } catch {\n /* empty */\n }\n return { item, localeValue };\n });\n\n if (sortMode === 'locales') {\n if (!localeOrder.length) {\n return items;\n }\n\n const orderedLocaleList = [\n defaultLocale,\n ...localeOrder.filter((locale) => locale !== defaultLocale),\n ];\n const localeOrderValues = orderedLocaleList.map((locale) =>\n getIdentifyingLocaleProperty(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n )\n );\n\n const orderedItems: JSONValue[] = [];\n const remainingItems = [...itemsWithLocale];\n\n for (const localeValue of localeOrderValues) {\n for (let i = 0; i < remainingItems.length; ) {\n const entry = remainingItems[i];\n if (entry.localeValue === localeValue) {\n orderedItems.push(entry.item);\n remainingItems.splice(i, 1);\n continue;\n }\n i += 1;\n }\n }\n\n remainingItems.forEach((entry) => orderedItems.push(entry.item));\n\n return orderedItems;\n }\n\n if (sortMode === 'localesAlphabetical') {\n const defaultLocaleValue = getIdentifyingLocaleProperty(\n defaultLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n\n const defaultItems: typeof itemsWithLocale = [];\n const sortableItems: typeof itemsWithLocale = [];\n const remainingItems: typeof itemsWithLocale = [];\n\n for (const entry of itemsWithLocale) {\n if (entry.localeValue === defaultLocaleValue) {\n defaultItems.push(entry);\n continue;\n }\n if (entry.localeValue) {\n sortableItems.push(entry);\n continue;\n }\n remainingItems.push(entry);\n }\n\n sortableItems.sort((a, b) => {\n if (!a.localeValue || !b.localeValue) {\n return 0;\n }\n return a.localeValue.localeCompare(b.localeValue);\n });\n\n return [...defaultItems, ...sortableItems, ...remainingItems].map(\n (entry) => entry.item\n );\n }\n\n return items;\n}\n\n/**\n * Remove top-level properties from a generated non-default-locale entry\n * (e.g. Mintlify's `default: true` flag, which is only valid on one entry)\n */\nfunction omitProperties(\n item: JSONValue,\n properties: string[] | undefined\n): void {\n if (!properties?.length) return;\n if (!item || typeof item !== 'object' || Array.isArray(item)) return;\n for (const property of properties) {\n delete (item as JSONObject)[property];\n }\n}\n\n/**\n * Apply transformations to the sourceItem in-place\n * @param sourceItem - The source item to apply transformations to\n * @param transform - The transformations to apply\n * @param targetLocale - The target locale\n * @param defaultLocale - The default locale\n */\nexport function applyTransformations(\n sourceItem: JSONValue,\n transform: SourceObjectOptions['transform'],\n targetLocale: string,\n defaultLocale: string\n): void {\n if (!transform) return;\n\n const targetLocaleProperties = getConfiguredLocaleProperties(targetLocale);\n const defaultLocaleProperties = getConfiguredLocaleProperties(defaultLocale);\n\n for (const [transformPath, transformOptions] of Object.entries(transform)) {\n if (\n !transformOptions.replace ||\n typeof transformOptions.replace !== 'string'\n ) {\n continue;\n }\n const results = getJSONPathMatches(sourceItem, transformPath);\n if (!results || results.length === 0) {\n continue;\n }\n results.forEach((result) => {\n if (typeof result.value !== 'string') {\n return;\n }\n // Replace locale placeholders in the replace string\n let replaceString = transformOptions.replace;\n\n // Replace all locale property placeholders\n replaceString = replaceLocalePlaceholders(\n replaceString,\n targetLocaleProperties\n );\n\n if (\n transformOptions.match &&\n typeof transformOptions.match === 'string'\n ) {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transformOptions.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n result.value = result.value.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n } else {\n result.value = replaceString;\n }\n\n // Update the actual sourceItem using JSONPointer\n setJSONPointerValue(sourceItem, result.pointer, result.value);\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mergeJson.js","names":[],"sources":["../../../src/formats/json/mergeJson.ts"],"sourcesContent":["import { AdditionalOptions, SourceObjectOptions } from '../../types/index.js';\nimport { exitSync } from '../../console/logging.js';\nimport { logger } from '../../console/logger.js';\nimport {\n findMatchingItemArray,\n findMatchingItemObject,\n generateSourceObjectPointers,\n getIdentifyingLocaleProperty,\n getSourceObjectOptionsArray,\n validateJsonSchema,\n} from './utils.js';\nimport {\n getConfiguredLocaleProperties,\n replaceLocalePlaceholders,\n} from '../utils.js';\nimport { api } from '../../utils/api.js';\nimport {\n applyStructuralTransforms,\n unapplyStructuralTransforms,\n} from './transformJson.js';\nimport type { JSONObject, JSONValue } from '../../types/data/json.js';\nimport { getJSONPathMatches, getJSONPathValues } from './jsonPath.js';\nimport { getJSONPointerValue, setJSONPointerValue } from './jsonPointer.js';\n\ntype ParsedTarget = {\n translatedContent: string;\n targetLocale: string;\n parsedContent: JSONObject;\n};\n\nexport function mergeJson(\n originalContent: string,\n inputPath: string,\n options: AdditionalOptions,\n targets: {\n translatedContent: string;\n targetLocale: string;\n }[],\n defaultLocale: string,\n localeOrder: string[] = []\n): string[] {\n const jsonSchema = validateJsonSchema(options, inputPath);\n if (!jsonSchema) {\n return targets.map((target) => target.translatedContent);\n }\n\n let originalJson: JSONValue;\n try {\n originalJson = JSON.parse(originalContent);\n } catch {\n logger.error(`Invalid JSON file: ${inputPath}`);\n return exitSync(1);\n }\n\n const useCanonicalLocaleKeys =\n options?.experimentalCanonicalLocaleKeys ?? false;\n const canonicalDefaultLocale = useCanonicalLocaleKeys\n ? api.resolveCanonicalLocale(defaultLocale)\n : defaultLocale;\n const canonicalLocaleOrder = useCanonicalLocaleKeys\n ? localeOrder.map((locale) => api.resolveCanonicalLocale(locale))\n : localeOrder;\n\n if (jsonSchema.structuralTransform && jsonSchema.composite) {\n applyStructuralTransforms(\n originalJson,\n jsonSchema.structuralTransform,\n jsonSchema.composite\n );\n }\n\n // Handle include\n if (jsonSchema.include) {\n const output: string[] = [];\n for (const target of targets) {\n // Must clone the original JSON to avoid mutations\n const mergedJson = structuredClone(originalJson);\n const translatedJson = JSON.parse(target.translatedContent) as JSONObject;\n for (const [jsonPointer, translatedValue] of Object.entries(\n translatedJson\n )) {\n try {\n const value = getJSONPointerValue(mergedJson, jsonPointer);\n if (!value) continue;\n setJSONPointerValue(mergedJson, jsonPointer, translatedValue);\n } catch {\n /* empty */\n }\n }\n output.push(JSON.stringify(mergedJson, null, 2));\n }\n return output;\n }\n\n if (!jsonSchema.composite) {\n logger.error('No composite property found in JSON schema');\n return exitSync(1);\n }\n\n // Handle composite\n // Create a deep copy of the original JSON to avoid mutations\n const mergedJson = structuredClone(originalJson);\n\n // Pre-parse all target contents ONCE (avoid re-parsing per pointer)\n const parsedTargets = targets.map((target) => ({\n ...target,\n parsedContent: JSON.parse(target.translatedContent) as JSONObject,\n })) satisfies ParsedTarget[];\n\n // Create mapping of sourceObjectPointer to SourceObjectOptions\n const sourceObjectPointers = generateSourceObjectPointers(\n jsonSchema.composite,\n originalJson\n );\n\n // Find the source object\n for (const [\n sourceObjectPointer,\n { sourceObjectValue, sourceObjectOptions },\n ] of Object.entries(sourceObjectPointers)) {\n // Find the source item\n if (sourceObjectOptions.type === 'array') {\n // Validate type\n if (!Array.isArray(sourceObjectValue)) {\n logger.error(\n `Source object value is not an array at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n\n // Get source item for default locale\n const matchingDefaultLocaleItems = findMatchingItemArray(\n canonicalDefaultLocale,\n sourceObjectOptions,\n sourceObjectPointer,\n sourceObjectValue\n );\n if (!Object.keys(matchingDefaultLocaleItems).length) {\n logger.warn(\n `Matching sourceItems not found at path: ${sourceObjectPointer}. Check that your JSON file includes the key field. Skipping this target`\n );\n continue;\n }\n\n const matchingDefaultLocaleItemKeys = new Set(\n Object.keys(matchingDefaultLocaleItems)\n );\n\n // For each target:\n // 1. Get the target items\n // 2. Track all array indecies to remove (will be overwritten)\n // 3. Merge matchingDefaultLocaleItems and targetItems\n // 4. Validate that the mergedItems is not empty\n // For each target item:\n // 5. Validate that all the array indecies are still present in the source json\n // 6. Override the source item with the translated values\n // 7. Apply additional mutations to the sourceItem\n // 8. Track all items to add\n // 9. Check that items to add is >= items to remove\n // 10. Remove all items for the target locale (they can be identified by the key)\n const indiciesToRemove = new Set<number>();\n const itemsToAdd: JSONValue[] = [];\n for (const target of parsedTargets) {\n let targetItems = target.parsedContent[sourceObjectPointer];\n // 1. Get the target items\n if (!targetItems) {\n // If no translation can be found, a transformation may need to happen still\n targetItems = {};\n }\n\n // 2. Track all array indecies to remove (will be overwritten)\n const targetItemsToRemove = findMatchingItemArray(\n useCanonicalLocaleKeys\n ? api.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectOptions,\n sourceObjectPointer,\n sourceObjectValue\n );\n Object.values(targetItemsToRemove).forEach(({ index }) =>\n indiciesToRemove.add(index)\n );\n\n // Remap mismatched positional keys to current source positions\n const sourceKeys = [...matchingDefaultLocaleItemKeys];\n const remappedTargetItems: Record<string, JSONValue> = {};\n for (const [key, value] of Object.entries(targetItems as JSONObject)) {\n if (matchingDefaultLocaleItemKeys.has(key)) {\n remappedTargetItems[key] = value;\n } else if (\n sourceKeys.length === 1 &&\n !(sourceKeys[0] in remappedTargetItems)\n ) {\n remappedTargetItems[sourceKeys[0]] = value;\n } else {\n logger.warn(\n `Skipping translated item at ${key}: cannot map to source item at path ${sourceObjectPointer}`\n );\n }\n }\n\n // Merge matchingDefaultLocaleItems and remapped targetItems\n const mergedItems = {\n ...(sourceObjectOptions.transform ? matchingDefaultLocaleItems : {}),\n ...remappedTargetItems,\n };\n // 4. Validate that the mergedItems is not empty\n if (Object.keys(mergedItems).length === 0) {\n logger.warn(\n `Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`\n );\n continue;\n }\n\n for (const [sourceItemPointer, targetItem] of Object.entries(\n mergedItems\n )) {\n // 5. Validate that all the array indecies are still present in the source json\n if (!matchingDefaultLocaleItemKeys.has(sourceItemPointer)) {\n logger.warn(\n `Skipping translated item at ${sourceItemPointer}: not present in source json at path ${sourceObjectPointer}`\n );\n continue;\n }\n\n // 6. Override the source item with the translated values\n const defaultLocaleSourceItem =\n matchingDefaultLocaleItems[sourceItemPointer].sourceItem;\n const defaultLocaleKeyPointer =\n matchingDefaultLocaleItems[sourceItemPointer].keyPointer;\n const mutatedSourceItem = structuredClone(defaultLocaleSourceItem);\n const { identifyingLocaleProperty: targetLocaleKeyProperty } =\n getSourceObjectOptionsArray(\n useCanonicalLocaleKeys\n ? api.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n setJSONPointerValue(\n mutatedSourceItem,\n defaultLocaleKeyPointer,\n targetLocaleKeyProperty\n );\n omitProperties(mutatedSourceItem, sourceObjectOptions.omitProperties);\n for (const [\n translatedKeyJsonPointer,\n translatedValue,\n ] of Object.entries((targetItem || {}) as JSONObject)) {\n const valueToSet =\n useCanonicalLocaleKeys &&\n defaultLocaleKeyPointer &&\n translatedKeyJsonPointer === defaultLocaleKeyPointer\n ? targetLocaleKeyProperty\n : translatedValue;\n try {\n const value = getJSONPointerValue(\n mutatedSourceItem,\n translatedKeyJsonPointer\n );\n if (!value) continue;\n setJSONPointerValue(\n mutatedSourceItem,\n translatedKeyJsonPointer,\n valueToSet\n );\n } catch {\n /* empty */\n }\n }\n\n // 7. Apply additional mutations to the sourceItem\n applyTransformations(\n mutatedSourceItem,\n sourceObjectOptions.transform,\n target.targetLocale,\n defaultLocale\n );\n\n itemsToAdd.push(mutatedSourceItem);\n }\n }\n\n // 8. Check that items to add is >= items to remove\n if (itemsToAdd.length < indiciesToRemove.size) {\n logger.warn(\n `Items to add (${itemsToAdd.length}) is less than items to remove (${indiciesToRemove.size}) at path: ${sourceObjectPointer}. Some translated items may have been skipped.`\n );\n }\n\n // 9. Remove all items for the target locale (they can be identified by the key)\n const filteredSourceObjectValue = sourceObjectValue.filter(\n (_, index: number) => !indiciesToRemove.has(index)\n );\n\n // 10. Add all items to the original JSON\n filteredSourceObjectValue.push(...itemsToAdd);\n\n setJSONPointerValue(\n mergedJson,\n sourceObjectPointer,\n sortByLocaleOrder(\n filteredSourceObjectValue,\n sourceObjectOptions,\n canonicalLocaleOrder,\n sourceObjectPointer,\n canonicalDefaultLocale\n )\n );\n } else {\n // Validate type\n if (typeof sourceObjectValue !== 'object' || sourceObjectValue === null) {\n logger.error(\n `Source object value is not an object at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n const sourceObjectRecord = sourceObjectValue as JSONObject;\n // Validate localeProperty\n const matchingDefaultLocaleItem = findMatchingItemObject(\n canonicalDefaultLocale,\n sourceObjectPointer,\n sourceObjectOptions,\n sourceObjectRecord\n );\n // Validate source item exists\n if (!matchingDefaultLocaleItem.sourceItem) {\n logger.error(\n `Source item not found at path: ${sourceObjectPointer}. You must specify a source item where its key matches the default locale`\n );\n return exitSync(1);\n }\n const { sourceItem: defaultLocaleSourceItem } = matchingDefaultLocaleItem;\n\n // For each target:\n // 1. Get the target items\n // 2. Find the source item for the target locale\n // 3. Merge the target items with the source item\n // 4. Validate that the mergedItems is not empty\n // 5. Override the source item with the translated values\n // 6. Apply additional mutations to the sourceItem\n // 7. Merge the source item with the original JSON (if the source item is not a new item)\n for (const target of parsedTargets) {\n // 1. Get the target items\n let targetItems = target.parsedContent[sourceObjectPointer];\n if (targetItems == null) {\n targetItems = {};\n }\n\n // 2. Find the source item for the target locale\n const matchingTargetItem = findMatchingItemObject(\n useCanonicalLocaleKeys\n ? api.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectPointer,\n sourceObjectOptions,\n sourceObjectRecord\n );\n const mutateSourceItemKey = matchingTargetItem.keyParentProperty;\n\n // If the source item is a string, use the translated string directly\n if (typeof defaultLocaleSourceItem === 'string') {\n if (typeof targetItems === 'string') {\n sourceObjectRecord[mutateSourceItemKey] = targetItems;\n }\n // If no translation found, leave the locale slot unchanged\n continue;\n }\n\n // If the target locale has a matching source item, use it to mutate the source item\n // Otherwise, fallback to the default locale source item\n const mutateSourceItem = structuredClone(defaultLocaleSourceItem);\n omitProperties(mutateSourceItem, sourceObjectOptions.omitProperties);\n\n // 3. Merge the target items with the source item (if there are transformations to perform)\n const mergedItems: Record<string, JSONValue> = {\n ...(sourceObjectOptions.transform\n ? (defaultLocaleSourceItem as JSONObject)\n : {}),\n ...(targetItems as JSONObject),\n };\n\n // 4. Validate that the mergedItems is not empty\n if (Object.keys(mergedItems).length === 0) {\n logger.warn(\n `Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`\n );\n continue;\n }\n\n // 5. Override the source item with the translated values\n for (const [\n translatedKeyJsonPointer,\n translatedValue,\n ] of Object.entries(mergedItems || {})) {\n try {\n const value = getJSONPointerValue(\n mutateSourceItem,\n translatedKeyJsonPointer\n );\n if (!value) continue;\n setJSONPointerValue(\n mutateSourceItem,\n translatedKeyJsonPointer,\n translatedValue\n );\n } catch {\n /* empty */\n }\n }\n // 6. Apply additional mutations to the sourceItem\n applyTransformations(\n mutateSourceItem,\n sourceObjectOptions.transform,\n target.targetLocale,\n defaultLocale\n );\n\n // 7. Merge the source item with the original JSON\n sourceObjectRecord[mutateSourceItemKey] = mutateSourceItem;\n }\n setJSONPointerValue(mergedJson, sourceObjectPointer, sourceObjectValue);\n }\n }\n if (jsonSchema.structuralTransform && jsonSchema.composite) {\n unapplyStructuralTransforms(\n mergedJson,\n jsonSchema.structuralTransform,\n jsonSchema.composite\n );\n }\n\n return [JSON.stringify(mergedJson, null, 2)];\n}\n\nfunction sortByLocaleOrder(\n items: JSONValue[],\n sourceObjectOptions: SourceObjectOptions,\n localeOrder: string[],\n sourceObjectPointer: string,\n defaultLocale: string\n): JSONValue[] {\n const sortMode = sourceObjectOptions.experimentalSort;\n if (!sortMode || !sourceObjectOptions.key) {\n return items;\n }\n\n const itemsWithLocale = items.map((item) => {\n let localeValue: string | undefined;\n try {\n const values = getJSONPathValues(item, sourceObjectOptions.key as string);\n const value = values?.[0];\n if (typeof value === 'string') {\n localeValue = value;\n }\n } catch {\n /* empty */\n }\n return { item, localeValue };\n });\n\n if (sortMode === 'locales') {\n if (!localeOrder.length) {\n return items;\n }\n\n const orderedLocaleList = [\n defaultLocale,\n ...localeOrder.filter((locale) => locale !== defaultLocale),\n ];\n const localeOrderValues = orderedLocaleList.map((locale) =>\n getIdentifyingLocaleProperty(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n )\n );\n\n const orderedItems: JSONValue[] = [];\n const remainingItems = [...itemsWithLocale];\n\n for (const localeValue of localeOrderValues) {\n for (let i = 0; i < remainingItems.length; ) {\n const entry = remainingItems[i];\n if (entry.localeValue === localeValue) {\n orderedItems.push(entry.item);\n remainingItems.splice(i, 1);\n continue;\n }\n i += 1;\n }\n }\n\n remainingItems.forEach((entry) => orderedItems.push(entry.item));\n\n return orderedItems;\n }\n\n if (sortMode === 'localesAlphabetical') {\n const defaultLocaleValue = getIdentifyingLocaleProperty(\n defaultLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n\n const defaultItems: typeof itemsWithLocale = [];\n const sortableItems: typeof itemsWithLocale = [];\n const remainingItems: typeof itemsWithLocale = [];\n\n for (const entry of itemsWithLocale) {\n if (entry.localeValue === defaultLocaleValue) {\n defaultItems.push(entry);\n continue;\n }\n if (entry.localeValue) {\n sortableItems.push(entry);\n continue;\n }\n remainingItems.push(entry);\n }\n\n sortableItems.sort((a, b) => {\n if (!a.localeValue || !b.localeValue) {\n return 0;\n }\n return a.localeValue.localeCompare(b.localeValue);\n });\n\n return [...defaultItems, ...sortableItems, ...remainingItems].map(\n (entry) => entry.item\n );\n }\n\n return items;\n}\n\n/**\n * Remove top-level properties from a generated non-default-locale entry\n * (e.g. Mintlify's `default: true` flag, which is only valid on one entry)\n */\nfunction omitProperties(\n item: JSONValue,\n properties: string[] | undefined\n): void {\n if (!properties?.length) return;\n if (!item || typeof item !== 'object' || Array.isArray(item)) return;\n for (const property of properties) {\n delete (item as JSONObject)[property];\n }\n}\n\n/**\n * Apply transformations to the sourceItem in-place\n * @param sourceItem - The source item to apply transformations to\n * @param transform - The transformations to apply\n * @param targetLocale - The target locale\n * @param defaultLocale - The default locale\n */\nexport function applyTransformations(\n sourceItem: JSONValue,\n transform: SourceObjectOptions['transform'],\n targetLocale: string,\n defaultLocale: string\n): void {\n if (!transform) return;\n\n const targetLocaleProperties = getConfiguredLocaleProperties(targetLocale);\n const defaultLocaleProperties = getConfiguredLocaleProperties(defaultLocale);\n\n for (const [transformPath, transformOptions] of Object.entries(transform)) {\n if (\n !transformOptions.replace ||\n typeof transformOptions.replace !== 'string'\n ) {\n continue;\n }\n const results = getJSONPathMatches(sourceItem, transformPath);\n if (!results || results.length === 0) {\n continue;\n }\n results.forEach((result) => {\n if (typeof result.value !== 'string') {\n return;\n }\n // Replace locale placeholders in the replace string\n let replaceString = transformOptions.replace;\n\n // Replace all locale property placeholders\n replaceString = replaceLocalePlaceholders(\n replaceString,\n targetLocaleProperties\n );\n\n if (\n transformOptions.match &&\n typeof transformOptions.match === 'string'\n ) {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transformOptions.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n result.value = result.value.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n } else {\n result.value = replaceString;\n }\n\n // Update the actual sourceItem using JSONPointer\n setJSONPointerValue(sourceItem, result.pointer, result.value);\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logger } from "../../console/logger.js";
|
|
2
|
-
import {
|
|
2
|
+
import { api } from "../../utils/api.js";
|
|
3
3
|
import { parseXcstringsCatalog, serializeXcstringsSlice } from "./parseXcstrings.js";
|
|
4
4
|
import * as fs$1 from "fs";
|
|
5
5
|
//#region src/formats/xcstrings/mergeXcstrings.ts
|
|
@@ -16,7 +16,7 @@ import * as fs$1 from "fs";
|
|
|
16
16
|
* catalog no longer has are skipped with a warning. Throws on invalid content.
|
|
17
17
|
*/
|
|
18
18
|
function mergeXcstringsLocale(downloadedContent, locale, inputPath) {
|
|
19
|
-
const canonicalLocale =
|
|
19
|
+
const canonicalLocale = api.resolveCanonicalLocale(locale);
|
|
20
20
|
const catalog = parseXcstringsCatalog(fs$1.readFileSync(inputPath, "utf8"));
|
|
21
21
|
const downloaded = parseXcstringsCatalog(downloadedContent);
|
|
22
22
|
const strings = Object.create(null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mergeXcstrings.js","names":["fs"],"sources":["../../../src/formats/xcstrings/mergeXcstrings.ts"],"sourcesContent":["import * as fs from 'fs';\nimport { logger } from '../../console/logger.js';\nimport {
|
|
1
|
+
{"version":3,"file":"mergeXcstrings.js","names":["fs"],"sources":["../../../src/formats/xcstrings/mergeXcstrings.ts"],"sourcesContent":["import * as fs from 'fs';\nimport { logger } from '../../console/logger.js';\nimport { api } from '../../utils/api.js';\nimport {\n parseXcstringsCatalog,\n serializeXcstringsSlice,\n type XcstringsEntry,\n} from './parseXcstrings.js';\n\n/**\n * Folds one locale's downloaded translations into the catalog at `inputPath`.\n *\n * Catalog keys are canonical tags and the configured `locale` may be a custom\n * alias, so the alias is resolved before it is used as the key on either side.\n * The catalog is read fresh so a batch of locales accumulates. It is truth for\n * everything except that key: for each entry the download carries that\n * localization for, only it is copied into the catalog's entry. Entries,\n * entry-level fields, other locales, and unknown fields at every level are\n * untouched. Keys the download did not translate are left alone; keys the\n * catalog no longer has are skipped with a warning. Throws on invalid content.\n */\nexport function mergeXcstringsLocale(\n downloadedContent: string,\n locale: string,\n inputPath: string\n): string {\n const canonicalLocale = api.resolveCanonicalLocale(locale);\n const catalog = parseXcstringsCatalog(fs.readFileSync(inputPath, 'utf8'));\n const downloaded = parseXcstringsCatalog(downloadedContent);\n\n const strings: Record<string, XcstringsEntry> = Object.create(null);\n for (const [key, entry] of Object.entries(catalog.strings)) {\n strings[key] = entry;\n }\n\n const unmatchedKeys: string[] = [];\n for (const [key, downloadedEntry] of Object.entries(downloaded.strings)) {\n const localization = downloadedEntry.localizations?.[canonicalLocale];\n if (localization === undefined) continue;\n const entry = strings[key];\n if (entry === undefined) {\n unmatchedKeys.push(key);\n continue;\n }\n strings[key] = {\n ...entry,\n localizations: withLocalization(\n entry.localizations,\n canonicalLocale,\n localization\n ),\n };\n }\n\n if (unmatchedKeys.length > 0) {\n logger.warn(\n `Skipped ${unmatchedKeys.length} downloaded ${locale} translation(s) with no matching entry in the local catalog: ${unmatchedKeys\n .map((key) => JSON.stringify(key))\n .join(', ')}`\n );\n }\n\n return serializeXcstringsSlice({ ...catalog, strings });\n}\n\n/**\n * Clones `localizations` with `locale` set. An existing locale keeps its\n * position; a new one is inserted before the first existing key that sorts\n * after it, so the bytes do not depend on the order locales were downloaded\n * and keys already present never move.\n */\nfunction withLocalization(\n localizations: Record<string, unknown> | undefined,\n locale: string,\n localization: unknown\n): Record<string, unknown> {\n const merged: Record<string, unknown> = Object.create(null);\n let inserted = false;\n for (const [existing, value] of Object.entries(localizations ?? {})) {\n if (existing === locale) {\n merged[locale] = localization;\n inserted = true;\n continue;\n }\n if (!inserted && locale < existing) {\n merged[locale] = localization;\n inserted = true;\n }\n merged[existing] = value;\n }\n if (!inserted) merged[locale] = localization;\n return merged;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAqBA,SAAgB,qBACd,mBACA,QACA,WACQ;CACR,MAAM,kBAAkB,IAAI,uBAAuB,OAAO;CAC1D,MAAM,UAAU,sBAAsBA,KAAG,aAAa,WAAW,OAAO,CAAC;CACzE,MAAM,aAAa,sBAAsB,kBAAkB;CAE3D,MAAM,UAA0C,OAAO,OAAO,KAAK;AACnE,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,QAAQ,CACxD,SAAQ,OAAO;CAGjB,MAAM,gBAA0B,EAAE;AAClC,MAAK,MAAM,CAAC,KAAK,oBAAoB,OAAO,QAAQ,WAAW,QAAQ,EAAE;EACvE,MAAM,eAAe,gBAAgB,gBAAgB;AACrD,MAAI,iBAAiB,KAAA,EAAW;EAChC,MAAM,QAAQ,QAAQ;AACtB,MAAI,UAAU,KAAA,GAAW;AACvB,iBAAc,KAAK,IAAI;AACvB;;AAEF,UAAQ,OAAO;GACb,GAAG;GACH,eAAe,iBACb,MAAM,eACN,iBACA,aACD;GACF;;AAGH,KAAI,cAAc,SAAS,EACzB,QAAO,KACL,WAAW,cAAc,OAAO,cAAc,OAAO,+DAA+D,cACjH,KAAK,QAAQ,KAAK,UAAU,IAAI,CAAC,CACjC,KAAK,KAAK,GACd;AAGH,QAAO,wBAAwB;EAAE,GAAG;EAAS;EAAS,CAAC;;;;;;;;AASzD,SAAS,iBACP,eACA,QACA,cACyB;CACzB,MAAM,SAAkC,OAAO,OAAO,KAAK;CAC3D,IAAI,WAAW;AACf,MAAK,MAAM,CAAC,UAAU,UAAU,OAAO,QAAQ,iBAAiB,EAAE,CAAC,EAAE;AACnE,MAAI,aAAa,QAAQ;AACvB,UAAO,UAAU;AACjB,cAAW;AACX;;AAEF,MAAI,CAAC,YAAY,SAAS,UAAU;AAClC,UAAO,UAAU;AACjB,cAAW;;AAEb,SAAO,YAAY;;AAErB,KAAI,CAAC,SAAU,QAAO,UAAU;AAChC,QAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PACKAGE_VERSION = "2.
|
|
1
|
+
export declare const PACKAGE_VERSION = "2.22.0";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","names":[],"sources":["../../src/generated/version.ts"],"sourcesContent":["// This file is auto-generated. Do not edit manually.\nexport const PACKAGE_VERSION = '2.
|
|
1
|
+
{"version":3,"file":"version.js","names":[],"sources":["../../src/generated/version.ts"],"sourcesContent":["// This file is auto-generated. Do not edit manually.\nexport const PACKAGE_VERSION = '2.22.0';\n"],"mappings":";AACA,MAAa,kBAAkB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import updateConfig from "../fs/config/updateConfig.js";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import fs from "node:fs";
|
|
4
|
-
import { createDiagnosticMessage } from "generaltranslation/
|
|
4
|
+
import { createDiagnosticMessage } from "generaltranslation/diagnostics";
|
|
5
5
|
import { execFile } from "node:child_process";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
//#region src/git/setupMergeDrivers.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setupMergeDrivers.js","names":[],"sources":["../../src/git/setupMergeDrivers.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { createDiagnosticMessage } from 'generaltranslation/internal';\nimport type { Settings } from '../types/index.js';\nimport updateConfig from '../fs/config/updateConfig.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport type GitMergeDriverSetupOptions = {\n cwd?: string;\n dryRun?: boolean;\n omitConfigIds?: boolean;\n driverCommand?: string;\n};\n\nexport type GitMergeDriverSetupResult = {\n gitRoot: string;\n gitattributesPath: string;\n addedAttributes: string[];\n gitConfigCommands: string[][];\n updatedConfig: boolean;\n dryRun: boolean;\n warnings: string[];\n};\n\nconst GT_LOCK_DRIVER = 'merge=gt-lock';\nconst GTJSON_DRIVER = 'merge=gtjson';\n\nexport async function setupGitMergeDrivers(\n settings: Settings,\n options: GitMergeDriverSetupOptions = {}\n): Promise<GitMergeDriverSetupResult> {\n const cwd = options.cwd ?? process.cwd();\n const gitRoot = await getGitRoot(cwd);\n const gitattributesPath = path.join(gitRoot, '.gitattributes');\n const entries = getGitAttributesEntries(settings, gitRoot, cwd);\n const existingAttributes = fs.existsSync(gitattributesPath)\n ? fs.readFileSync(gitattributesPath, 'utf8')\n : '';\n const attributesUpdate = mergeGitAttributes(\n existingAttributes,\n entries.map(formatAttributeEntry)\n );\n const driverCommand = options.driverCommand ?? getDefaultDriverCommand(cwd);\n const gitConfigCommands = getGitConfigCommands(driverCommand);\n\n if (!options.dryRun) {\n if (attributesUpdate.added.length > 0) {\n fs.writeFileSync(gitattributesPath, attributesUpdate.content, 'utf8');\n }\n\n for (const args of gitConfigCommands) {\n await execFileAsync('git', ['config', '--local', ...args], {\n cwd: gitRoot,\n windowsHide: true,\n });\n }\n\n if (options.omitConfigIds) {\n await updateConfig(settings.config, {\n omitConfigIds: true,\n _versionId: null,\n _branchId: null,\n });\n }\n }\n\n return {\n gitRoot,\n gitattributesPath,\n addedAttributes: attributesUpdate.added,\n gitConfigCommands,\n updatedConfig: options.omitConfigIds === true,\n dryRun: options.dryRun === true,\n warnings: getLockfileWarnings(cwd),\n };\n}\n\nfunction getLockfileWarnings(cwd: string): string[] {\n const lockfilePath = path.join(cwd, 'gt-lock.json');\n if (!fs.existsSync(lockfilePath)) return [];\n\n let version: unknown;\n try {\n version = JSON.parse(fs.readFileSync(lockfilePath, 'utf8'))?.version;\n } catch {\n version = undefined;\n }\n if (version === 2) return [];\n\n return [\n createDiagnosticMessage({\n whatHappened:\n 'The gt-lock merge driver will fall back to a normal Git conflict',\n why: 'gt-lock.json is not in the version 2 lockfile format',\n fix: 'Run gt translate or gt stage to regenerate the lockfile',\n }),\n ];\n}\n\nexport function getGitAttributesEntries(\n settings: Settings,\n gitRoot: string,\n cwd: string = process.cwd()\n): { pattern: string; driver: string }[] {\n const entries: { pattern: string; driver: string }[] = [];\n const normalizedGitRoot = normalizePathForGitAttributes(gitRoot);\n const normalizedCwd = normalizePathForGitAttributes(cwd);\n const lockPattern = toGitAttributePattern(\n path.relative(normalizedGitRoot, path.join(normalizedCwd, 'gt-lock.json'))\n );\n if (lockPattern) {\n entries.push({ pattern: lockPattern, driver: GT_LOCK_DRIVER });\n }\n\n const gtJsonPath = settings.files?.placeholderPaths.gt;\n if (gtJsonPath) {\n const normalizedGtJsonPath = normalizePathForGitAttributes(gtJsonPath);\n const gtJsonPattern = toGitAttributePattern(\n path\n .relative(normalizedGitRoot, normalizedGtJsonPath)\n .replace(/\\[locale\\]/g, '*')\n );\n if (gtJsonPattern) {\n entries.push({ pattern: gtJsonPattern, driver: GTJSON_DRIVER });\n }\n }\n\n return entries;\n}\n\nexport function mergeGitAttributes(\n existingContent: string,\n linesToAdd: string[]\n): { content: string; added: string[] } {\n const existingLines = existingContent\n .split(/\\r?\\n/)\n .filter((line) => line.trim().length > 0);\n const existing = new Set(existingLines.map((line) => line.trim()));\n const added = linesToAdd.filter((line) => !existing.has(line.trim()));\n\n if (added.length === 0) {\n return { content: existingContent, added };\n }\n\n const prefix =\n existingContent.length > 0 && !existingContent.endsWith('\\n') ? '\\n' : '';\n const content = `${existingContent}${prefix}${added.join('\\n')}\\n`;\n return { content, added };\n}\n\nexport function getGitConfigCommands(driverCommand: string): string[][] {\n return [\n ['merge.gt-lock.name', 'GT lockfile merge driver'],\n [\n 'merge.gt-lock.driver',\n `${driverCommand} git merge-driver gt-lock %O %A %B %P`,\n ],\n ['merge.gtjson.name', 'GTJSON merge driver'],\n [\n 'merge.gtjson.driver',\n `${driverCommand} git merge-driver gtjson %O %A %B %P`,\n ],\n ];\n}\n\nexport function getDefaultDriverCommand(cwd: string = process.cwd()): string {\n // Git invokes merge drivers through sh on every platform, including\n // Windows, so use the extensionless sh shim with forward slashes rather\n // than gt.cmd.\n const localBin = path.join(cwd, 'node_modules', '.bin', 'gt');\n if (fs.existsSync(localBin)) {\n return quoteShellArg(localBin.split(path.sep).join('/'));\n }\n return 'gt';\n}\n\nfunction formatAttributeEntry(entry: {\n pattern: string;\n driver: string;\n}): string {\n return `${entry.pattern} ${entry.driver}`;\n}\n\nfunction toGitAttributePattern(relativePath: string): string | null {\n if (\n !relativePath ||\n relativePath.startsWith('..') ||\n path.isAbsolute(relativePath)\n ) {\n return null;\n }\n const pattern = relativePath.split(path.sep).join('/');\n if (!/[\\s\"]/.test(pattern)) {\n // Escape literal backslashes for the glob layer\n return pattern.replace(/\\\\/g, '\\\\\\\\');\n }\n // gitattributes splits fields on whitespace without honoring backslash\n // escapes, so patterns containing whitespace must be C-style double-quoted\n // (supported since Git 2.32). Quoted patterns decode twice — C-style\n // unquoting, then glob matching — so one literal backslash becomes four.\n const quoted = pattern.replace(/[\"\\\\]/g, (char) =>\n char === '\\\\' ? '\\\\\\\\\\\\\\\\' : '\\\\\"'\n );\n return `\"${quoted}\"`;\n}\n\nfunction quoteShellArg(value: string): string {\n return `\"${value.replace(/([\"\\\\$`])/g, '\\\\$1')}\"`;\n}\n\nfunction normalizePathForGitAttributes(filepath: string): string {\n const absolutePath = path.resolve(filepath);\n let existingPath = absolutePath;\n\n while (!fs.existsSync(existingPath)) {\n const parentPath = path.dirname(existingPath);\n if (parentPath === existingPath) {\n return absolutePath;\n }\n existingPath = parentPath;\n }\n\n try {\n return path.join(\n fs.realpathSync(existingPath),\n path.relative(existingPath, absolutePath)\n );\n } catch {\n return absolutePath;\n }\n}\n\nasync function getGitRoot(cwd: string): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\n 'git',\n ['rev-parse', '--show-toplevel'],\n {\n cwd,\n encoding: 'utf8',\n windowsHide: true,\n }\n );\n return stdout.trim();\n } catch {\n throw new Error(\n 'No Git repository found. Run this command from a Git worktree.'\n );\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"setupMergeDrivers.js","names":[],"sources":["../../src/git/setupMergeDrivers.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { createDiagnosticMessage } from 'generaltranslation/diagnostics';\nimport type { Settings } from '../types/index.js';\nimport updateConfig from '../fs/config/updateConfig.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport type GitMergeDriverSetupOptions = {\n cwd?: string;\n dryRun?: boolean;\n omitConfigIds?: boolean;\n driverCommand?: string;\n};\n\nexport type GitMergeDriverSetupResult = {\n gitRoot: string;\n gitattributesPath: string;\n addedAttributes: string[];\n gitConfigCommands: string[][];\n updatedConfig: boolean;\n dryRun: boolean;\n warnings: string[];\n};\n\nconst GT_LOCK_DRIVER = 'merge=gt-lock';\nconst GTJSON_DRIVER = 'merge=gtjson';\n\nexport async function setupGitMergeDrivers(\n settings: Settings,\n options: GitMergeDriverSetupOptions = {}\n): Promise<GitMergeDriverSetupResult> {\n const cwd = options.cwd ?? process.cwd();\n const gitRoot = await getGitRoot(cwd);\n const gitattributesPath = path.join(gitRoot, '.gitattributes');\n const entries = getGitAttributesEntries(settings, gitRoot, cwd);\n const existingAttributes = fs.existsSync(gitattributesPath)\n ? fs.readFileSync(gitattributesPath, 'utf8')\n : '';\n const attributesUpdate = mergeGitAttributes(\n existingAttributes,\n entries.map(formatAttributeEntry)\n );\n const driverCommand = options.driverCommand ?? getDefaultDriverCommand(cwd);\n const gitConfigCommands = getGitConfigCommands(driverCommand);\n\n if (!options.dryRun) {\n if (attributesUpdate.added.length > 0) {\n fs.writeFileSync(gitattributesPath, attributesUpdate.content, 'utf8');\n }\n\n for (const args of gitConfigCommands) {\n await execFileAsync('git', ['config', '--local', ...args], {\n cwd: gitRoot,\n windowsHide: true,\n });\n }\n\n if (options.omitConfigIds) {\n await updateConfig(settings.config, {\n omitConfigIds: true,\n _versionId: null,\n _branchId: null,\n });\n }\n }\n\n return {\n gitRoot,\n gitattributesPath,\n addedAttributes: attributesUpdate.added,\n gitConfigCommands,\n updatedConfig: options.omitConfigIds === true,\n dryRun: options.dryRun === true,\n warnings: getLockfileWarnings(cwd),\n };\n}\n\nfunction getLockfileWarnings(cwd: string): string[] {\n const lockfilePath = path.join(cwd, 'gt-lock.json');\n if (!fs.existsSync(lockfilePath)) return [];\n\n let version: unknown;\n try {\n version = JSON.parse(fs.readFileSync(lockfilePath, 'utf8'))?.version;\n } catch {\n version = undefined;\n }\n if (version === 2) return [];\n\n return [\n createDiagnosticMessage({\n whatHappened:\n 'The gt-lock merge driver will fall back to a normal Git conflict',\n why: 'gt-lock.json is not in the version 2 lockfile format',\n fix: 'Run gt translate or gt stage to regenerate the lockfile',\n }),\n ];\n}\n\nexport function getGitAttributesEntries(\n settings: Settings,\n gitRoot: string,\n cwd: string = process.cwd()\n): { pattern: string; driver: string }[] {\n const entries: { pattern: string; driver: string }[] = [];\n const normalizedGitRoot = normalizePathForGitAttributes(gitRoot);\n const normalizedCwd = normalizePathForGitAttributes(cwd);\n const lockPattern = toGitAttributePattern(\n path.relative(normalizedGitRoot, path.join(normalizedCwd, 'gt-lock.json'))\n );\n if (lockPattern) {\n entries.push({ pattern: lockPattern, driver: GT_LOCK_DRIVER });\n }\n\n const gtJsonPath = settings.files?.placeholderPaths.gt;\n if (gtJsonPath) {\n const normalizedGtJsonPath = normalizePathForGitAttributes(gtJsonPath);\n const gtJsonPattern = toGitAttributePattern(\n path\n .relative(normalizedGitRoot, normalizedGtJsonPath)\n .replace(/\\[locale\\]/g, '*')\n );\n if (gtJsonPattern) {\n entries.push({ pattern: gtJsonPattern, driver: GTJSON_DRIVER });\n }\n }\n\n return entries;\n}\n\nexport function mergeGitAttributes(\n existingContent: string,\n linesToAdd: string[]\n): { content: string; added: string[] } {\n const existingLines = existingContent\n .split(/\\r?\\n/)\n .filter((line) => line.trim().length > 0);\n const existing = new Set(existingLines.map((line) => line.trim()));\n const added = linesToAdd.filter((line) => !existing.has(line.trim()));\n\n if (added.length === 0) {\n return { content: existingContent, added };\n }\n\n const prefix =\n existingContent.length > 0 && !existingContent.endsWith('\\n') ? '\\n' : '';\n const content = `${existingContent}${prefix}${added.join('\\n')}\\n`;\n return { content, added };\n}\n\nexport function getGitConfigCommands(driverCommand: string): string[][] {\n return [\n ['merge.gt-lock.name', 'GT lockfile merge driver'],\n [\n 'merge.gt-lock.driver',\n `${driverCommand} git merge-driver gt-lock %O %A %B %P`,\n ],\n ['merge.gtjson.name', 'GTJSON merge driver'],\n [\n 'merge.gtjson.driver',\n `${driverCommand} git merge-driver gtjson %O %A %B %P`,\n ],\n ];\n}\n\nexport function getDefaultDriverCommand(cwd: string = process.cwd()): string {\n // Git invokes merge drivers through sh on every platform, including\n // Windows, so use the extensionless sh shim with forward slashes rather\n // than gt.cmd.\n const localBin = path.join(cwd, 'node_modules', '.bin', 'gt');\n if (fs.existsSync(localBin)) {\n return quoteShellArg(localBin.split(path.sep).join('/'));\n }\n return 'gt';\n}\n\nfunction formatAttributeEntry(entry: {\n pattern: string;\n driver: string;\n}): string {\n return `${entry.pattern} ${entry.driver}`;\n}\n\nfunction toGitAttributePattern(relativePath: string): string | null {\n if (\n !relativePath ||\n relativePath.startsWith('..') ||\n path.isAbsolute(relativePath)\n ) {\n return null;\n }\n const pattern = relativePath.split(path.sep).join('/');\n if (!/[\\s\"]/.test(pattern)) {\n // Escape literal backslashes for the glob layer\n return pattern.replace(/\\\\/g, '\\\\\\\\');\n }\n // gitattributes splits fields on whitespace without honoring backslash\n // escapes, so patterns containing whitespace must be C-style double-quoted\n // (supported since Git 2.32). Quoted patterns decode twice — C-style\n // unquoting, then glob matching — so one literal backslash becomes four.\n const quoted = pattern.replace(/[\"\\\\]/g, (char) =>\n char === '\\\\' ? '\\\\\\\\\\\\\\\\' : '\\\\\"'\n );\n return `\"${quoted}\"`;\n}\n\nfunction quoteShellArg(value: string): string {\n return `\"${value.replace(/([\"\\\\$`])/g, '\\\\$1')}\"`;\n}\n\nfunction normalizePathForGitAttributes(filepath: string): string {\n const absolutePath = path.resolve(filepath);\n let existingPath = absolutePath;\n\n while (!fs.existsSync(existingPath)) {\n const parentPath = path.dirname(existingPath);\n if (parentPath === existingPath) {\n return absolutePath;\n }\n existingPath = parentPath;\n }\n\n try {\n return path.join(\n fs.realpathSync(existingPath),\n path.relative(existingPath, absolutePath)\n );\n } catch {\n return absolutePath;\n }\n}\n\nasync function getGitRoot(cwd: string): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\n 'git',\n ['rev-parse', '--show-toplevel'],\n {\n cwd,\n encoding: 'utf8',\n windowsHide: true,\n }\n );\n return stdout.trim();\n } catch {\n throw new Error(\n 'No Git repository found. Run this command from a Git worktree.'\n );\n }\n}\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logger } from "./console/logger.js";
|
|
1
2
|
import { isPythonLibrary } from "./types/libraries.js";
|
|
2
3
|
import { determineLibraryForCLI } from "./fs/determineFramework/index.js";
|
|
3
4
|
import { BaseCLI } from "./cli/base.js";
|
|
@@ -9,6 +10,7 @@ import { VueCLI } from "./cli/vue.js";
|
|
|
9
10
|
//#region src/index.ts
|
|
10
11
|
function main(program) {
|
|
11
12
|
program.name("gt");
|
|
13
|
+
logger.setConsoleOutput("stderr");
|
|
12
14
|
const { library, additionalModules, directlyDeclaresVue } = determineLibraryForCLI();
|
|
13
15
|
let cli;
|
|
14
16
|
if (library === "gt-next") cli = new NextCLI(program, library, additionalModules);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { BaseCLI } from './cli/base.js';\nimport { NextCLI } from './cli/next.js';\nimport { ReactCLI } from './cli/react.js';\nimport { PythonCLI } from './cli/python.js';\nimport { determineLibraryForCLI } from './fs/determineFramework/index.js';\nimport { Command } from 'commander';\nimport { NodeCLI } from './cli/node.js';\nimport { Libraries, isPythonLibrary } from './types/libraries.js';\nimport { VueCLI } from './cli/vue.js';\n\nexport function main(program: Command) {\n program.name('gt');\n\n const { library, additionalModules, directlyDeclaresVue } =\n determineLibraryForCLI();\n let cli: BaseCLI;\n if (library === Libraries.GT_NEXT) {\n cli = new NextCLI(program, library, additionalModules);\n } else if (\n library === Libraries.GT_REACT ||\n library === Libraries.GT_REACT_NATIVE ||\n library === Libraries.GT_TANSTACK_START\n ) {\n cli = new ReactCLI(program, library, additionalModules);\n } else if (library === Libraries.GT_NODE) {\n cli = new NodeCLI(program, library, additionalModules);\n } else if (library === Libraries.GT_VUE) {\n cli = new VueCLI(program, additionalModules);\n } else if (isPythonLibrary(library)) {\n cli = new PythonCLI(program, library, additionalModules);\n } else if (\n directlyDeclaresVue &&\n (library === 'next-intl' || library === 'i18next')\n ) {\n cli = new VueCLI(program, additionalModules, library);\n } else {\n cli = new BaseCLI(program, library, additionalModules);\n }\n cli.init();\n cli.execute();\n}\n\nexport { BaseCLI };\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { BaseCLI } from './cli/base.js';\nimport { NextCLI } from './cli/next.js';\nimport { ReactCLI } from './cli/react.js';\nimport { PythonCLI } from './cli/python.js';\nimport { determineLibraryForCLI } from './fs/determineFramework/index.js';\nimport { Command } from 'commander';\nimport { NodeCLI } from './cli/node.js';\nimport { Libraries, isPythonLibrary } from './types/libraries.js';\nimport { VueCLI } from './cli/vue.js';\nimport { logger } from './console/logger.js';\n\nexport function main(program: Command) {\n program.name('gt');\n\n // Framework detection can log before the command is known, so keep startup\n // diagnostics off stdout; the first BaseCLI preAction hook then selects the\n // command's own routing.\n logger.setConsoleOutput('stderr');\n const { library, additionalModules, directlyDeclaresVue } =\n determineLibraryForCLI();\n let cli: BaseCLI;\n if (library === Libraries.GT_NEXT) {\n cli = new NextCLI(program, library, additionalModules);\n } else if (\n library === Libraries.GT_REACT ||\n library === Libraries.GT_REACT_NATIVE ||\n library === Libraries.GT_TANSTACK_START\n ) {\n cli = new ReactCLI(program, library, additionalModules);\n } else if (library === Libraries.GT_NODE) {\n cli = new NodeCLI(program, library, additionalModules);\n } else if (library === Libraries.GT_VUE) {\n cli = new VueCLI(program, additionalModules);\n } else if (isPythonLibrary(library)) {\n cli = new PythonCLI(program, library, additionalModules);\n } else if (\n directlyDeclaresVue &&\n (library === 'next-intl' || library === 'i18next')\n ) {\n cli = new VueCLI(program, additionalModules, library);\n } else {\n cli = new BaseCLI(program, library, additionalModules);\n }\n cli.init();\n cli.execute();\n}\n\nexport { BaseCLI };\n"],"mappings":";;;;;;;;;;AAWA,SAAgB,KAAK,SAAkB;AACrC,SAAQ,KAAK,KAAK;AAKlB,QAAO,iBAAiB,SAAS;CACjC,MAAM,EAAE,SAAS,mBAAmB,wBAClC,wBAAwB;CAC1B,IAAI;AACJ,KAAI,YAAA,UACF,OAAM,IAAI,QAAQ,SAAS,SAAS,kBAAkB;UAEtD,YAAA,cACA,YAAA,qBACA,YAAA,oBAEA,OAAM,IAAI,SAAS,SAAS,SAAS,kBAAkB;UAC9C,YAAA,UACT,OAAM,IAAI,QAAQ,SAAS,SAAS,kBAAkB;UAC7C,YAAA,SACT,OAAM,IAAI,OAAO,SAAS,kBAAkB;UACnC,gBAAgB,QAAQ,CACjC,OAAM,IAAI,UAAU,SAAS,SAAS,kBAAkB;UAExD,wBACC,YAAY,eAAe,YAAY,WAExC,OAAM,IAAI,OAAO,SAAS,mBAAmB,QAAQ;KAErD,OAAM,IAAI,QAAQ,SAAS,SAAS,kBAAkB;AAExD,KAAI,MAAM;AACV,KAAI,SAAS"}
|
package/dist/main.js
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { main } from "./index.js";
|
|
3
|
-
import
|
|
3
|
+
import { loadEnv } from "./utils/loadEnv.js";
|
|
4
4
|
import { program } from "commander";
|
|
5
5
|
//#region src/main.ts
|
|
6
|
-
|
|
7
|
-
dotenv.config({
|
|
8
|
-
path: ".env.local",
|
|
9
|
-
override: true
|
|
10
|
-
});
|
|
11
|
-
dotenv.config({
|
|
12
|
-
path: ".env.production",
|
|
13
|
-
override: true
|
|
14
|
-
});
|
|
6
|
+
loadEnv();
|
|
15
7
|
main(program);
|
|
16
8
|
program.parse();
|
|
17
9
|
//#endregion
|
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","names":[],"sources":["../src/main.ts"],"sourcesContent":["#!/usr/bin/env node\n\n// Non-binary router - directly runs main.ts\nimport { main } from './index.js';\nimport
|
|
1
|
+
{"version":3,"file":"main.js","names":[],"sources":["../src/main.ts"],"sourcesContent":["#!/usr/bin/env node\n\n// Non-binary router - directly runs main.ts\nimport { main } from './index.js';\nimport { loadEnv } from './utils/loadEnv.js';\nimport { program } from 'commander';\n\nloadEnv();\n\nmain(program);\nprogram.parse();\n"],"mappings":";;;;;AAOA,SAAS;AAET,KAAK,QAAQ;AACb,QAAQ,OAAO"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DATA_ATTR_PREFIX } from "../constants.js";
|
|
2
2
|
import { defaultVariableNames, getVariableName, minifyVariableType } from "../../../utils/getVariableName.js";
|
|
3
|
-
import { isAcceptedPluralForm } from "generaltranslation/internal";
|
|
4
3
|
import { HTML_CONTENT_PROPS } from "@generaltranslation/format/types";
|
|
4
|
+
import { isAcceptedPluralForm } from "generaltranslation/internal";
|
|
5
5
|
//#region src/react/jsx/utils/jsxParsing/addGTIdentifierToSyntaxTree.ts
|
|
6
6
|
/**
|
|
7
7
|
* Construct the data-_gt prop
|
|
@@ -19,10 +19,10 @@ import { randomUUID } from "node:crypto";
|
|
|
19
19
|
import path from "node:path";
|
|
20
20
|
import fs from "node:fs";
|
|
21
21
|
import * as t from "@babel/types";
|
|
22
|
-
import { isAcceptedPluralForm } from "generaltranslation/internal";
|
|
23
22
|
import { parse } from "@babel/parser";
|
|
24
23
|
import generateModule from "@babel/generator";
|
|
25
24
|
import { HTML_CONTENT_PROPS } from "@generaltranslation/format/types";
|
|
25
|
+
import { isAcceptedPluralForm } from "generaltranslation/internal";
|
|
26
26
|
import traverseModule from "@babel/traverse";
|
|
27
27
|
//#region src/react/jsx/utils/jsxParsing/parseJsx.ts
|
|
28
28
|
const generate = generateModule.default || generateModule;
|
|
@@ -6,9 +6,9 @@ import { resolveImportPath } from "./resolveImportPath.js";
|
|
|
6
6
|
import { buildImportMap } from "./buildImportMap.js";
|
|
7
7
|
import fs from "node:fs";
|
|
8
8
|
import * as t from "@babel/types";
|
|
9
|
-
import { declareVar } from "generaltranslation/internal";
|
|
10
9
|
import { parse } from "@babel/parser";
|
|
11
10
|
import generateModule from "@babel/generator";
|
|
11
|
+
import { declareVar } from "generaltranslation/internal";
|
|
12
12
|
import traverseModule from "@babel/traverse";
|
|
13
13
|
//#region src/react/jsx/utils/parseString.ts
|
|
14
14
|
const traverse = traverseModule.default || traverseModule;
|
|
@@ -3,8 +3,8 @@ import { warnInvalidIcuSync, warnNonStringSync } from "../../../../../console/in
|
|
|
3
3
|
import { nodeToStrings } from "../../parseString.js";
|
|
4
4
|
import { handleDerivation } from "./handleDerivation.js";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
|
-
import { indexVars } from "generaltranslation/internal";
|
|
7
6
|
import generateModule from "@babel/generator";
|
|
7
|
+
import { indexVars } from "generaltranslation/internal";
|
|
8
8
|
//#region src/react/jsx/utils/stringParsing/derivation/index.ts
|
|
9
9
|
const generate = generateModule.default || generateModule;
|
|
10
10
|
/**
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Settings, SupportedFrameworks } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Picks or creates the project, mints a development key limited to runtime
|
|
4
|
+
* translation, and saves both to .env.local. The file is checked before any
|
|
5
|
+
* key exists, every service call completes before anything is written, and
|
|
6
|
+
* the key is never printed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function provisionDevelopmentCredentials(settings: Settings, framework: SupportedFrameworks | undefined, cwd?: string): Promise<void>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { logger } from "../console/logger.js";
|
|
2
|
+
import { logErrorAndExit, promptSelect, promptText } from "../console/logging.js";
|
|
3
|
+
import { api } from "../utils/api.js";
|
|
4
|
+
import { inspectCredentialsEnvFile, setCredentials } from "../utils/credentials.js";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { createDiagnosticMessage } from "generaltranslation/diagnostics";
|
|
8
|
+
import { ProjectApiKeyPermission } from "generaltranslation/api";
|
|
9
|
+
//#region src/setup/developmentCredentials.ts
|
|
10
|
+
const DEVELOPMENT_KEY_NAME = "Development key (gt init)";
|
|
11
|
+
function noCreatableOrgError(dashboardUrl) {
|
|
12
|
+
return createDiagnosticMessage({
|
|
13
|
+
source: "gt",
|
|
14
|
+
severity: "Error",
|
|
15
|
+
whatHappened: "You are not a member of an organization that can create projects",
|
|
16
|
+
fix: `Create an organization in the dashboard ${dashboardUrl} or ask an admin for access, then rerun the setup wizard`
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async function createProject(settings, cwd) {
|
|
20
|
+
const orgs = await api.listOrgs();
|
|
21
|
+
if (orgs.length === 0) return logErrorAndExit(noCreatableOrgError(settings.dashboardUrl));
|
|
22
|
+
const orgId = orgs.length === 1 ? orgs[0].id : await promptSelect({
|
|
23
|
+
message: "Which organization should own the new project?",
|
|
24
|
+
options: orgs.map((org) => ({
|
|
25
|
+
value: org.id,
|
|
26
|
+
label: org.name
|
|
27
|
+
}))
|
|
28
|
+
});
|
|
29
|
+
const name = await promptText({
|
|
30
|
+
message: "What should the project be called?",
|
|
31
|
+
defaultValue: path.basename(cwd),
|
|
32
|
+
validate: (value) => value.trim().length > 0 || "Enter a project name"
|
|
33
|
+
});
|
|
34
|
+
const { project } = await api.createProject(orgId, {
|
|
35
|
+
name: name.trim() || path.basename(cwd),
|
|
36
|
+
defaultLocale: settings.defaultLocale
|
|
37
|
+
});
|
|
38
|
+
logger.info(`Created ${project.name} (${project.id})`);
|
|
39
|
+
return project.id;
|
|
40
|
+
}
|
|
41
|
+
async function selectProject(settings, cwd) {
|
|
42
|
+
const projects = await api.listProjects();
|
|
43
|
+
if (projects.length === 0) {
|
|
44
|
+
logger.info("No projects found for your account; creating one.");
|
|
45
|
+
return createProject(settings, cwd);
|
|
46
|
+
}
|
|
47
|
+
const choice = await promptSelect({
|
|
48
|
+
message: "Which project should this app use?",
|
|
49
|
+
options: [...projects.map((project) => ({
|
|
50
|
+
value: project,
|
|
51
|
+
label: project.name,
|
|
52
|
+
hint: project.orgName
|
|
53
|
+
})), {
|
|
54
|
+
value: null,
|
|
55
|
+
label: "Create a new project"
|
|
56
|
+
}]
|
|
57
|
+
});
|
|
58
|
+
return choice ? choice.id : createProject(settings, cwd);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Picks or creates the project, mints a development key limited to runtime
|
|
62
|
+
* translation, and saves both to .env.local. The file is checked before any
|
|
63
|
+
* key exists, every service call completes before anything is written, and
|
|
64
|
+
* the key is never printed.
|
|
65
|
+
*/
|
|
66
|
+
async function provisionDevelopmentCredentials(settings, framework, cwd = process.cwd()) {
|
|
67
|
+
await inspectCredentialsEnvFile(cwd);
|
|
68
|
+
const projectId = settings.projectId || await selectProject(settings, cwd);
|
|
69
|
+
const { apiKey } = await api.createProjectApiKey(projectId, {
|
|
70
|
+
name: DEVELOPMENT_KEY_NAME,
|
|
71
|
+
permissions: [ProjectApiKeyPermission["PROJECT:TRANSLATIONS:GENERATE"]]
|
|
72
|
+
});
|
|
73
|
+
await setCredentials({
|
|
74
|
+
projectId,
|
|
75
|
+
apiKey: apiKey.key
|
|
76
|
+
}, framework, cwd);
|
|
77
|
+
logger.success(`Saved the project ID and a development key to ${chalk.cyan(".env.local")}.`);
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { provisionDevelopmentCredentials };
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=developmentCredentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"developmentCredentials.js","names":[],"sources":["../../src/setup/developmentCredentials.ts"],"sourcesContent":["import path from 'node:path';\nimport chalk from 'chalk';\nimport { ProjectApiKeyPermission } from 'generaltranslation/api';\nimport { createDiagnosticMessage } from 'generaltranslation/diagnostics';\nimport { logger } from '../console/logger.js';\nimport {\n logErrorAndExit,\n promptSelect,\n promptText,\n} from '../console/logging.js';\nimport type { Settings, SupportedFrameworks } from '../types/index.js';\nimport { api } from '../utils/api.js';\nimport {\n inspectCredentialsEnvFile,\n setCredentials,\n} from '../utils/credentials.js';\n\nconst DEVELOPMENT_KEY_NAME = 'Development key (gt init)';\n\ntype ProjectChoice = Awaited<ReturnType<typeof api.listProjects>>[number];\n\nfunction noCreatableOrgError(dashboardUrl: string): string {\n return createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened:\n 'You are not a member of an organization that can create projects',\n fix: `Create an organization in the dashboard ${dashboardUrl} or ask an admin for access, then rerun the setup wizard`,\n });\n}\n\nasync function createProject(settings: Settings, cwd: string) {\n // Organizations are only needed to create; picking an existing project\n // must work for members without create access.\n const orgs = await api.listOrgs();\n if (orgs.length === 0) {\n return logErrorAndExit(noCreatableOrgError(settings.dashboardUrl));\n }\n const orgId =\n orgs.length === 1\n ? orgs[0].id\n : await promptSelect({\n message: 'Which organization should own the new project?',\n options: orgs.map((org) => ({ value: org.id, label: org.name })),\n });\n const name = await promptText({\n message: 'What should the project be called?',\n defaultValue: path.basename(cwd),\n validate: (value) => value.trim().length > 0 || 'Enter a project name',\n });\n const { project } = await api.createProject(orgId, {\n name: name.trim() || path.basename(cwd),\n defaultLocale: settings.defaultLocale,\n });\n logger.info(`Created ${project.name} (${project.id})`);\n return project.id;\n}\n\nasync function selectProject(settings: Settings, cwd: string) {\n const projects = await api.listProjects();\n if (projects.length === 0) {\n logger.info('No projects found for your account; creating one.');\n return createProject(settings, cwd);\n }\n const choice = await promptSelect<ProjectChoice | null>({\n message: 'Which project should this app use?',\n options: [\n ...projects.map((project) => ({\n value: project,\n label: project.name,\n hint: project.orgName,\n })),\n { value: null, label: 'Create a new project' },\n ],\n });\n return choice ? choice.id : createProject(settings, cwd);\n}\n\n/**\n * Picks or creates the project, mints a development key limited to runtime\n * translation, and saves both to .env.local. The file is checked before any\n * key exists, every service call completes before anything is written, and\n * the key is never printed.\n */\nexport async function provisionDevelopmentCredentials(\n settings: Settings,\n framework: SupportedFrameworks | undefined,\n cwd: string = process.cwd()\n): Promise<void> {\n await inspectCredentialsEnvFile(cwd);\n const projectId = settings.projectId || (await selectProject(settings, cwd));\n const { apiKey } = await api.createProjectApiKey(projectId, {\n name: DEVELOPMENT_KEY_NAME,\n permissions: [ProjectApiKeyPermission['PROJECT:TRANSLATIONS:GENERATE']],\n });\n await setCredentials({ projectId, apiKey: apiKey.key }, framework, cwd);\n logger.success(\n `Saved the project ID and a development key to ${chalk.cyan('.env.local')}.`\n );\n}\n"],"mappings":";;;;;;;;;AAiBA,MAAM,uBAAuB;AAI7B,SAAS,oBAAoB,cAA8B;AACzD,QAAO,wBAAwB;EAC7B,QAAQ;EACR,UAAU;EACV,cACE;EACF,KAAK,2CAA2C,aAAa;EAC9D,CAAC;;AAGJ,eAAe,cAAc,UAAoB,KAAa;CAG5D,MAAM,OAAO,MAAM,IAAI,UAAU;AACjC,KAAI,KAAK,WAAW,EAClB,QAAO,gBAAgB,oBAAoB,SAAS,aAAa,CAAC;CAEpE,MAAM,QACJ,KAAK,WAAW,IACZ,KAAK,GAAG,KACR,MAAM,aAAa;EACjB,SAAS;EACT,SAAS,KAAK,KAAK,SAAS;GAAE,OAAO,IAAI;GAAI,OAAO,IAAI;GAAM,EAAE;EACjE,CAAC;CACR,MAAM,OAAO,MAAM,WAAW;EAC5B,SAAS;EACT,cAAc,KAAK,SAAS,IAAI;EAChC,WAAW,UAAU,MAAM,MAAM,CAAC,SAAS,KAAK;EACjD,CAAC;CACF,MAAM,EAAE,YAAY,MAAM,IAAI,cAAc,OAAO;EACjD,MAAM,KAAK,MAAM,IAAI,KAAK,SAAS,IAAI;EACvC,eAAe,SAAS;EACzB,CAAC;AACF,QAAO,KAAK,WAAW,QAAQ,KAAK,IAAI,QAAQ,GAAG,GAAG;AACtD,QAAO,QAAQ;;AAGjB,eAAe,cAAc,UAAoB,KAAa;CAC5D,MAAM,WAAW,MAAM,IAAI,cAAc;AACzC,KAAI,SAAS,WAAW,GAAG;AACzB,SAAO,KAAK,oDAAoD;AAChE,SAAO,cAAc,UAAU,IAAI;;CAErC,MAAM,SAAS,MAAM,aAAmC;EACtD,SAAS;EACT,SAAS,CACP,GAAG,SAAS,KAAK,aAAa;GAC5B,OAAO;GACP,OAAO,QAAQ;GACf,MAAM,QAAQ;GACf,EAAE,EACH;GAAE,OAAO;GAAM,OAAO;GAAwB,CAC/C;EACF,CAAC;AACF,QAAO,SAAS,OAAO,KAAK,cAAc,UAAU,IAAI;;;;;;;;AAS1D,eAAsB,gCACpB,UACA,WACA,MAAc,QAAQ,KAAK,EACZ;AACf,OAAM,0BAA0B,IAAI;CACpC,MAAM,YAAY,SAAS,aAAc,MAAM,cAAc,UAAU,IAAI;CAC3E,MAAM,EAAE,WAAW,MAAM,IAAI,oBAAoB,WAAW;EAC1D,MAAM;EACN,aAAa,CAAC,wBAAwB,iCAAiC;EACxE,CAAC;AACF,OAAM,eAAe;EAAE;EAAW,QAAQ,OAAO;EAAK,EAAE,WAAW,IAAI;AACvE,QAAO,QACL,iDAAiD,MAAM,KAAK,aAAa,CAAC,GAC3E"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { logger } from "../console/logger.js";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import fs from "node:fs";
|
|
4
|
-
import { createDiagnosticMessage } from "generaltranslation/
|
|
4
|
+
import { createDiagnosticMessage } from "generaltranslation/diagnostics";
|
|
5
5
|
//#region src/setup/setupViteSPA.ts
|
|
6
6
|
const defaultBootstrapFilename = "gt-entry.ts";
|
|
7
7
|
const alternateBootstrapFilename = "gt-bootstrap.ts";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setupViteSPA.js","names":[],"sources":["../../src/setup/setupViteSPA.ts"],"sourcesContent":["import { createDiagnosticMessage } from 'generaltranslation/
|
|
1
|
+
{"version":3,"file":"setupViteSPA.js","names":[],"sources":["../../src/setup/setupViteSPA.ts"],"sourcesContent":["import { createDiagnosticMessage } from 'generaltranslation/diagnostics';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { logger } from '../console/logger.js';\n\ntype SetupViteSPAOptions = {\n appDirectory: string;\n configFilepath: string;\n defaultLocale: string;\n locales: string[];\n translationsDir?: string;\n};\n\nconst defaultBootstrapFilename = 'gt-entry.ts';\nconst alternateBootstrapFilename = 'gt-bootstrap.ts';\n\nfunction getBootstrapConflictError(filename: string): string {\n return createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened: 'The Vite bootstrap file already exists',\n why: `GT will not overwrite an existing src/${filename} file`,\n fix: 'Move or rename that file and rerun `npx gt@latest`',\n });\n}\n\nfunction getLoaderContent(translationsImport: string): string {\n return `export default async function loadTranslations(locale: string) {\n const translations = await import(\\`${translationsImport}/\\${locale}.json\\`);\n return translations.default;\n}\n`;\n}\n\nfunction isGeneratedLoader(content: string): boolean {\n return /^export default async function loadTranslations\\(locale: string\\) \\{\\r?\\n const translations = await import\\(`[^`]+\\/\\$\\{locale\\}\\.json`\\);\\r?\\n return translations\\.default;\\r?\\n\\}\\r?\\n?$/.test(\n content\n );\n}\n\nfunction toRelativeImport(fromDirectory: string, toPath: string): string {\n const relativePath = path\n .relative(fromDirectory, toPath)\n .split(path.sep)\n .join(path.posix.sep);\n return relativePath.startsWith('.') ? relativePath : `./${relativePath}`;\n}\n\nfunction getEntryImport(\n appDirectory: string,\n sourceDirectory: string,\n source: string\n): string {\n const sourcePath = source.replace(/[?#].*$/, '');\n const entryPath = sourcePath.startsWith('/')\n ? path.resolve(appDirectory, `.${sourcePath}`)\n : path.resolve(appDirectory, sourcePath);\n return toRelativeImport(sourceDirectory, entryPath).replace(\n /\\.(?:[cm]?[jt]sx?)$/i,\n ''\n );\n}\n\nfunction getBootstrapEntry(bootstrap: string): string | undefined {\n return bootstrap.match(/await\\s+import\\(\\s*(['\"])([^'\"]+)\\1\\s*\\)/)?.[2];\n}\n\nfunction getModuleEntry(indexHtml: string): {\n script: string;\n source: string;\n} {\n const moduleScripts = indexHtml.match(\n /<script\\b[^>]*\\btype=(['\"])module\\1[^>]*>\\s*<\\/script>/gi\n );\n for (const script of moduleScripts ?? []) {\n const source = script.match(/\\bsrc=(['\"])([^'\"]+)\\1/i)?.[2];\n if (source) return { script, source };\n }\n\n throw new Error(\n createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened: 'No Vite module entry was found in index.html',\n fix: 'Add a module script for the app entry and rerun `npx gt@latest`',\n })\n );\n}\n\nexport async function setupViteSPA({\n appDirectory,\n configFilepath,\n defaultLocale,\n locales,\n translationsDir,\n}: SetupViteSPAOptions): Promise<void> {\n const indexHtmlPath = path.join(appDirectory, 'index.html');\n const sourceDirectory = path.join(appDirectory, 'src');\n const indexHtml = await fs.promises.readFile(indexHtmlPath, 'utf8');\n const { script, source } = getModuleEntry(indexHtml);\n const declaredEntryImport = getEntryImport(\n appDirectory,\n sourceDirectory,\n source\n );\n const configuredBootstrap = source.match(\n /^\\/?src\\/(gt-entry\\.ts|gt-bootstrap\\.ts)(?:[?#].*)?$/\n )?.[1];\n const isAlreadyConfigured = configuredBootstrap !== undefined;\n const bootstrapFilename =\n configuredBootstrap ??\n (declaredEntryImport === './gt-entry'\n ? alternateBootstrapFilename\n : defaultBootstrapFilename);\n const bootstrapPath = path.join(sourceDirectory, bootstrapFilename);\n let existingBootstrap: string | undefined;\n\n if (fs.existsSync(bootstrapPath)) {\n existingBootstrap = await fs.promises.readFile(bootstrapPath, 'utf8');\n if (!existingBootstrap.includes('initializeGTSPA')) {\n throw new Error(getBootstrapConflictError(bootstrapFilename));\n }\n }\n\n const entryImport = isAlreadyConfigured\n ? existingBootstrap && getBootstrapEntry(existingBootstrap)\n : declaredEntryImport;\n if (!entryImport) {\n throw new Error(\n createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened: 'The existing Vite bootstrap has no app entry import',\n fix: 'Restore the app entry import and rerun `npx gt@latest`',\n })\n );\n }\n\n await fs.promises.mkdir(sourceDirectory, { recursive: true });\n\n let loadTranslationsImport = '';\n let loadTranslationsOption = 'gtConfig';\n if (translationsDir) {\n const translationsPath = path.resolve(appDirectory, translationsDir);\n const loaderPath = path.join(sourceDirectory, 'loadTranslations.ts');\n const translationsImport = toRelativeImport(\n sourceDirectory,\n translationsPath\n );\n const existingLoader = fs.existsSync(loaderPath)\n ? await fs.promises.readFile(loaderPath, 'utf8')\n : undefined;\n if (!existingLoader || isGeneratedLoader(existingLoader)) {\n await fs.promises.writeFile(\n loaderPath,\n getLoaderContent(translationsImport)\n );\n }\n\n await fs.promises.mkdir(translationsPath, { recursive: true });\n for (const locale of new Set(locales)) {\n if (locale === defaultLocale) continue;\n const stubPath = path.join(translationsPath, `${locale}.json`);\n if (!fs.existsSync(stubPath)) {\n await fs.promises.writeFile(stubPath, '{}\\n');\n }\n }\n\n loadTranslationsImport =\n \"import loadTranslations from './loadTranslations';\\n\";\n loadTranslationsOption = '{ ...gtConfig, loadTranslations }';\n }\n\n const configImport = toRelativeImport(\n sourceDirectory,\n path.resolve(appDirectory, configFilepath)\n );\n await fs.promises.writeFile(\n bootstrapPath,\n `import { initializeGTSPA } from 'gt-react';\nimport gtConfig from '${configImport}';\n${loadTranslationsImport}\nawait initializeGTSPA(${loadTranslationsOption});\n\nawait import('${entryImport}');\n`\n );\n\n if (!isAlreadyConfigured) {\n const updatedScript = script.replace(source, `/src/${bootstrapFilename}`);\n await fs.promises.writeFile(\n indexHtmlPath,\n indexHtml.replace(script, updatedScript)\n );\n }\n\n logger.success('Configured initializeGTSPA for this Vite application.');\n}\n"],"mappings":"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"}
|
package/dist/setup/userInput.js
CHANGED
|
@@ -4,12 +4,17 @@ import { libraryDefaultLocale } from "generaltranslation/internal";
|
|
|
4
4
|
async function getDesiredLocales(existingConfig) {
|
|
5
5
|
const configuredDefaultLocale = typeof existingConfig?.defaultLocale === "string" && existingConfig.defaultLocale ? existingConfig.defaultLocale : void 0;
|
|
6
6
|
const configuredLocales = Array.isArray(existingConfig?.locales) && existingConfig.locales.every((locale) => typeof locale === "string") ? existingConfig.locales : void 0;
|
|
7
|
+
const customMapping = existingConfig?.customMapping && typeof existingConfig.customMapping === "object" ? existingConfig.customMapping : void 0;
|
|
7
8
|
return {
|
|
8
9
|
defaultLocale: configuredDefaultLocale ?? await promptLocale({
|
|
9
10
|
message: "What is the default locale for your project?",
|
|
10
|
-
defaultValue: libraryDefaultLocale
|
|
11
|
+
defaultValue: libraryDefaultLocale,
|
|
12
|
+
customMapping
|
|
11
13
|
}),
|
|
12
|
-
locales: configuredLocales ?? await promptLocaleList({
|
|
14
|
+
locales: configuredLocales ?? await promptLocaleList({
|
|
15
|
+
message: "Which languages would you like to translate your project into?",
|
|
16
|
+
customMapping
|
|
17
|
+
})
|
|
13
18
|
};
|
|
14
19
|
}
|
|
15
20
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"userInput.js","names":[],"sources":["../../src/setup/userInput.ts"],"sourcesContent":["import { libraryDefaultLocale } from 'generaltranslation/internal';\nimport { promptLocale, promptLocaleList } from '../console/logging.js';\n\nexport async function getDesiredLocales(existingConfig?: {\n defaultLocale?: unknown;\n locales?: unknown;\n}): Promise<{\n defaultLocale: string;\n locales: string[];\n}> {\n const configuredDefaultLocale =\n typeof existingConfig?.defaultLocale === 'string' &&\n existingConfig.defaultLocale\n ? existingConfig.defaultLocale\n : undefined;\n const configuredLocales =\n Array.isArray(existingConfig?.locales) &&\n existingConfig.locales.every((locale) => typeof locale === 'string')\n ? (existingConfig.locales as string[])\n : undefined;\n\n // Ask for the default locale\n const defaultLocale =\n configuredDefaultLocale ??\n (await promptLocale({\n message: 'What is the default locale for your project?',\n defaultValue: libraryDefaultLocale,\n }));\n\n // Ask for the locales\n const locales =\n configuredLocales ??\n (await promptLocaleList({\n message: 'Which languages would you like to translate your project into?',\n }));\n return { defaultLocale, locales };\n}\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"userInput.js","names":[],"sources":["../../src/setup/userInput.ts"],"sourcesContent":["import { libraryDefaultLocale } from 'generaltranslation/internal';\nimport type { CustomMapping } from 'generaltranslation/types';\nimport { promptLocale, promptLocaleList } from '../console/logging.js';\n\nexport async function getDesiredLocales(existingConfig?: {\n defaultLocale?: unknown;\n locales?: unknown;\n customMapping?: unknown;\n}): Promise<{\n defaultLocale: string;\n locales: string[];\n}> {\n const configuredDefaultLocale =\n typeof existingConfig?.defaultLocale === 'string' &&\n existingConfig.defaultLocale\n ? existingConfig.defaultLocale\n : undefined;\n const configuredLocales =\n Array.isArray(existingConfig?.locales) &&\n existingConfig.locales.every((locale) => typeof locale === 'string')\n ? (existingConfig.locales as string[])\n : undefined;\n // Accept configured aliases (e.g. `french: { code: 'fr' }`) at the prompt.\n const customMapping =\n existingConfig?.customMapping &&\n typeof existingConfig.customMapping === 'object'\n ? (existingConfig.customMapping as CustomMapping)\n : undefined;\n\n // Ask for the default locale\n const defaultLocale =\n configuredDefaultLocale ??\n (await promptLocale({\n message: 'What is the default locale for your project?',\n defaultValue: libraryDefaultLocale,\n customMapping,\n }));\n\n // Ask for the locales\n const locales =\n configuredLocales ??\n (await promptLocaleList({\n message: 'Which languages would you like to translate your project into?',\n customMapping,\n }));\n return { defaultLocale, locales };\n}\n"],"mappings":";;;AAIA,eAAsB,kBAAkB,gBAOrC;CACD,MAAM,0BACJ,OAAO,gBAAgB,kBAAkB,YACzC,eAAe,gBACX,eAAe,gBACf,KAAA;CACN,MAAM,oBACJ,MAAM,QAAQ,gBAAgB,QAAQ,IACtC,eAAe,QAAQ,OAAO,WAAW,OAAO,WAAW,SAAS,GAC/D,eAAe,UAChB,KAAA;CAEN,MAAM,gBACJ,gBAAgB,iBAChB,OAAO,eAAe,kBAAkB,WACnC,eAAe,gBAChB,KAAA;AAkBN,QAAO;EAAE,eAdP,2BACC,MAAM,aAAa;GAClB,SAAS;GACT,cAAc;GACd;GACD,CAAC;EASoB,SALtB,qBACC,MAAM,iBAAiB;GACtB,SAAS;GACT;GACD,CAAC;EAC6B"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createDiagnosticMessage } from "generaltranslation/
|
|
1
|
+
import { createDiagnosticMessage } from "generaltranslation/diagnostics";
|
|
2
2
|
import { planVueExtraction } from "@generaltranslation/vue-extractor/integration";
|
|
3
3
|
//#region src/translation/extractInline.ts
|
|
4
4
|
const missingVueExtractorDiagnostic = createDiagnosticMessage({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractInline.js","names":[],"sources":["../../src/translation/extractInline.ts"],"sourcesContent":["import {\n planVueExtraction,\n type InlineExtractionOutput,\n type PrimaryInlineExtractor,\n} from '@generaltranslation/vue-extractor/integration';\nimport { createDiagnosticMessage } from 'generaltranslation/
|
|
1
|
+
{"version":3,"file":"extractInline.js","names":[],"sources":["../../src/translation/extractInline.ts"],"sourcesContent":["import {\n planVueExtraction,\n type InlineExtractionOutput,\n type PrimaryInlineExtractor,\n} from '@generaltranslation/vue-extractor/integration';\nimport { createDiagnosticMessage } from 'generaltranslation/diagnostics';\nimport type { InlineLibrary } from '../types/libraries.js';\nimport type { GTParsingFlags, ParsingConfigOptions } from '../types/parsing.js';\n\nexport type { PrimaryInlineExtractor } from '@generaltranslation/vue-extractor/integration';\n\nconst missingVueExtractorDiagnostic = createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened: 'The gt-vue extractor did not claim this project',\n fix: 'Install compatible versions of gt and @generaltranslation/vue-extractor',\n});\n\n/**\n * Adds package-owned Vue extraction around the existing inline extractor.\n *\n * Non-Vue projects immediately call the historical extractor with the same\n * arguments. The standalone Vue package owns activation, source partitioning,\n * compiler resolution, extraction, and cross-framework result merging.\n */\nexport function extractInlineFromProject(\n library: InlineLibrary,\n filePatterns: string[] | undefined,\n parsingFlags: GTParsingFlags,\n parsingOptions: ParsingConfigOptions,\n extractPrimary?: PrimaryInlineExtractor,\n tsconfigPath?: string\n): Promise<InlineExtractionOutput> {\n const plan = planVueExtraction({\n library,\n projectRoot: process.cwd(),\n filePatterns,\n includeSourceCodeContext: parsingFlags.includeSourceCodeContext,\n conditionNames: parsingOptions.conditionNames,\n vueCompilerOptions: parsingFlags.vueCompilerOptions,\n viteConfigPath: parsingFlags.viteConfigPath,\n tsconfigPath,\n });\n\n if (!plan.handled) {\n if (!extractPrimary) {\n throw new Error(missingVueExtractorDiagnostic);\n }\n return extractPrimary(filePatterns);\n }\n\n return plan.run({ extractPrimary });\n}\n"],"mappings":";;;AAWA,MAAM,gCAAgC,wBAAwB;CAC5D,QAAQ;CACR,UAAU;CACV,cAAc;CACd,KAAK;CACN,CAAC;;;;;;;;AASF,SAAgB,yBACd,SACA,cACA,cACA,gBACA,gBACA,cACiC;CACjC,MAAM,OAAO,kBAAkB;EAC7B;EACA,aAAa,QAAQ,KAAK;EAC1B;EACA,0BAA0B,aAAa;EACvC,gBAAgB,eAAe;EAC/B,oBAAoB,aAAa;EACjC,gBAAgB,aAAa;EAC7B;EACD,CAAC;AAEF,KAAI,CAAC,KAAK,SAAS;AACjB,MAAI,CAAC,eACH,OAAM,IAAI,MAAM,8BAA8B;AAEhD,SAAO,eAAe,aAAa;;AAGrC,QAAO,KAAK,IAAI,EAAE,gBAAgB,CAAC"}
|