gt 2.22.4 → 2.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/auth/errors.d.ts +1 -0
  3. package/dist/auth/errors.js +12 -2
  4. package/dist/auth/errors.js.map +1 -1
  5. package/dist/auth/interactiveLogin.d.ts +2 -0
  6. package/dist/auth/interactiveLogin.js +22 -0
  7. package/dist/auth/interactiveLogin.js.map +1 -0
  8. package/dist/auth/oauth.js +2 -2
  9. package/dist/auth/oauth.js.map +1 -1
  10. package/dist/cli/base.d.ts +22 -4
  11. package/dist/cli/base.js +328 -223
  12. package/dist/cli/base.js.map +1 -1
  13. package/dist/cli/commands/api.js +15 -16
  14. package/dist/cli/commands/api.js.map +1 -1
  15. package/dist/cli/node.js +1 -1
  16. package/dist/cli/node.js.map +1 -1
  17. package/dist/cli/react.js +1 -1
  18. package/dist/cli/react.js.map +1 -1
  19. package/dist/cli/vue.js +2 -2
  20. package/dist/cli/vue.js.map +1 -1
  21. package/dist/config/generateSettings.d.ts +2 -0
  22. package/dist/config/generateSettings.js +13 -6
  23. package/dist/config/generateSettings.js.map +1 -1
  24. package/dist/config/resolveConfig.js +0 -4
  25. package/dist/config/resolveConfig.js.map +1 -1
  26. package/dist/config/validateSettings.js +5 -4
  27. package/dist/config/validateSettings.js.map +1 -1
  28. package/dist/console/{inkLocaleData.d.ts → localeOptions.d.ts} +7 -2
  29. package/dist/console/{inkLocaleData.js → localeOptions.js} +3 -8
  30. package/dist/console/localeOptions.js.map +1 -0
  31. package/dist/console/logger.d.ts +7 -0
  32. package/dist/console/logger.js +48 -85
  33. package/dist/console/logger.js.map +1 -1
  34. package/dist/console/logging.d.ts +11 -1
  35. package/dist/console/logging.js +88 -100
  36. package/dist/console/logging.js.map +1 -1
  37. package/dist/formats/files/aggregateFiles.js +1 -1
  38. package/dist/formats/files/supportedFiles.d.ts +3 -0
  39. package/dist/formats/files/supportedFiles.js +10 -1
  40. package/dist/formats/files/supportedFiles.js.map +1 -1
  41. package/dist/formats/yaml/mergeYaml.js +1 -1
  42. package/dist/fs/config/loadConfig.d.ts +2 -0
  43. package/dist/fs/config/loadConfig.js +5 -1
  44. package/dist/fs/config/loadConfig.js.map +1 -1
  45. package/dist/fs/config/setupConfig.d.ts +18 -9
  46. package/dist/fs/config/setupConfig.js +51 -31
  47. package/dist/fs/config/setupConfig.js.map +1 -1
  48. package/dist/fs/createLoadTranslationsFile.d.ts +22 -1
  49. package/dist/fs/createLoadTranslationsFile.js +43 -20
  50. package/dist/fs/createLoadTranslationsFile.js.map +1 -1
  51. package/dist/generated/version.d.ts +1 -1
  52. package/dist/generated/version.js +1 -1
  53. package/dist/generated/version.js.map +1 -1
  54. package/dist/hooks/postProcess.d.ts +1 -2
  55. package/dist/hooks/postProcess.js +1 -1
  56. package/dist/hooks/postProcess.js.map +1 -1
  57. package/dist/locadex/setupFlow.d.ts +4 -1
  58. package/dist/locadex/setupFlow.js +5 -3
  59. package/dist/locadex/setupFlow.js.map +1 -1
  60. package/dist/react/jsx/utils/jsxParsing/addGTIdentifierToSyntaxTree.js +1 -1
  61. package/dist/react/jsx/utils/jsxParsing/parseJsx.js +1 -1
  62. package/dist/react/jsx/utils/parseString.js +1 -1
  63. package/dist/react/jsx/utils/stringParsing/derivation/index.js +1 -1
  64. package/dist/setup/buildTools/index.d.ts +44 -0
  65. package/dist/setup/buildTools/index.js +10 -0
  66. package/dist/setup/buildTools/index.js.map +1 -0
  67. package/dist/setup/buildTools/vite.d.ts +2 -0
  68. package/dist/setup/buildTools/vite.js +61 -0
  69. package/dist/setup/buildTools/vite.js.map +1 -0
  70. package/dist/setup/developmentCredentials.d.ts +40 -5
  71. package/dist/setup/developmentCredentials.js +119 -37
  72. package/dist/setup/developmentCredentials.js.map +1 -1
  73. package/dist/setup/instructions/base.md +1 -1
  74. package/dist/setup/onboarding.d.ts +131 -0
  75. package/dist/setup/onboarding.js +289 -0
  76. package/dist/setup/onboarding.js.map +1 -0
  77. package/dist/setup/setupTarget.d.ts +1 -0
  78. package/dist/setup/setupTarget.js +52 -0
  79. package/dist/setup/setupTarget.js.map +1 -0
  80. package/dist/setup/setupViteSPA.d.ts +32 -1
  81. package/dist/setup/setupViteSPA.js +110 -20
  82. package/dist/setup/setupViteSPA.js.map +1 -1
  83. package/dist/setup/userInput.d.ts +12 -3
  84. package/dist/setup/userInput.js +27 -11
  85. package/dist/setup/userInput.js.map +1 -1
  86. package/dist/setup/wizard.d.ts +19 -3
  87. package/dist/setup/wizard.js +119 -105
  88. package/dist/setup/wizard.js.map +1 -1
  89. package/dist/types/index.d.ts +2 -1
  90. package/dist/types/index.js +13 -1
  91. package/dist/types/index.js.map +1 -0
  92. package/dist/utils/credentials.d.ts +9 -6
  93. package/dist/utils/credentials.js +36 -22
  94. package/dist/utils/credentials.js.map +1 -1
  95. package/dist/utils/monorepoVersionCheck.d.ts +1 -1
  96. package/dist/utils/monorepoVersionCheck.js +5 -5
  97. package/dist/utils/monorepoVersionCheck.js.map +1 -1
  98. package/dist/utils/packageManager.js +13 -14
  99. package/dist/utils/packageManager.js.map +1 -1
  100. package/package.json +5 -9
  101. package/dist/console/inkFields.d.ts +0 -23
  102. package/dist/console/inkFields.js +0 -88
  103. package/dist/console/inkFields.js.map +0 -1
  104. package/dist/console/inkLayout.d.ts +0 -7
  105. package/dist/console/inkLayout.js +0 -140
  106. package/dist/console/inkLayout.js.map +0 -1
  107. package/dist/console/inkLocaleData.js.map +0 -1
  108. package/dist/console/inkPrompts.d.ts +0 -8
  109. package/dist/console/inkPrompts.js +0 -499
  110. package/dist/console/inkPrompts.js.map +0 -1
  111. package/dist/console/inkSession.d.ts +0 -3
  112. package/dist/console/inkSession.js +0 -42
  113. package/dist/console/inkSession.js.map +0 -1
  114. package/dist/console/inkTerminal.d.ts +0 -4
  115. package/dist/console/inkTerminal.js +0 -15
  116. package/dist/console/inkTerminal.js.map +0 -1
  117. package/dist/console/inkTheme.d.ts +0 -1
  118. package/dist/console/inkTheme.js +0 -6
  119. package/dist/console/inkTheme.js.map +0 -1
  120. package/dist/console/inkTypes.d.ts +0 -69
  121. package/dist/console/inkTypes.js +0 -1
  122. package/dist/console/inkUtils.d.ts +0 -24
  123. package/dist/console/inkUtils.js +0 -87
  124. package/dist/console/inkUtils.js.map +0 -1
  125. package/dist/console/terminalSession.d.ts +0 -5
  126. package/dist/console/terminalSession.js +0 -38
  127. package/dist/console/terminalSession.js.map +0 -1
@@ -1,9 +1,12 @@
1
+ import type { Option } from '@clack/prompts';
1
2
  import type { CustomMapping, FileToUpload } from 'generaltranslation/types';
2
3
  import { type InlineLibrary } from '../types/libraries.js';
3
4
  /**
4
5
  * Strip ANSI escape codes from a string (e.g., chalk color codes)
5
6
  */
6
7
  export declare function stripAnsi(str: string): string;
8
+ /** The message of the last logErrorAndExit call, for exit-time reporting. */
9
+ export declare function getLastExitError(): string | undefined;
7
10
  export declare function logErrorAndExit(message: string): never;
8
11
  export declare function exitSync(code: number): never;
9
12
  export declare function displayHeader(introString?: string): void;
@@ -17,6 +20,12 @@ export declare function promptText({ message, defaultValue, validate, }: {
17
20
  defaultValue?: string;
18
21
  validate?: (value: string) => boolean | string;
19
22
  }): Promise<string>;
23
+ /**
24
+ * Searchable locale options: supported locales ranked by the query, the
25
+ * configured customMapping aliases, and any other valid locale typed or
26
+ * already selected, so aliases and custom tags stay selectable.
27
+ */
28
+ export declare function getLocalePromptOptions(query: string, selected?: string[], customMapping?: CustomMapping): Option<string>[];
20
29
  export declare function promptLocale({ message, defaultValue, customMapping, }: {
21
30
  message: string;
22
31
  defaultValue?: string;
@@ -28,10 +37,11 @@ export declare function promptLocaleList({ message, defaultValue, required, cust
28
37
  required?: boolean;
29
38
  customMapping?: CustomMapping;
30
39
  }): Promise<string[]>;
31
- export declare function promptGlobPatterns({ label, message, defaultValue, }: {
40
+ export declare function promptGlobPatterns(options: {
32
41
  label: string;
33
42
  message: string;
34
43
  defaultValue?: string;
44
+ validate?: (value: string) => boolean | string;
35
45
  }): Promise<string>;
36
46
  export declare function promptSelect<T>({ message, options, defaultValue, }: {
37
47
  message: string;
@@ -1,24 +1,19 @@
1
- import { endTerminalSession, shouldUseInkPrompts } from "./terminalSession.js";
2
1
  import { logger } from "./logger.js";
3
2
  import { getCLIVersion } from "../utils/packageJson.js";
4
3
  import "../utils/constants.js";
5
- import { parseLocaleList, validateLocale, validateLocaleList } from "./promptParsing.js";
4
+ import { parseTypedLocale } from "./promptParsing.js";
5
+ import { getFilteredLocaleOptions } from "./localeOptions.js";
6
6
  import { getInlineElementsLabel } from "../types/libraries.js";
7
- import { cancel, confirm, isCancel, multiselect, select, text } from "@clack/prompts";
7
+ import { autocomplete, autocompleteMultiselect, cancel, confirm, isCancel, multiselect, select, text } from "@clack/prompts";
8
8
  import chalk from "chalk";
9
+ import { createDiagnosticMessage } from "generaltranslation/internal";
9
10
  //#region src/console/logging.ts
10
- function cancelPromptAndExit(message) {
11
- endTerminalSession();
12
- cancel(message);
13
- return exitSync(0);
14
- }
15
- async function loadInkPrompts() {
16
- return import("./inkPrompts.js");
17
- }
18
- async function runInkPrompt(prompt, cancelMessage = "Operation cancelled") {
19
- const result = await prompt(await loadInkPrompts());
20
- if (result.cancelled) return cancelPromptAndExit(cancelMessage);
21
- return result.value;
11
+ function exitIfCancelled(result, message = "Operation cancelled") {
12
+ if (isCancel(result)) {
13
+ cancel(message);
14
+ return exitSync(0);
15
+ }
16
+ return result;
22
17
  }
23
18
  /**
24
19
  * Strip ANSI escape codes from a string (e.g., chalk color codes)
@@ -26,7 +21,13 @@ async function runInkPrompt(prompt, cancelMessage = "Operation cancelled") {
26
21
  function stripAnsi(str) {
27
22
  return str.replace(/\x1B\[[0-9;]*m/g, "");
28
23
  }
24
+ let lastExitError;
25
+ /** The message of the last logErrorAndExit call, for exit-time reporting. */
26
+ function getLastExitError() {
27
+ return lastExitError;
28
+ }
29
29
  function logErrorAndExit(message) {
30
+ lastExitError = message;
30
31
  logger.error(message);
31
32
  return exitSync(1);
32
33
  }
@@ -77,117 +78,104 @@ function displayUpdatedVersionsFile(versionFilepath) {
77
78
  logger.success(`Updated versions file ${chalk.cyan(versionFilepath)}`);
78
79
  }
79
80
  async function promptText({ message, defaultValue, validate }) {
80
- if (shouldUseInkPrompts()) return await runInkPrompt((prompts) => prompts.inkPromptText({
81
- message,
82
- defaultValue,
83
- validate
84
- })) ?? "";
85
- const result = await text({
81
+ return exitIfCancelled(await text({
86
82
  message,
87
83
  placeholder: defaultValue,
84
+ defaultValue,
88
85
  validate: validate ? (value) => {
89
- const validation = validate(value || "");
86
+ const validation = validate(value || defaultValue || "");
90
87
  return validation === true ? void 0 : validation.toString();
91
88
  } : void 0
92
- });
93
- if (isCancel(result)) {
94
- cancel("Operation cancelled");
95
- return exitSync(0);
96
- }
97
- return result;
89
+ }));
90
+ }
91
+ /**
92
+ * Clack keeps the focused option while it still matches a new search, so
93
+ * typing `fr` could leave `af` (Afrikaans) focused. Refocus the top-ranked
94
+ * option whenever the search changes.
95
+ */
96
+ function searchableLocaleOptions(getOptions) {
97
+ let lastQuery = "";
98
+ return function() {
99
+ const query = this.userInput ?? "";
100
+ if (query !== lastQuery) {
101
+ lastQuery = query;
102
+ this.focusedValue = void 0;
103
+ }
104
+ return getOptions(query, this.selectedValues ?? []);
105
+ };
98
106
  }
107
+ /**
108
+ * Searchable locale options: supported locales ranked by the query, the
109
+ * configured customMapping aliases, and any other valid locale typed or
110
+ * already selected, so aliases and custom tags stay selectable.
111
+ */
112
+ function getLocalePromptOptions(query, selected = [], customMapping) {
113
+ const normalizedQuery = query.trim().toLowerCase();
114
+ const supported = getFilteredLocaleOptions(query).map((option) => ({
115
+ value: option.code,
116
+ label: option.label
117
+ }));
118
+ const known = new Set(supported.map((option) => option.value));
119
+ const aliases = Object.keys(customMapping ?? {}).filter((alias) => alias.toLowerCase().includes(normalizedQuery)).map((alias) => ({
120
+ value: alias,
121
+ label: alias,
122
+ hint: "custom mapping"
123
+ }));
124
+ const typed = parseTypedLocale(query, customMapping);
125
+ const extras = [...selected, ...typed ? [typed] : []].filter((locale) => !known.has(locale) && !aliases.some((alias) => alias.value === locale)).map((locale) => ({
126
+ value: locale,
127
+ label: locale
128
+ }));
129
+ return [...new Map([
130
+ ...aliases,
131
+ ...extras,
132
+ ...supported
133
+ ].map((option) => [option.value, option])).values()].sort((a, b) => Number(b.value.toLowerCase() === normalizedQuery) - Number(a.value.toLowerCase() === normalizedQuery));
134
+ }
135
+ const noLocaleSelectedError = createDiagnosticMessage({
136
+ whatHappened: "No locale matches the search",
137
+ fix: "Change the search and select a locale from the list"
138
+ });
99
139
  async function promptLocale({ message, defaultValue, customMapping }) {
100
- if (shouldUseInkPrompts()) return await runInkPrompt((prompts) => prompts.inkPromptLocale({
101
- message,
102
- defaultValue,
103
- customMapping
104
- })) ?? "";
105
- return promptText({
140
+ return exitIfCancelled(await autocomplete({
106
141
  message,
107
- defaultValue,
108
- validate: (value) => validateLocale(value, customMapping)
109
- });
142
+ placeholder: "Type to search locales",
143
+ initialValue: defaultValue,
144
+ options: searchableLocaleOptions((query) => getLocalePromptOptions(query, [], customMapping)),
145
+ validate: (value) => value ? void 0 : noLocaleSelectedError
146
+ }));
110
147
  }
111
148
  async function promptLocaleList({ message, defaultValue, required = true, customMapping }) {
112
- if (shouldUseInkPrompts()) return await runInkPrompt((prompts) => prompts.inkPromptLocaleMulti({
149
+ return exitIfCancelled(await autocompleteMultiselect({
113
150
  message,
114
- defaultValue,
151
+ placeholder: "Type to search, Tab or Space to select",
152
+ initialValues: defaultValue,
115
153
  required,
116
- customMapping
117
- })) ?? [];
118
- return promptText({
119
- message,
120
- defaultValue: defaultValue?.join(" "),
121
- validate: (value) => validateLocaleList(value, customMapping)
122
- }).then(parseLocaleList);
154
+ options: searchableLocaleOptions((query, selected) => getLocalePromptOptions(query, [...defaultValue ?? [], ...selected], customMapping))
155
+ }));
123
156
  }
124
- async function promptGlobPatterns({ label, message, defaultValue }) {
125
- if (shouldUseInkPrompts()) return await runInkPrompt((prompts) => prompts.inkPromptGlob({
126
- label,
127
- message,
128
- defaultValue
129
- })) ?? "";
130
- return promptText({
131
- message,
132
- defaultValue
133
- });
157
+ async function promptGlobPatterns(options) {
158
+ return promptText(options);
134
159
  }
135
160
  async function promptSelect({ message, options, defaultValue }) {
136
- if (shouldUseInkPrompts()) return await runInkPrompt((prompts) => prompts.inkPromptSelect({
161
+ return exitIfCancelled(await select({
137
162
  message,
138
163
  options,
139
- defaultValue
140
- }));
141
- const result = await select({
142
- message,
143
- options: options.map((opt) => ({
144
- value: opt.value,
145
- label: opt.label,
146
- hint: opt.hint
147
- })),
148
164
  initialValue: defaultValue
149
- });
150
- if (isCancel(result)) {
151
- cancel("Operation cancelled");
152
- return exitSync(0);
153
- }
154
- return result;
165
+ }));
155
166
  }
156
167
  async function promptMultiSelect({ message, options, required = true }) {
157
- if (shouldUseInkPrompts()) return await runInkPrompt((prompts) => prompts.inkPromptMultiSelect({
168
+ return exitIfCancelled(await multiselect({
158
169
  message,
159
170
  options,
160
171
  required
161
- })) ?? [];
162
- const result = await multiselect({
163
- message,
164
- options: options.map((opt) => ({
165
- value: opt.value,
166
- label: opt.label,
167
- hint: opt.hint
168
- })),
169
- required
170
- });
171
- if (isCancel(result)) {
172
- cancel("Operation cancelled");
173
- return exitSync(0);
174
- }
175
- return result;
172
+ }));
176
173
  }
177
174
  async function promptConfirm({ message, defaultValue = true, cancelMessage = "Operation cancelled" }) {
178
- if (shouldUseInkPrompts()) return await runInkPrompt((prompts) => prompts.inkPromptConfirm({
179
- message,
180
- defaultValue
181
- }), cancelMessage) ?? defaultValue;
182
- const result = await confirm({
175
+ return exitIfCancelled(await confirm({
183
176
  message,
184
177
  initialValue: defaultValue
185
- });
186
- if (isCancel(result)) {
187
- cancel(cancelMessage);
188
- return exitSync(0);
189
- }
190
- return result;
178
+ }), cancelMessage);
191
179
  }
192
180
  function warnApiKeyInConfig(optionsFilepath) {
193
181
  logger.warn(`Found ${chalk.cyan("apiKey")} in "${chalk.green(optionsFilepath)}". ` + chalk.white("Your API key is exposed! Remove it from the file and include it as an environment variable."));
@@ -221,6 +209,6 @@ function logCollectedFiles(files, inlineComponents, inlineLibrary) {
221
209
  }).join("\n"));
222
210
  }
223
211
  //#endregion
224
- export { displayCreatedConfigFile, displayHeader, displayProjectId, displayResolvedPaths, displayUpdatedConfigFile, displayUpdatedVersionsFile, exitSync, logCollectedFiles, logErrorAndExit, promptConfirm, promptGlobPatterns, promptLocale, promptLocaleList, promptMultiSelect, promptSelect, promptText, stripAnsi, warnApiKeyInConfig, warnDeprecatedField, warnHasUnwrappedExpression, warnNoId, warnTemplateLiteral, warnTernary, warnVariableProp };
212
+ export { displayCreatedConfigFile, displayHeader, displayProjectId, displayResolvedPaths, displayUpdatedConfigFile, displayUpdatedVersionsFile, exitSync, getLastExitError, getLocalePromptOptions, logCollectedFiles, logErrorAndExit, promptConfirm, promptGlobPatterns, promptLocale, promptLocaleList, promptMultiSelect, promptSelect, promptText, stripAnsi, warnApiKeyInConfig, warnDeprecatedField, warnHasUnwrappedExpression, warnNoId, warnTemplateLiteral, warnTernary, warnVariableProp };
225
213
 
226
214
  //# sourceMappingURL=logging.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logging.js","names":[],"sources":["../../src/console/logging.ts"],"sourcesContent":["import {\n text,\n select,\n confirm,\n isCancel,\n cancel,\n multiselect,\n} from '@clack/prompts';\nimport type { Option } from '@clack/prompts';\nimport chalk from 'chalk';\nimport { getCLIVersion } from '../utils/packageJson.js';\nimport { logger } from './logger.js';\nimport { TEMPLATE_FILE_NAME } from '../utils/constants.js';\nimport type { CustomMapping, FileToUpload } from 'generaltranslation/types';\nimport { endTerminalSession, shouldUseInkPrompts } from './terminalSession.js';\nimport {\n parseLocaleList,\n validateLocale,\n validateLocaleList,\n} from './promptParsing.js';\nimport {\n getInlineElementsLabel,\n type InlineLibrary,\n} from '../types/libraries.js';\n\nfunction cancelPromptAndExit(message: string): never {\n endTerminalSession();\n cancel(message);\n return exitSync(0);\n}\n\nasync function loadInkPrompts() {\n return import('./inkPrompts.js');\n}\n\nasync function runInkPrompt<T>(\n prompt: (prompts: Awaited<ReturnType<typeof loadInkPrompts>>) => Promise<{\n value?: T;\n cancelled: boolean;\n }>,\n cancelMessage = 'Operation cancelled'\n) {\n const result = await prompt(await loadInkPrompts());\n if (result.cancelled) {\n return cancelPromptAndExit(cancelMessage);\n }\n return result.value;\n}\n\n/**\n * Strip ANSI escape codes from a string (e.g., chalk color codes)\n */\nexport function stripAnsi(str: string): string {\n // eslint-disable-next-line no-control-regex, no-useless-escape\n return str.replace(/\\x1B\\[[0-9;]*m/g, '');\n}\n\nexport function logErrorAndExit(message: string): never {\n logger.error(message);\n return exitSync(1);\n}\n\nexport function exitSync(code: number): never {\n // Flush logs before exit\n logger.flush();\n process.exit(code);\n}\n\n// GT specific logging\nexport function displayHeader(introString?: string) {\n // The ASCII banner is pure chatter and writes to console directly, bypassing\n // the logger, so gate it here to honor --quiet. startCommand stays outside\n // the gate: it is quiet-aware itself and must still run so the file log\n // gets its [START] marker.\n if (!logger.isQuiet()) {\n displayAsciiTitle();\n displayInitializingText();\n }\n\n if (introString) {\n logger.startCommand(introString);\n }\n}\n\nfunction displayAsciiTitle() {\n // eslint-disable-next-line no-console\n console.log(\n chalk.cyan(\n `\\n @@@@@@@@@@@@@@@@@@@@@@\n @@ @@@@@@@@@@@@ @@@@@@@\n @@ @@@ @ @\n@@ @@ @ @\n@@ @ @@@@@ @ @\n@@ @@ @@@ @ @ @\n @@ @@ @ @ @ @\n @@ @@@@@@@ @ @ @\n @@@@@@@@@ @ @`\n )\n );\n}\n\nfunction displayInitializingText() {\n const version = getCLIVersion();\n // eslint-disable-next-line no-console\n console.log(\n `\\n${chalk.bold.blue('General Translation, Inc.')}\n${chalk.dim('https://generaltranslation.com/docs')}\n${chalk.dim(`CLI Version: ${version}\\n`)}`\n );\n}\n\nexport function displayProjectId(projectId: string) {\n logger.message(\n chalk.dim(`Project ID: ${chalk.bold(projectId)}`),\n chalk.cyan('~')\n );\n}\n\nexport function displayResolvedPaths(resolvedPaths: [string, string][]) {\n const paths = resolvedPaths.map(([key, resolvedPath]) => {\n return chalk.dim(`'${chalk.white(key)}' → '${chalk.green(resolvedPath)}'`);\n });\n logger.step(`Resolved path aliases:\\n${paths.join('\\n')}`);\n}\n\nexport function displayCreatedConfigFile(configFilepath: string) {\n logger.step(`Created config file ${chalk.cyan(configFilepath)}`);\n}\n\nexport function displayUpdatedConfigFile(configFilepath: string) {\n logger.success(`Updated config file ${chalk.cyan(configFilepath)}`);\n}\n\nexport function displayUpdatedVersionsFile(versionFilepath: string) {\n logger.success(`Updated versions file ${chalk.cyan(versionFilepath)}`);\n}\n\n// Input prompts\nexport async function promptText({\n message,\n defaultValue,\n validate,\n}: {\n message: string;\n defaultValue?: string;\n validate?: (value: string) => boolean | string;\n}) {\n if (shouldUseInkPrompts()) {\n return (\n (await runInkPrompt<string>((prompts) =>\n prompts.inkPromptText({ message, defaultValue, validate })\n )) ?? ''\n );\n }\n\n const result = await text({\n message,\n placeholder: defaultValue,\n validate: validate\n ? (value) => {\n const validation = validate(value || '');\n return validation === true ? undefined : validation.toString();\n }\n : undefined,\n });\n\n if (isCancel(result)) {\n cancel('Operation cancelled');\n return exitSync(0);\n }\n\n return result;\n}\n\nexport async function promptLocale({\n message,\n defaultValue,\n customMapping,\n}: {\n message: string;\n defaultValue?: string;\n customMapping?: CustomMapping;\n}) {\n if (shouldUseInkPrompts()) {\n return (\n (await runInkPrompt<string>((prompts) =>\n prompts.inkPromptLocale({ message, defaultValue, customMapping })\n )) ?? ''\n );\n }\n\n return promptText({\n message,\n defaultValue,\n validate: (value) => validateLocale(value, customMapping),\n });\n}\n\nexport async function promptLocaleList({\n message,\n defaultValue,\n required = true,\n customMapping,\n}: {\n message: string;\n defaultValue?: string[];\n required?: boolean;\n customMapping?: CustomMapping;\n}) {\n if (shouldUseInkPrompts()) {\n return (\n (await runInkPrompt<string[]>((prompts) =>\n prompts.inkPromptLocaleMulti({\n message,\n defaultValue,\n required,\n customMapping,\n })\n )) ?? []\n );\n }\n\n return promptText({\n message,\n defaultValue: defaultValue?.join(' '),\n validate: (value) => validateLocaleList(value, customMapping),\n }).then(parseLocaleList);\n}\n\nexport async function promptGlobPatterns({\n label,\n message,\n defaultValue,\n}: {\n label: string;\n message: string;\n defaultValue?: string;\n}) {\n if (shouldUseInkPrompts()) {\n return (\n (await runInkPrompt<string>((prompts) =>\n prompts.inkPromptGlob({ label, message, defaultValue })\n )) ?? ''\n );\n }\n\n return promptText({\n message,\n defaultValue,\n });\n}\n\nexport async function promptSelect<T>({\n message,\n options,\n defaultValue,\n}: {\n message: string;\n options: Array<{ value: T; label: string; hint?: string }>;\n defaultValue?: T;\n}) {\n if (shouldUseInkPrompts()) {\n return (await runInkPrompt<T>((prompts) =>\n prompts.inkPromptSelect({ message, options, defaultValue })\n )) as T;\n }\n\n // Convert options to the format expected by clack\n const clackOptions = options.map((opt) => ({\n value: opt.value,\n label: opt.label,\n hint: opt.hint,\n })) as Option<T>[];\n\n const result = await select({\n message,\n options: clackOptions,\n initialValue: defaultValue,\n });\n\n if (isCancel(result)) {\n cancel('Operation cancelled');\n return exitSync(0);\n }\n\n return result as T;\n}\n\nexport async function promptMultiSelect<T extends string>({\n message,\n options,\n required = true,\n}: {\n message: string;\n options: Array<{ value: T; label: string; hint?: string }>;\n required?: boolean;\n}) {\n if (shouldUseInkPrompts()) {\n return (\n (await runInkPrompt<Array<T>>((prompts) =>\n prompts.inkPromptMultiSelect({\n message,\n options,\n required,\n })\n )) ?? []\n );\n }\n\n // Convert options to the format expected by clack\n const clackOptions = options.map((opt) => ({\n value: opt.value,\n label: opt.label,\n hint: opt.hint,\n })) as Option<T>[];\n\n const result = await multiselect({\n message,\n options: clackOptions,\n required,\n });\n\n if (isCancel(result)) {\n cancel('Operation cancelled');\n return exitSync(0);\n }\n\n return result as Array<T>;\n}\n\nexport async function promptConfirm({\n message,\n defaultValue = true,\n cancelMessage = 'Operation cancelled',\n}: {\n message: string;\n defaultValue?: boolean;\n cancelMessage?: string;\n}) {\n if (shouldUseInkPrompts()) {\n return (\n (await runInkPrompt<boolean>(\n (prompts) => prompts.inkPromptConfirm({ message, defaultValue }),\n cancelMessage\n )) ?? defaultValue\n );\n }\n\n const result = await confirm({\n message,\n initialValue: defaultValue,\n });\n\n if (isCancel(result)) {\n cancel(cancelMessage);\n return exitSync(0);\n }\n\n return result;\n}\n\n// Warning display functions\nexport function warnApiKeyInConfig(optionsFilepath: string) {\n logger.warn(\n `Found ${chalk.cyan('apiKey')} in \"${chalk.green(optionsFilepath)}\". ` +\n chalk.white(\n 'Your API key is exposed! Remove it from the file and include it as an environment variable.'\n )\n );\n}\n\nexport function warnVariableProp(\n file: string,\n attrName: string,\n value: string\n) {\n logger.warn(\n `Found ${chalk.green('<T>')} component in ${chalk.cyan(file)} with variable ${attrName}: \"${chalk.white(value)}\". ` +\n `Change \"${attrName}\" to ensure this content is translated.`\n );\n}\n\nexport function warnNoId(file: string) {\n logger.warn(\n `Found ${chalk.green('<T>')} component in ${chalk.cyan(file)} with no id. ` +\n chalk.white('Add an id to ensure the content is translated.')\n );\n}\n\nexport function warnHasUnwrappedExpression(\n file: string,\n id: string,\n unwrappedExpressions: string[]\n) {\n logger.warn(\n `${chalk.green('<T>')} with id \"${id}\" in ${chalk.cyan(file)} has children: ${unwrappedExpressions.join(', ')} that could change at runtime. ` +\n chalk.white('Use a variable component like ') +\n chalk.green('<Var>') +\n chalk.white(' (') +\n chalk.blue('https://generaltranslation.com/docs') +\n chalk.white(') to translate this properly.')\n );\n}\n\nexport function warnTemplateLiteral(file: string, value: string) {\n logger.warn(\n `Found template literal with quasis (${value}) in ${chalk.cyan(file)}. ` +\n chalk.white(\n 'Change the template literal to a string to ensure this content is translated.'\n )\n );\n}\n\nexport function warnTernary(file: string) {\n logger.warn(\n `Found ternary expression in ${chalk.cyan(file)}. ` +\n chalk.white('A Branch component may be more appropriate here.')\n );\n}\n\nexport function warnDeprecatedField(\n deprecatedField: string,\n replacement: string\n) {\n logger.warn(\n `${chalk.green(deprecatedField)} is deprecated. ` +\n chalk.white(`Use ${chalk.green(replacement)} instead.`)\n );\n}\n\n/**\n * Helper: Log all collected files\n */\nexport function logCollectedFiles(\n files: Pick<FileToUpload, 'fileName'>[],\n inlineComponents?: number,\n inlineLibrary?: InlineLibrary\n): void {\n const elementLabel = getInlineElementsLabel(inlineLibrary);\n logger.message(\n chalk.cyan('Files found in project:') +\n '\\n' +\n files\n .map((file) => {\n if (file.fileName === TEMPLATE_FILE_NAME) {\n return `- <${elementLabel}>${inlineComponents ? ` (${inlineComponents})` : ''}`;\n }\n return `- ${file.fileName}`;\n })\n .join('\\n')\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"logging.js","names":[],"sources":["../../src/console/logging.ts"],"sourcesContent":["import {\n text,\n select,\n confirm,\n isCancel,\n cancel,\n multiselect,\n autocomplete,\n autocompleteMultiselect,\n} from '@clack/prompts';\nimport type { Option } from '@clack/prompts';\nimport chalk from 'chalk';\nimport { createDiagnosticMessage } from 'generaltranslation/internal';\nimport { getCLIVersion } from '../utils/packageJson.js';\nimport { logger } from './logger.js';\nimport { TEMPLATE_FILE_NAME } from '../utils/constants.js';\nimport type { CustomMapping, FileToUpload } from 'generaltranslation/types';\nimport { parseTypedLocale } from './promptParsing.js';\nimport { getFilteredLocaleOptions } from './localeOptions.js';\nimport {\n getInlineElementsLabel,\n type InlineLibrary,\n} from '../types/libraries.js';\n\nfunction exitIfCancelled<T>(\n result: T | symbol,\n message = 'Operation cancelled'\n): T {\n if (isCancel(result)) {\n cancel(message);\n return exitSync(0);\n }\n return result as T;\n}\n\n/**\n * Strip ANSI escape codes from a string (e.g., chalk color codes)\n */\nexport function stripAnsi(str: string): string {\n // eslint-disable-next-line no-control-regex, no-useless-escape\n return str.replace(/\\x1B\\[[0-9;]*m/g, '');\n}\n\nlet lastExitError: string | undefined;\n\n/** The message of the last logErrorAndExit call, for exit-time reporting. */\nexport function getLastExitError(): string | undefined {\n return lastExitError;\n}\n\nexport function logErrorAndExit(message: string): never {\n lastExitError = message;\n logger.error(message);\n return exitSync(1);\n}\n\nexport function exitSync(code: number): never {\n // Flush logs before exit\n logger.flush();\n process.exit(code);\n}\n\n// GT specific logging\nexport function displayHeader(introString?: string) {\n // The ASCII banner is pure chatter and writes to console directly, bypassing\n // the logger, so gate it here to honor --quiet. startCommand stays outside\n // the gate: it is quiet-aware itself and must still run so the file log\n // gets its [START] marker.\n if (!logger.isQuiet()) {\n displayAsciiTitle();\n displayInitializingText();\n }\n\n if (introString) {\n logger.startCommand(introString);\n }\n}\n\nfunction displayAsciiTitle() {\n // eslint-disable-next-line no-console\n console.log(\n chalk.cyan(\n `\\n @@@@@@@@@@@@@@@@@@@@@@\n @@ @@@@@@@@@@@@ @@@@@@@\n @@ @@@ @ @\n@@ @@ @ @\n@@ @ @@@@@ @ @\n@@ @@ @@@ @ @ @\n @@ @@ @ @ @ @\n @@ @@@@@@@ @ @ @\n @@@@@@@@@ @ @`\n )\n );\n}\n\nfunction displayInitializingText() {\n const version = getCLIVersion();\n // eslint-disable-next-line no-console\n console.log(\n `\\n${chalk.bold.blue('General Translation, Inc.')}\n${chalk.dim('https://generaltranslation.com/docs')}\n${chalk.dim(`CLI Version: ${version}\\n`)}`\n );\n}\n\nexport function displayProjectId(projectId: string) {\n logger.message(\n chalk.dim(`Project ID: ${chalk.bold(projectId)}`),\n chalk.cyan('~')\n );\n}\n\nexport function displayResolvedPaths(resolvedPaths: [string, string][]) {\n const paths = resolvedPaths.map(([key, resolvedPath]) => {\n return chalk.dim(`'${chalk.white(key)}' → '${chalk.green(resolvedPath)}'`);\n });\n logger.step(`Resolved path aliases:\\n${paths.join('\\n')}`);\n}\n\nexport function displayCreatedConfigFile(configFilepath: string) {\n logger.step(`Created config file ${chalk.cyan(configFilepath)}`);\n}\n\nexport function displayUpdatedConfigFile(configFilepath: string) {\n logger.success(`Updated config file ${chalk.cyan(configFilepath)}`);\n}\n\nexport function displayUpdatedVersionsFile(versionFilepath: string) {\n logger.success(`Updated versions file ${chalk.cyan(versionFilepath)}`);\n}\n\n// Input prompts\nexport async function promptText({\n message,\n defaultValue,\n validate,\n}: {\n message: string;\n defaultValue?: string;\n validate?: (value: string) => boolean | string;\n}) {\n const result = await text({\n message,\n placeholder: defaultValue,\n defaultValue,\n validate: validate\n ? (value) => {\n // Clack applies defaultValue after validation; check what Enter returns.\n const validation = validate(value || defaultValue || '');\n return validation === true ? undefined : validation.toString();\n }\n : undefined,\n });\n return exitIfCancelled(result);\n}\n\ntype LocalePromptContext = {\n userInput: string;\n selectedValues: string[];\n focusedValue?: string;\n};\n\n/**\n * Clack keeps the focused option while it still matches a new search, so\n * typing `fr` could leave `af` (Afrikaans) focused. Refocus the top-ranked\n * option whenever the search changes.\n */\nfunction searchableLocaleOptions(\n getOptions: (query: string, selected: string[]) => Option<string>[]\n) {\n let lastQuery = '';\n return function (this: LocalePromptContext) {\n const query = this.userInput ?? '';\n if (query !== lastQuery) {\n lastQuery = query;\n this.focusedValue = undefined;\n }\n return getOptions(query, this.selectedValues ?? []);\n };\n}\n\n/**\n * Searchable locale options: supported locales ranked by the query, the\n * configured customMapping aliases, and any other valid locale typed or\n * already selected, so aliases and custom tags stay selectable.\n */\nexport function getLocalePromptOptions(\n query: string,\n selected: string[] = [],\n customMapping?: CustomMapping\n): Option<string>[] {\n const normalizedQuery = query.trim().toLowerCase();\n const supported = getFilteredLocaleOptions(query).map((option) => ({\n value: option.code,\n label: option.label,\n }));\n const known = new Set(supported.map((option) => option.value));\n const aliases = Object.keys(customMapping ?? {})\n .filter((alias) => alias.toLowerCase().includes(normalizedQuery))\n .map((alias) => ({ value: alias, label: alias, hint: 'custom mapping' }));\n const typed = parseTypedLocale(query, customMapping);\n const extras = [...selected, ...(typed ? [typed] : [])]\n .filter(\n (locale) =>\n !known.has(locale) && !aliases.some((alias) => alias.value === locale)\n )\n .map((locale) => ({ value: locale, label: locale }));\n return [\n ...new Map(\n [...aliases, ...extras, ...supported].map((option) => [\n option.value,\n option,\n ])\n ).values(),\n ].sort(\n (a, b) =>\n Number(b.value.toLowerCase() === normalizedQuery) -\n Number(a.value.toLowerCase() === normalizedQuery)\n );\n}\n\n// Body-only: Clack shows it inline under the prompt.\nconst noLocaleSelectedError = createDiagnosticMessage({\n whatHappened: 'No locale matches the search',\n fix: 'Change the search and select a locale from the list',\n});\n\nexport async function promptLocale({\n message,\n defaultValue,\n customMapping,\n}: {\n message: string;\n defaultValue?: string;\n customMapping?: CustomMapping;\n}) {\n const result = await autocomplete<string>({\n message,\n placeholder: 'Type to search locales',\n initialValue: defaultValue,\n options: searchableLocaleOptions((query) =>\n getLocalePromptOptions(query, [], customMapping)\n ),\n // Enter with no matching option submits nothing; keep asking.\n validate: (value) => (value ? undefined : noLocaleSelectedError),\n });\n return exitIfCancelled(result);\n}\n\nexport async function promptLocaleList({\n message,\n defaultValue,\n required = true,\n customMapping,\n}: {\n message: string;\n defaultValue?: string[];\n required?: boolean;\n customMapping?: CustomMapping;\n}) {\n const result = await autocompleteMultiselect<string>({\n message,\n placeholder: 'Type to search, Tab or Space to select',\n initialValues: defaultValue,\n required,\n // Clack only preselects defaults present in the first options list, so\n // keep custom default tags listed even before they are selected.\n options: searchableLocaleOptions((query, selected) =>\n getLocalePromptOptions(\n query,\n [...(defaultValue ?? []), ...selected],\n customMapping\n )\n ),\n });\n return exitIfCancelled(result);\n}\n\nexport async function promptGlobPatterns(options: {\n label: string;\n message: string;\n defaultValue?: string;\n validate?: (value: string) => boolean | string;\n}) {\n return promptText(options);\n}\n\nexport async function promptSelect<T>({\n message,\n options,\n defaultValue,\n}: {\n message: string;\n options: Array<{ value: T; label: string; hint?: string }>;\n defaultValue?: T;\n}) {\n const result = await select({\n message,\n options: options as Option<T>[],\n initialValue: defaultValue,\n });\n return exitIfCancelled(result);\n}\n\nexport async function promptMultiSelect<T extends string>({\n message,\n options,\n required = true,\n}: {\n message: string;\n options: Array<{ value: T; label: string; hint?: string }>;\n required?: boolean;\n}) {\n const result = await multiselect({\n message,\n options: options as Option<T>[],\n required,\n });\n return exitIfCancelled(result);\n}\n\nexport async function promptConfirm({\n message,\n defaultValue = true,\n cancelMessage = 'Operation cancelled',\n}: {\n message: string;\n defaultValue?: boolean;\n cancelMessage?: string;\n}) {\n const result = await confirm({\n message,\n initialValue: defaultValue,\n });\n return exitIfCancelled(result, cancelMessage);\n}\n\n// Warning display functions\nexport function warnApiKeyInConfig(optionsFilepath: string) {\n logger.warn(\n `Found ${chalk.cyan('apiKey')} in \"${chalk.green(optionsFilepath)}\". ` +\n chalk.white(\n 'Your API key is exposed! Remove it from the file and include it as an environment variable.'\n )\n );\n}\n\nexport function warnVariableProp(\n file: string,\n attrName: string,\n value: string\n) {\n logger.warn(\n `Found ${chalk.green('<T>')} component in ${chalk.cyan(file)} with variable ${attrName}: \"${chalk.white(value)}\". ` +\n `Change \"${attrName}\" to ensure this content is translated.`\n );\n}\n\nexport function warnNoId(file: string) {\n logger.warn(\n `Found ${chalk.green('<T>')} component in ${chalk.cyan(file)} with no id. ` +\n chalk.white('Add an id to ensure the content is translated.')\n );\n}\n\nexport function warnHasUnwrappedExpression(\n file: string,\n id: string,\n unwrappedExpressions: string[]\n) {\n logger.warn(\n `${chalk.green('<T>')} with id \"${id}\" in ${chalk.cyan(file)} has children: ${unwrappedExpressions.join(', ')} that could change at runtime. ` +\n chalk.white('Use a variable component like ') +\n chalk.green('<Var>') +\n chalk.white(' (') +\n chalk.blue('https://generaltranslation.com/docs') +\n chalk.white(') to translate this properly.')\n );\n}\n\nexport function warnTemplateLiteral(file: string, value: string) {\n logger.warn(\n `Found template literal with quasis (${value}) in ${chalk.cyan(file)}. ` +\n chalk.white(\n 'Change the template literal to a string to ensure this content is translated.'\n )\n );\n}\n\nexport function warnTernary(file: string) {\n logger.warn(\n `Found ternary expression in ${chalk.cyan(file)}. ` +\n chalk.white('A Branch component may be more appropriate here.')\n );\n}\n\nexport function warnDeprecatedField(\n deprecatedField: string,\n replacement: string\n) {\n logger.warn(\n `${chalk.green(deprecatedField)} is deprecated. ` +\n chalk.white(`Use ${chalk.green(replacement)} instead.`)\n );\n}\n\n/**\n * Helper: Log all collected files\n */\nexport function logCollectedFiles(\n files: Pick<FileToUpload, 'fileName'>[],\n inlineComponents?: number,\n inlineLibrary?: InlineLibrary\n): void {\n const elementLabel = getInlineElementsLabel(inlineLibrary);\n logger.message(\n chalk.cyan('Files found in project:') +\n '\\n' +\n files\n .map((file) => {\n if (file.fileName === TEMPLATE_FILE_NAME) {\n return `- <${elementLabel}>${inlineComponents ? ` (${inlineComponents})` : ''}`;\n }\n return `- ${file.fileName}`;\n })\n .join('\\n')\n );\n}\n"],"mappings":"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"}
@@ -6,7 +6,6 @@ import { fileEncodingSkipReason, lottieExpressionsError, xcstringsSourceLanguage
6
6
  import { SUPPORTED_FILE_EXTENSIONS } from "./supportedFiles.js";
7
7
  import { CONFIG_FILE_TYPE_TO_FILE_FORMAT, getTransformFormatProperty } from "./transformFormat.js";
8
8
  import { api } from "../../utils/api.js";
9
- import { determineLibrary } from "../../fs/determineFramework/index.js";
10
9
  import { readFileContent } from "../../fs/fileContent.js";
11
10
  import { flattenJson } from "../json/flattenJson.js";
12
11
  import { recordWarning } from "../../state/translateWarnings.js";
@@ -18,6 +17,7 @@ import { resolveMintlifyRefs, shouldResolveRefs } from "../../utils/resolveMintl
18
17
  import { storeRefMap } from "../../state/mintlifyRefMap.js";
19
18
  import { validateYamlSchema } from "../yaml/utils.js";
20
19
  import parseYaml from "../yaml/parseYaml.js";
20
+ import { determineLibrary } from "../../fs/determineFramework/index.js";
21
21
  import { preprocessContent } from "./preprocessContent.js";
22
22
  import { parseKeyedMetadata } from "../parseKeyedMetadata.js";
23
23
  import YAML from "yaml";
@@ -1,4 +1,7 @@
1
1
  export declare const SUPPORTED_FILE_EXTENSIONS: readonly ["json", "pot", "mdx", "md", "ts", "js", "yaml", "html", "txt", "twilioContentJson", "lottie", "dotStrings", "dotStringsdict", "androidStrings", "xcstrings", "srt"];
2
+ /** The formats setup offers: an intentional subset of the supported ones. */
3
+ export declare const SETUP_FILE_FORMATS: readonly ["json", "md", "mdx", "ts", "js", "yaml"];
4
+ export type SetupFileFormat = (typeof SETUP_FILE_FORMATS)[number];
2
5
  export declare const FILE_EXT_TO_EXT_LABEL: {
3
6
  json: string;
4
7
  pot: string;
@@ -17,6 +17,15 @@ const SUPPORTED_FILE_EXTENSIONS = [
17
17
  "xcstrings",
18
18
  "srt"
19
19
  ];
20
+ /** The formats setup offers: an intentional subset of the supported ones. */
21
+ const SETUP_FILE_FORMATS = [
22
+ "json",
23
+ "md",
24
+ "mdx",
25
+ "ts",
26
+ "js",
27
+ "yaml"
28
+ ];
20
29
  const FILE_EXT_TO_EXT_LABEL = {
21
30
  json: "JSON",
22
31
  pot: "POT",
@@ -36,6 +45,6 @@ const FILE_EXT_TO_EXT_LABEL = {
36
45
  srt: "SubRip subtitles (.srt)"
37
46
  };
38
47
  //#endregion
39
- export { FILE_EXT_TO_EXT_LABEL, SUPPORTED_FILE_EXTENSIONS };
48
+ export { FILE_EXT_TO_EXT_LABEL, SETUP_FILE_FORMATS, SUPPORTED_FILE_EXTENSIONS };
40
49
 
41
50
  //# sourceMappingURL=supportedFiles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"supportedFiles.js","names":[],"sources":["../../../src/formats/files/supportedFiles.ts"],"sourcesContent":["export const SUPPORTED_FILE_EXTENSIONS = [\n 'json',\n 'pot',\n 'mdx',\n 'md',\n 'ts',\n 'js',\n 'yaml',\n 'html',\n 'txt',\n 'twilioContentJson',\n 'lottie',\n 'dotStrings',\n 'dotStringsdict',\n 'androidStrings',\n 'xcstrings',\n 'srt',\n] as const;\n\nexport const FILE_EXT_TO_EXT_LABEL = {\n json: 'JSON',\n pot: 'POT',\n mdx: 'MDX',\n md: 'Markdown',\n ts: 'TypeScript',\n js: 'JavaScript',\n yaml: 'YAML',\n html: 'HTML',\n txt: 'Text',\n twilioContentJson: 'Twilio Content JSON',\n lottie: 'Lottie',\n dotStrings: '.strings',\n dotStringsdict: '.stringsdict',\n androidStrings: 'Android strings.xml',\n xcstrings: '.xcstrings',\n srt: 'SubRip subtitles (.srt)',\n};\n"],"mappings":";AAAA,MAAa,4BAA4B;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAa,wBAAwB;CACnC,MAAM;CACN,KAAK;CACL,KAAK;CACL,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM;CACN,MAAM;CACN,KAAK;CACL,mBAAmB;CACnB,QAAQ;CACR,YAAY;CACZ,gBAAgB;CAChB,gBAAgB;CAChB,WAAW;CACX,KAAK;CACN"}
1
+ {"version":3,"file":"supportedFiles.js","names":[],"sources":["../../../src/formats/files/supportedFiles.ts"],"sourcesContent":["export const SUPPORTED_FILE_EXTENSIONS = [\n 'json',\n 'pot',\n 'mdx',\n 'md',\n 'ts',\n 'js',\n 'yaml',\n 'html',\n 'txt',\n 'twilioContentJson',\n 'lottie',\n 'dotStrings',\n 'dotStringsdict',\n 'androidStrings',\n 'xcstrings',\n 'srt',\n] as const;\n\n/** The formats setup offers: an intentional subset of the supported ones. */\nexport const SETUP_FILE_FORMATS = [\n 'json',\n 'md',\n 'mdx',\n 'ts',\n 'js',\n 'yaml',\n] as const satisfies readonly (typeof SUPPORTED_FILE_EXTENSIONS)[number][];\nexport type SetupFileFormat = (typeof SETUP_FILE_FORMATS)[number];\n\nexport const FILE_EXT_TO_EXT_LABEL = {\n json: 'JSON',\n pot: 'POT',\n mdx: 'MDX',\n md: 'Markdown',\n ts: 'TypeScript',\n js: 'JavaScript',\n yaml: 'YAML',\n html: 'HTML',\n txt: 'Text',\n twilioContentJson: 'Twilio Content JSON',\n lottie: 'Lottie',\n dotStrings: '.strings',\n dotStringsdict: '.stringsdict',\n androidStrings: 'Android strings.xml',\n xcstrings: '.xcstrings',\n srt: 'SubRip subtitles (.srt)',\n};\n"],"mappings":";AAAA,MAAa,4BAA4B;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;AAGD,MAAa,qBAAqB;CAChC;CACA;CACA;CACA;CACA;CACA;CACD;AAGD,MAAa,wBAAwB;CACnC,MAAM;CACN,KAAK;CACL,KAAK;CACL,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM;CACN,MAAM;CACN,KAAK;CACL,mBAAmB;CACnB,QAAQ;CACR,YAAY;CACZ,gBAAgB;CAChB,gBAAgB;CAChB,WAAW;CACX,KAAK;CACN"}
@@ -2,8 +2,8 @@ import { logger } from "../../console/logger.js";
2
2
  import { exitSync } from "../../console/logging.js";
3
3
  import { validateYamlSchema } from "./utils.js";
4
4
  import { applyTransformations } from "../json/mergeJson.js";
5
- import JSONPointer from "jsonpointer";
6
5
  import YAML from "yaml";
6
+ import JSONPointer from "jsonpointer";
7
7
  //#region src/formats/yaml/mergeYaml.ts
8
8
  function mergeYaml(originalContent, inputPath, options, targets, defaultLocale) {
9
9
  const yamlSchema = validateYamlSchema(options, inputPath);
@@ -1 +1,3 @@
1
+ /** `--config` accepts a path with or without its `.json` extension. */
2
+ export declare function withJsonExtension(configPath: string): string;
1
3
  export declare function loadConfig(filepath: string): Record<string, unknown>;
@@ -1,5 +1,9 @@
1
1
  import fs from "node:fs";
2
2
  //#region src/fs/config/loadConfig.ts
3
+ /** `--config` accepts a path with or without its `.json` extension. */
4
+ function withJsonExtension(configPath) {
5
+ return configPath.endsWith(".json") ? configPath : `${configPath}.json`;
6
+ }
3
7
  function loadConfig(filepath) {
4
8
  try {
5
9
  return JSON.parse(fs.readFileSync(filepath, "utf-8"));
@@ -8,6 +12,6 @@ function loadConfig(filepath) {
8
12
  }
9
13
  }
10
14
  //#endregion
11
- export { loadConfig };
15
+ export { loadConfig, withJsonExtension };
12
16
 
13
17
  //# sourceMappingURL=loadConfig.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"loadConfig.js","names":[],"sources":["../../../src/fs/config/loadConfig.ts"],"sourcesContent":["import fs from 'node:fs';\n\nexport function loadConfig(filepath: string): Record<string, unknown> {\n try {\n return JSON.parse(fs.readFileSync(filepath, 'utf-8')) as Record<\n string,\n unknown\n >;\n } catch {\n return {};\n }\n}\n"],"mappings":";;AAEA,SAAgB,WAAW,UAA2C;AACpE,KAAI;AACF,SAAO,KAAK,MAAM,GAAG,aAAa,UAAU,QAAQ,CAAC;SAI/C;AACN,SAAO,EAAE"}
1
+ {"version":3,"file":"loadConfig.js","names":[],"sources":["../../../src/fs/config/loadConfig.ts"],"sourcesContent":["import fs from 'node:fs';\n\n/** `--config` accepts a path with or without its `.json` extension. */\nexport function withJsonExtension(configPath: string): string {\n return configPath.endsWith('.json') ? configPath : `${configPath}.json`;\n}\n\nexport function loadConfig(filepath: string): Record<string, unknown> {\n try {\n return JSON.parse(fs.readFileSync(filepath, 'utf-8')) as Record<\n string,\n unknown\n >;\n } catch {\n return {};\n }\n}\n"],"mappings":";;;AAGA,SAAgB,kBAAkB,YAA4B;AAC5D,QAAO,WAAW,SAAS,QAAQ,GAAG,aAAa,GAAG,WAAW;;AAGnE,SAAgB,WAAW,UAA2C;AACpE,KAAI;AACF,SAAO,KAAK,MAAM,GAAG,aAAa,UAAU,QAAQ,CAAC;SAI/C;AACN,SAAO,EAAE"}
@@ -1,12 +1,5 @@
1
1
  import { FilesOptions, SupportedFrameworks } from '../../types/index.js';
2
- /**
3
- * Checks if the config file exists.
4
- * If yes, make sure make sure projectId is correct
5
- * If not, creates a new JSON file at the given filepath and writes the provided config object to it.
6
- * @param {string} configFilepath - The path to the config file.
7
- * @param {Record<string, any>} configObject - The config object to write if the file does not exist.
8
- */
9
- export declare function createOrUpdateConfig(configFilepath: string, options: {
2
+ export type SetupConfigUpdate = {
10
3
  projectId?: string;
11
4
  defaultLocale?: string;
12
5
  locales?: string[];
@@ -17,4 +10,20 @@ export declare function createOrUpdateConfig(configFilepath: string, options: {
17
10
  publish?: boolean;
18
11
  /** Removes stale global CDN intent when the selected runtime forbids it. */
19
12
  clearPublish?: boolean;
20
- }): Promise<string>;
13
+ /** File format entries deselected by an explicit format list. */
14
+ removeFiles?: string[];
15
+ /** Drops files.gt.output after an explicit switch to CDN storage. */
16
+ clearGtOutput?: boolean;
17
+ };
18
+ /**
19
+ * The config setup writes: the existing content with the update applied.
20
+ * Unrelated fields and per-format options are kept; locales are replaced.
21
+ */
22
+ export declare function mergeSetupConfig(oldContent: Record<string, unknown>, options: SetupConfigUpdate): Record<string, unknown>;
23
+ /**
24
+ * Creates the config file, or applies the update to the existing one.
25
+ * @param {string} configFilepath - The path to the config file.
26
+ * @param {SetupConfigUpdate} options - The values setup resolved.
27
+ * @throws When the existing file is not valid JSON or cannot be written.
28
+ */
29
+ export declare function createOrUpdateConfig(configFilepath: string, options: SetupConfigUpdate): Promise<string>;
@@ -1,59 +1,79 @@
1
- import { logger } from "../../console/logger.js";
2
1
  import { GT_CONFIG_SCHEMA_URL } from "../../utils/constants.js";
3
2
  import { displayCreatedConfigFile } from "../../console/logging.js";
3
+ import { createDiagnosticMessage, formatDiagnosticErrorDetails } from "generaltranslation/internal";
4
4
  import fs from "node:fs";
5
5
  //#region src/fs/config/setupConfig.ts
6
+ function asObject(value) {
7
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
8
+ }
6
9
  /**
7
- * Checks if the config file exists.
8
- * If yes, make sure make sure projectId is correct
9
- * If not, creates a new JSON file at the given filepath and writes the provided config object to it.
10
- * @param {string} configFilepath - The path to the config file.
11
- * @param {Record<string, any>} configObject - The config object to write if the file does not exist.
10
+ * The config setup writes: the existing content with the update applied.
11
+ * Unrelated fields and per-format options are kept; locales are replaced.
12
12
  */
13
- async function createOrUpdateConfig(configFilepath, options) {
13
+ function mergeSetupConfig(oldContent, options) {
14
14
  const newContent = {
15
15
  ...options.projectId && { projectId: options.projectId },
16
16
  ...options.defaultLocale && { defaultLocale: options.defaultLocale },
17
17
  ...options.src && { src: options.src },
18
- ...options.files && { files: options.files },
19
18
  ...options.framework && { framework: options.framework },
20
19
  ...options.baseUrl && { baseUrl: options.baseUrl },
21
20
  ...options.publish && { publish: options.publish }
22
21
  };
22
+ const mergedContent = {
23
+ $schema: GT_CONFIG_SCHEMA_URL,
24
+ ...oldContent,
25
+ ...newContent
26
+ };
27
+ if (options.clearPublish) delete mergedContent.publish;
28
+ if (options.files || options.removeFiles?.length || options.clearGtOutput) {
29
+ const oldFiles = asObject(oldContent.files);
30
+ const files = {
31
+ ...oldFiles,
32
+ ...options.files
33
+ };
34
+ const gt = {
35
+ ...asObject(oldFiles.gt),
36
+ ...options.files?.gt
37
+ };
38
+ if (options.clearGtOutput) delete gt.output;
39
+ if (Object.keys(gt).length > 0) files.gt = gt;
40
+ else delete files.gt;
41
+ for (const format of options.removeFiles ?? []) delete files[format];
42
+ if (Object.keys(files).length > 0) mergedContent.files = files;
43
+ else delete mergedContent.files;
44
+ }
45
+ if (options.locales) mergedContent.locales = options.locales;
46
+ return mergedContent;
47
+ }
48
+ /**
49
+ * Creates the config file, or applies the update to the existing one.
50
+ * @param {string} configFilepath - The path to the config file.
51
+ * @param {SetupConfigUpdate} options - The values setup resolved.
52
+ * @throws When the existing file is not valid JSON or cannot be written.
53
+ */
54
+ async function createOrUpdateConfig(configFilepath, options) {
23
55
  try {
24
56
  let oldContent = {};
25
57
  if (fs.existsSync(configFilepath)) {
26
58
  const parsed = JSON.parse(await fs.promises.readFile(configFilepath, "utf-8"));
27
- oldContent = typeof parsed === "object" && parsed !== null ? parsed : {};
28
- }
29
- const mergedContent = {
30
- $schema: GT_CONFIG_SCHEMA_URL,
31
- ...oldContent,
32
- ...newContent
33
- };
34
- if (options.clearPublish) delete mergedContent.publish;
35
- if (options.files) {
36
- const oldFiles = oldContent.files && typeof oldContent.files === "object" ? oldContent.files : {};
37
- const oldGt = oldFiles.gt && typeof oldFiles.gt === "object" ? oldFiles.gt : {};
38
- mergedContent.files = {
39
- ...oldFiles,
40
- ...options.files,
41
- ...options.files.gt && { gt: {
42
- ...oldGt,
43
- ...options.files.gt
44
- } }
45
- };
59
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("the file does not contain a JSON object");
60
+ oldContent = parsed;
46
61
  }
47
- if (options.locales) mergedContent.locales = mergedContent.locales ? [...new Set([...mergedContent.locales, ...options.locales])] : options.locales;
48
- const mergedJsonContent = JSON.stringify(mergedContent, null, 2);
62
+ const mergedJsonContent = JSON.stringify(mergeSetupConfig(oldContent, options), null, 2);
49
63
  await fs.promises.writeFile(configFilepath, mergedJsonContent, "utf-8");
50
64
  displayCreatedConfigFile(configFilepath);
51
65
  } catch (error) {
52
- logger.error(`An error occurred while updating ${configFilepath}: ${error}`);
66
+ throw new Error(createDiagnosticMessage({
67
+ source: "gt",
68
+ severity: "Error",
69
+ whatHappened: `Could not update ${configFilepath}`,
70
+ details: formatDiagnosticErrorDetails(error),
71
+ fix: "Fix or remove the file, then rerun the command"
72
+ }));
53
73
  }
54
74
  return configFilepath;
55
75
  }
56
76
  //#endregion
57
- export { createOrUpdateConfig };
77
+ export { createOrUpdateConfig, mergeSetupConfig };
58
78
 
59
79
  //# sourceMappingURL=setupConfig.js.map