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.
- package/CHANGELOG.md +28 -0
- package/dist/auth/errors.d.ts +1 -0
- package/dist/auth/errors.js +12 -2
- package/dist/auth/errors.js.map +1 -1
- package/dist/auth/interactiveLogin.d.ts +2 -0
- package/dist/auth/interactiveLogin.js +22 -0
- package/dist/auth/interactiveLogin.js.map +1 -0
- package/dist/auth/oauth.js +2 -2
- package/dist/auth/oauth.js.map +1 -1
- package/dist/cli/base.d.ts +22 -4
- package/dist/cli/base.js +328 -223
- package/dist/cli/base.js.map +1 -1
- package/dist/cli/commands/api.js +15 -16
- package/dist/cli/commands/api.js.map +1 -1
- package/dist/cli/node.js +1 -1
- package/dist/cli/node.js.map +1 -1
- package/dist/cli/react.js +1 -1
- package/dist/cli/react.js.map +1 -1
- package/dist/cli/vue.js +2 -2
- package/dist/cli/vue.js.map +1 -1
- package/dist/config/generateSettings.d.ts +2 -0
- package/dist/config/generateSettings.js +13 -6
- package/dist/config/generateSettings.js.map +1 -1
- package/dist/config/resolveConfig.js +0 -4
- package/dist/config/resolveConfig.js.map +1 -1
- package/dist/config/validateSettings.js +5 -4
- package/dist/config/validateSettings.js.map +1 -1
- package/dist/console/{inkLocaleData.d.ts → localeOptions.d.ts} +7 -2
- package/dist/console/{inkLocaleData.js → localeOptions.js} +3 -8
- package/dist/console/localeOptions.js.map +1 -0
- package/dist/console/logger.d.ts +7 -0
- package/dist/console/logger.js +48 -85
- package/dist/console/logger.js.map +1 -1
- package/dist/console/logging.d.ts +11 -1
- package/dist/console/logging.js +88 -100
- package/dist/console/logging.js.map +1 -1
- package/dist/formats/files/aggregateFiles.js +1 -1
- package/dist/formats/files/supportedFiles.d.ts +3 -0
- package/dist/formats/files/supportedFiles.js +10 -1
- package/dist/formats/files/supportedFiles.js.map +1 -1
- package/dist/formats/yaml/mergeYaml.js +1 -1
- package/dist/fs/config/loadConfig.d.ts +2 -0
- package/dist/fs/config/loadConfig.js +5 -1
- package/dist/fs/config/loadConfig.js.map +1 -1
- package/dist/fs/config/setupConfig.d.ts +18 -9
- package/dist/fs/config/setupConfig.js +51 -31
- package/dist/fs/config/setupConfig.js.map +1 -1
- package/dist/fs/createLoadTranslationsFile.d.ts +22 -1
- package/dist/fs/createLoadTranslationsFile.js +43 -20
- package/dist/fs/createLoadTranslationsFile.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/hooks/postProcess.d.ts +1 -2
- package/dist/hooks/postProcess.js +1 -1
- package/dist/hooks/postProcess.js.map +1 -1
- package/dist/locadex/setupFlow.d.ts +4 -1
- package/dist/locadex/setupFlow.js +5 -3
- package/dist/locadex/setupFlow.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/buildTools/index.d.ts +44 -0
- package/dist/setup/buildTools/index.js +10 -0
- package/dist/setup/buildTools/index.js.map +1 -0
- package/dist/setup/buildTools/vite.d.ts +2 -0
- package/dist/setup/buildTools/vite.js +61 -0
- package/dist/setup/buildTools/vite.js.map +1 -0
- package/dist/setup/developmentCredentials.d.ts +40 -5
- package/dist/setup/developmentCredentials.js +119 -37
- package/dist/setup/developmentCredentials.js.map +1 -1
- package/dist/setup/instructions/base.md +1 -1
- package/dist/setup/onboarding.d.ts +131 -0
- package/dist/setup/onboarding.js +289 -0
- package/dist/setup/onboarding.js.map +1 -0
- package/dist/setup/setupTarget.d.ts +1 -0
- package/dist/setup/setupTarget.js +52 -0
- package/dist/setup/setupTarget.js.map +1 -0
- package/dist/setup/setupViteSPA.d.ts +32 -1
- package/dist/setup/setupViteSPA.js +110 -20
- package/dist/setup/setupViteSPA.js.map +1 -1
- package/dist/setup/userInput.d.ts +12 -3
- package/dist/setup/userInput.js +27 -11
- package/dist/setup/userInput.js.map +1 -1
- package/dist/setup/wizard.d.ts +19 -3
- package/dist/setup/wizard.js +119 -105
- package/dist/setup/wizard.js.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +13 -1
- package/dist/types/index.js.map +1 -0
- package/dist/utils/credentials.d.ts +9 -6
- package/dist/utils/credentials.js +36 -22
- package/dist/utils/credentials.js.map +1 -1
- package/dist/utils/monorepoVersionCheck.d.ts +1 -1
- package/dist/utils/monorepoVersionCheck.js +5 -5
- package/dist/utils/monorepoVersionCheck.js.map +1 -1
- package/dist/utils/packageManager.js +13 -14
- package/dist/utils/packageManager.js.map +1 -1
- package/package.json +5 -9
- package/dist/console/inkFields.d.ts +0 -23
- package/dist/console/inkFields.js +0 -88
- package/dist/console/inkFields.js.map +0 -1
- package/dist/console/inkLayout.d.ts +0 -7
- package/dist/console/inkLayout.js +0 -140
- package/dist/console/inkLayout.js.map +0 -1
- package/dist/console/inkLocaleData.js.map +0 -1
- package/dist/console/inkPrompts.d.ts +0 -8
- package/dist/console/inkPrompts.js +0 -499
- package/dist/console/inkPrompts.js.map +0 -1
- package/dist/console/inkSession.d.ts +0 -3
- package/dist/console/inkSession.js +0 -42
- package/dist/console/inkSession.js.map +0 -1
- package/dist/console/inkTerminal.d.ts +0 -4
- package/dist/console/inkTerminal.js +0 -15
- package/dist/console/inkTerminal.js.map +0 -1
- package/dist/console/inkTheme.d.ts +0 -1
- package/dist/console/inkTheme.js +0 -6
- package/dist/console/inkTheme.js.map +0 -1
- package/dist/console/inkTypes.d.ts +0 -69
- package/dist/console/inkTypes.js +0 -1
- package/dist/console/inkUtils.d.ts +0 -24
- package/dist/console/inkUtils.js +0 -87
- package/dist/console/inkUtils.js.map +0 -1
- package/dist/console/terminalSession.d.ts +0 -5
- package/dist/console/terminalSession.js +0 -38
- package/dist/console/terminalSession.js.map +0 -1
package/dist/cli/vue.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue.js","names":[],"sources":["../../src/cli/vue.ts"],"sourcesContent":["import type { Command } from 'commander';\nimport type {
|
|
1
|
+
{"version":3,"file":"vue.js","names":[],"sources":["../../src/cli/vue.ts"],"sourcesContent":["import type { Command } from 'commander';\nimport type { SupportedLibraries, TranslateFlags } from '../types/index.js';\nimport { Libraries } from '../types/libraries.js';\nimport { displayHeader } from '../console/logging.js';\nimport { logger } from '../console/logger.js';\nimport { attachInlineTranslateFlags, attachTranslateFlags } from './flags.js';\nimport { InlineCLI } from './inline.js';\nimport {\n attachConfigureFlags,\n type ConfigureOptions,\n} from '../setup/onboarding.js';\n\nconst VUE_SOURCE_HELP =\n \"Space-separated list of glob patterns containing the app's Vue source code; defaults cover root SFCs and conventional Vue and Nuxt directories\";\n\n/** CLI commands for an application whose selected inline runtime is gt-vue. */\nexport class VueCLI extends InlineCLI {\n public constructor(\n command: Command,\n additionalModules?: SupportedLibraries[],\n fileLibrary: SupportedLibraries = Libraries.GT_VUE\n ) {\n super(command, fileLibrary, additionalModules);\n }\n\n public override init(): void {\n this.setupSetupProjectCommand();\n super.init();\n }\n\n /** Uses Vue-specific default source guidance on inherited inline commands. */\n protected override getInlineSourceHelp(): string {\n return VUE_SOURCE_HELP;\n }\n\n /** Mixed file projects still use Vue for generate and validation. */\n protected override getInlineLibrary(): typeof Libraries.GT_VUE {\n return Libraries.GT_VUE;\n }\n\n /** Configure Vue without entering the React application setup wizard. */\n protected override setupInitCommand(): void {\n attachConfigureFlags(\n this.program\n .command('init')\n .description(\n 'Configure a gt-vue project for General Translation without modifying application source. Flags answer its questions; unanswered ones are asked, or listed as missing with --no-interactive'\n ),\n VUE_SOURCE_HELP\n ).action((options: ConfigureOptions) =>\n this.handleConfigureCommand(options, 'init')\n );\n }\n\n /** Uploads Vue inline sources with the same targeting flags as extraction. */\n protected override setupSetupProjectCommand(): void {\n attachInlineTranslateFlags(\n attachTranslateFlags(\n this.program\n .command('setup')\n .description(\n 'Upload source files and setup the project for translation'\n )\n ),\n this.getInlineSourceHelp()\n ).action(async (options: TranslateFlags) => {\n displayHeader('Uploading source files and setting up project...');\n await this.handleSetupProject(options);\n logger.endCommand('Done!');\n });\n }\n}\n"],"mappings":";;;;;;;AAYA,MAAM,kBACJ;;AAGF,IAAa,SAAb,cAA4B,UAAU;CACpC,YACE,SACA,mBACA,cAAA,UACA;AACA,QAAM,SAAS,aAAa,kBAAkB;;CAGhD,OAA6B;AAC3B,OAAK,0BAA0B;AAC/B,QAAM,MAAM;;;CAId,sBAAiD;AAC/C,SAAO;;;CAIT,mBAA+D;AAC7D,SAAA;;;CAIF,mBAA4C;AAC1C,uBACE,KAAK,QACF,QAAQ,OAAO,CACf,YACC,6LACD,EACH,gBACD,CAAC,QAAQ,YACR,KAAK,uBAAuB,SAAS,OAAO,CAC7C;;;CAIH,2BAAoD;AAClD,6BACE,qBACE,KAAK,QACF,QAAQ,QAAQ,CAChB,YACC,4DACD,CACJ,EACD,KAAK,qBAAqB,CAC3B,CAAC,OAAO,OAAO,YAA4B;AAC1C,iBAAc,mDAAmD;AACjE,SAAM,KAAK,mBAAmB,QAAQ;AACtC,UAAO,WAAW,QAAQ;IAC1B"}
|
|
@@ -32,9 +32,11 @@ type GenerateSettingsInput = Partial<Omit<Settings, 'tag'>> & {
|
|
|
32
32
|
* @param cwd - The current working directory
|
|
33
33
|
* @param options - Additional options
|
|
34
34
|
* @param options.requireConfig - If true, exit with an error when no config file is found
|
|
35
|
+
* @param options.resolvedConfig - Config content to validate instead of the file, e.g. what setup is about to write
|
|
35
36
|
* @returns The generated settings
|
|
36
37
|
*/
|
|
37
38
|
export declare function generateSettings(flags: GenerateSettingsInput, cwd?: string, options?: {
|
|
38
39
|
requireConfig?: boolean;
|
|
40
|
+
resolvedConfig?: Record<string, unknown>;
|
|
39
41
|
}): Promise<Settings>;
|
|
40
42
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { logger } from "../console/logger.js";
|
|
2
2
|
import "../utils/constants.js";
|
|
3
3
|
import { displayProjectId, exitSync, logErrorAndExit, warnApiKeyInConfig, warnDeprecatedField } from "../console/logging.js";
|
|
4
|
-
import { loadConfig } from "../fs/config/loadConfig.js";
|
|
4
|
+
import { loadConfig, withJsonExtension } from "../fs/config/loadConfig.js";
|
|
5
5
|
import { GT_PARSING_FLAGS_DEFAULT } from "./defaults.js";
|
|
6
6
|
import { normalizeFilesOptions } from "../formats/files/transformFormat.js";
|
|
7
7
|
import { resolveFiles } from "../fs/config/parseFilesConfig.js";
|
|
@@ -11,11 +11,11 @@ import { createUserTokenProvider } from "../auth/oauth.js";
|
|
|
11
11
|
import { resolveConfig } from "./resolveConfig.js";
|
|
12
12
|
import { configureApiClient } from "../utils/api.js";
|
|
13
13
|
import { generatePreset } from "./optionPresets.js";
|
|
14
|
-
import { determineLibrary } from "../fs/determineFramework/index.js";
|
|
15
14
|
import chalk from "chalk";
|
|
15
|
+
import { defaultBaseUrl, libraryDefaultLocale } from "generaltranslation/internal";
|
|
16
16
|
import crypto from "node:crypto";
|
|
17
17
|
import path from "node:path";
|
|
18
|
-
import
|
|
18
|
+
import fs from "node:fs";
|
|
19
19
|
import { execSync } from "node:child_process";
|
|
20
20
|
//#region src/config/generateSettings.ts
|
|
21
21
|
const DEFAULT_SRC_PATTERNS = [
|
|
@@ -44,12 +44,14 @@ function hasConfiguredTranslationFiles(files) {
|
|
|
44
44
|
* @param cwd - The current working directory
|
|
45
45
|
* @param options - Additional options
|
|
46
46
|
* @param options.requireConfig - If true, exit with an error when no config file is found
|
|
47
|
+
* @param options.resolvedConfig - Config content to validate instead of the file, e.g. what setup is about to write
|
|
47
48
|
* @returns The generated settings
|
|
48
49
|
*/
|
|
49
50
|
async function generateSettings(flags, cwd = process.cwd(), options) {
|
|
50
51
|
let gtConfig = {};
|
|
51
|
-
if (flags.config
|
|
52
|
-
if (
|
|
52
|
+
if (flags.config) flags.config = withJsonExtension(flags.config);
|
|
53
|
+
if (options?.resolvedConfig) gtConfig = options.resolvedConfig;
|
|
54
|
+
else if (flags.config) gtConfig = loadConfig(flags.config);
|
|
53
55
|
else {
|
|
54
56
|
const config = resolveConfig(cwd);
|
|
55
57
|
if (config) {
|
|
@@ -77,7 +79,12 @@ async function generateSettings(flags, cwd = process.cwd(), options) {
|
|
|
77
79
|
...gtConfig,
|
|
78
80
|
...flags
|
|
79
81
|
};
|
|
80
|
-
if (
|
|
82
|
+
if (!hasConfiguredTranslationFiles(mergedOptions.files) && ![
|
|
83
|
+
"package.json",
|
|
84
|
+
"pyproject.toml",
|
|
85
|
+
"requirements.txt",
|
|
86
|
+
"setup.py"
|
|
87
|
+
].some((file) => fs.existsSync(path.join(cwd, file)))) logger.warn(chalk.yellow("No package.json or Python project file found in the current directory. Run this command from the root of your project."));
|
|
81
88
|
mergedOptions.defaultLocale = mergedOptions.defaultLocale || libraryDefaultLocale;
|
|
82
89
|
mergedOptions.locales = Array.from(new Set([...gtConfig.locales || [], ...flags.locales || []]));
|
|
83
90
|
mergedOptions.locales = mergedOptions.locales.filter((locale) => locale !== mergedOptions.defaultLocale);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateSettings.js","names":[],"sources":["../../src/config/generateSettings.ts"],"sourcesContent":["import {\n displayProjectId,\n exitSync,\n logErrorAndExit,\n warnApiKeyInConfig,\n warnDeprecatedField,\n} from '../console/logging.js';\nimport { loadConfig } from '../fs/config/loadConfig.js';\nimport { FilesOptions, Settings } from '../types/index.js';\nimport {\n defaultBaseUrl,\n libraryDefaultLocale,\n} from 'generaltranslation/internal';\nimport { resolveFiles } from '../fs/config/parseFilesConfig.js';\nimport { validateSettings } from './validateSettings.js';\nimport {\n DEFAULT_GIT_REMOTE_NAME,\n GT_DASHBOARD_URL,\n} from '../utils/constants.js';\nimport { resolveProjectId } from '../fs/utils.js';\nimport { createUserTokenProvider } from '../auth/oauth.js';\nimport crypto from 'node:crypto';\nimport { execSync } from 'node:child_process';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { resolveConfig } from './resolveConfig.js';\nimport { configureApiClient } from '../utils/api.js';\nimport { generatePreset } from './optionPresets.js';\nimport { GT_PARSING_FLAGS_DEFAULT } from './defaults.js';\nimport { normalizeFilesOptions } from '../formats/files/transformFormat.js';\nimport { determineLibrary } from '../fs/determineFramework/index.js';\nimport { logger } from '../console/logger.js';\n\nexport const DEFAULT_SRC_PATTERNS = [\n 'src/**/*.{js,jsx,ts,tsx}',\n 'app/**/*.{js,jsx,ts,tsx}',\n 'pages/**/*.{js,jsx,ts,tsx}',\n 'components/**/*.{js,jsx,ts,tsx}',\n];\n\nexport const DEFAULT_PYTHON_SRC_PATTERNS = ['**/*.py'];\nexport const DEFAULT_PYTHON_SRC_EXCLUDES = [\n 'venv/**',\n '.venv/**',\n '__pycache__/**',\n '**/migrations/**',\n '**/tests/**',\n '**/test_*.py',\n '**/*_test.py',\n];\n\ntype GenerateSettingsInput = Partial<Omit<Settings, 'tag'>> & {\n config?: string;\n projectId?: string;\n locales?: string[];\n options?: Settings['options'];\n files?: unknown;\n publish?: boolean;\n omitConfigIds?: boolean;\n saveLocal?: boolean;\n tag?: string | boolean;\n message?: string;\n branch?: string;\n enableBranching?: boolean;\n disableBranchDetection?: boolean;\n remoteName?: string;\n experimentalLocalizeStaticImports?: boolean;\n experimentalLocalizeStaticUrls?: boolean;\n experimentalLocalizeRelativeAssets?: boolean;\n experimentalHideDefaultLocale?: boolean;\n experimentalFlattenJsonFiles?: boolean;\n experimentalClearLocaleDirs?: boolean;\n clearLocaleDirsExclude?: string[];\n [key: string]: unknown;\n};\n\nfunction hasConfiguredTranslationFiles(files: unknown): boolean {\n if (!files || typeof files !== 'object' || Array.isArray(files)) {\n return false;\n }\n return Object.keys(files).some((key) => key !== 'gt');\n}\n\n/**\n * Generates settings from any\n * @param flags - The CLI flags to generate settings from\n * @param cwd - The current working directory\n * @param options - Additional options\n * @param options.requireConfig - If true, exit with an error when no config file is found\n * @returns The generated settings\n */\nexport async function generateSettings(\n flags: GenerateSettingsInput,\n cwd: string = process.cwd(),\n options?: { requireConfig?: boolean }\n): Promise<Settings> {\n // Load config file\n let gtConfig: GenerateSettingsInput = {};\n\n if (flags.config && !flags.config.endsWith('.json')) {\n flags.config = `${flags.config}.json`;\n }\n if (flags.config) {\n gtConfig = loadConfig(flags.config);\n } else {\n const config = resolveConfig(cwd);\n if (config) {\n gtConfig = config.config as GenerateSettingsInput;\n flags.config = config.path;\n } else {\n if (options?.requireConfig) {\n return logErrorAndExit(\n 'No gt.config.json file was found. Run `npx gt init` to create one, pass --config, or run this command from your project root.'\n );\n }\n gtConfig = {};\n }\n }\n\n // Warn if apiKey is present in gt.config.json\n if (gtConfig.apiKey) {\n warnApiKeyInConfig(flags.config ?? 'gt.config.json');\n exitSync(1);\n }\n const projectIdEnv = resolveProjectId();\n // Resolve mismatched projectIds\n if (\n gtConfig.projectId &&\n flags.projectId &&\n gtConfig.projectId !== flags.projectId\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but the CLI flag uses ${chalk.green(flags.projectId)}. Use the same projectId in all configs.`\n );\n } else if (\n gtConfig.projectId &&\n projectIdEnv &&\n gtConfig.projectId !== projectIdEnv\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but GT_PROJECT_ID uses ${chalk.green(projectIdEnv)}. Use the same projectId in all configs.`\n );\n }\n\n if (\n flags.options?.docsUrlPattern &&\n !flags.options?.docsUrlPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static URLs could not be localized because the URL pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated URL.'\n );\n }\n\n if (\n flags.options?.docsImportPattern &&\n !flags.options?.docsImportPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static imports could not be localized because the import pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated import path.'\n );\n }\n\n if (flags.options?.copyFiles) {\n for (const file of flags.options.copyFiles) {\n if (!file.includes('[locale]')) {\n logErrorAndExit(\n 'Files could not be copied because the file path is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the copied path.'\n );\n }\n }\n }\n\n // Warn on deprecated includeSourceCodeContext\n const configuredFiles = gtConfig.files as FilesOptions | undefined;\n if (configuredFiles?.gt?.includeSourceCodeContext != null) {\n warnDeprecatedField(\n 'files.gt.includeSourceCodeContext',\n 'files.gt.parsingFlags.includeSourceCodeContext'\n );\n }\n\n // merge options\n const mergedOptions: Settings = { ...gtConfig, ...flags } as Settings;\n\n if (\n determineLibrary().library === 'base' &&\n !hasConfiguredTranslationFiles(mergedOptions.files)\n ) {\n logger.warn(\n chalk.yellow(\n 'No package.json or Python project file found in the current directory. Run this command from the root of your project.'\n )\n );\n }\n\n // Add defaultLocale if not provided\n mergedOptions.defaultLocale =\n mergedOptions.defaultLocale || libraryDefaultLocale;\n\n // merge locales\n mergedOptions.locales = Array.from(\n new Set([...(gtConfig.locales || []), ...(flags.locales || [])])\n );\n // Separate defaultLocale from locales\n mergedOptions.locales = mergedOptions.locales.filter(\n (locale) => locale !== mergedOptions.defaultLocale\n );\n\n // Add projectId if not provided\n mergedOptions.projectId = mergedOptions.projectId || resolveProjectId();\n\n // Add baseUrl if not provided\n mergedOptions.baseUrl = mergedOptions.baseUrl || defaultBaseUrl;\n\n // The API client prefers apiKey when both are set; the provider is lazy.\n mergedOptions.apiKey = mergedOptions.apiKey || process.env.GT_API_KEY;\n mergedOptions.userTokenProvider = createUserTokenProvider({\n baseUrl: mergedOptions.baseUrl,\n });\n\n // Add dashboardUrl if not provided\n mergedOptions.dashboardUrl = mergedOptions.dashboardUrl || GT_DASHBOARD_URL;\n\n // Add locales if not provided\n mergedOptions.locales = mergedOptions.locales || [];\n\n // Only set config path if one was actually found or explicitly provided.\n // Do not default to a phantom path — that would cause downstream writes\n // (e.g. updateConfig) to silently create a config file.\n if (!mergedOptions.config) {\n mergedOptions.config = '';\n }\n\n // Display projectId if present\n if (mergedOptions.projectId) {\n displayProjectId(mergedOptions.projectId);\n }\n\n // Add stageTranslations if not provided\n // For human review, always stage the project\n mergedOptions.stageTranslations = mergedOptions.stageTranslations ?? false;\n\n // Top-level default for whether translated files require approval before use.\n // Effective policy is hash-changing, so reject anything but a real boolean.\n if (\n mergedOptions.requiresReview !== undefined &&\n typeof mergedOptions.requiresReview !== 'boolean'\n ) {\n logErrorAndExit(\n 'requiresReview in gt.config.json must be a boolean. Use files.<type>.requiresReview for glob-scoped overrides.'\n );\n }\n mergedOptions.requiresReview = mergedOptions.requiresReview ?? false;\n\n // Add publish — only set if explicitly configured or passed via flag.\n // When neither is set, leave undefined so the publish step knows\n // there is no global publish intent.\n if (flags.publish) {\n mergedOptions.publish = true;\n } else if (gtConfig.publish !== undefined) {\n mergedOptions.publish = gtConfig.publish;\n } else {\n mergedOptions.publish = undefined;\n }\n\n mergedOptions.omitConfigIds =\n flags.omitConfigIds === true || gtConfig.omitConfigIds === true;\n\n // Don't default src here — each pipeline (JS/Python) has its own defaults.\n // Only set src if the user explicitly provided it via flags or config.\n\n // Resolve all glob patterns in the files object\n const compositePatterns = Object.entries(\n mergedOptions.options?.jsonSchema || {}\n )\n .filter(([, schema]) => schema.composite)\n .map(([key]) => key);\n mergedOptions.files = mergedOptions.files\n ? resolveFiles(\n normalizeFilesOptions(mergedOptions.files as FilesOptions),\n mergedOptions.defaultLocale,\n mergedOptions.locales,\n cwd,\n compositePatterns,\n mergedOptions.requiresReview\n )\n : {\n resolvedPaths: {},\n placeholderPaths: {},\n transformPaths: {},\n transformFormats: {},\n publishPaths: new Set<string>(),\n unpublishPaths: new Set<string>(),\n requiresReviewPaths: new Set<string>(),\n parsingFlags: {},\n gtJson: {\n parsingFlags: GT_PARSING_FLAGS_DEFAULT,\n },\n };\n\n mergedOptions.options = {\n ...mergedOptions.options,\n mintlify: {\n ...mergedOptions.options?.mintlify,\n inferTitleFromFilename:\n gtConfig.options?.mintlify?.inferTitleFromFilename ||\n mergedOptions.options?.mintlify?.inferTitleFromFilename,\n },\n experimentalLocalizeStaticImports:\n gtConfig.options?.experimentalLocalizeStaticImports ||\n flags.experimentalLocalizeStaticImports,\n experimentalLocalizeStaticUrls:\n gtConfig.options?.experimentalLocalizeStaticUrls ||\n flags.experimentalLocalizeStaticUrls,\n experimentalLocalizeRelativeAssets:\n gtConfig.options?.experimentalLocalizeRelativeAssets ||\n flags.experimentalLocalizeRelativeAssets,\n experimentalHideDefaultLocale:\n gtConfig.options?.experimentalHideDefaultLocale ||\n flags.experimentalHideDefaultLocale,\n experimentalFlattenJsonFiles:\n gtConfig.options?.experimentalFlattenJsonFiles ||\n flags.experimentalFlattenJsonFiles,\n experimentalClearLocaleDirs:\n gtConfig.options?.experimentalClearLocaleDirs ||\n flags.experimentalClearLocaleDirs,\n clearLocaleDirsExclude:\n gtConfig.options?.clearLocaleDirsExclude || flags.clearLocaleDirsExclude,\n saveLocal: flags.saveLocal ?? gtConfig.options?.saveLocal ?? false,\n };\n\n if (\n mergedOptions.omitConfigIds &&\n (mergedOptions.publish === true ||\n mergedOptions.files.gtJson.publish === true)\n ) {\n logger.warn(\n chalk.yellow(\n 'Config IDs will be omitted even though CDN publishing is enabled. Remote cache/CDN consumers may load the latest available translations instead of a pinned version.'\n )\n );\n }\n\n // Add additional options if provided\n if (mergedOptions.options) {\n if (mergedOptions.options.jsonSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.jsonSchema)) {\n const jsonSchema = mergedOptions.options.jsonSchema[fileGlob];\n if (jsonSchema.preset) {\n mergedOptions.options.jsonSchema[fileGlob] = {\n ...generatePreset(jsonSchema.preset, 'json'),\n ...jsonSchema,\n };\n }\n }\n }\n if (mergedOptions.options.yamlSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.yamlSchema)) {\n const yamlSchema = mergedOptions.options.yamlSchema[fileGlob];\n if (yamlSchema.preset) {\n mergedOptions.options.yamlSchema[fileGlob] = {\n ...generatePreset(yamlSchema.preset, 'yaml'),\n ...yamlSchema,\n };\n }\n }\n }\n }\n\n // Add parsing options if not provided\n mergedOptions.parsingOptions = mergedOptions.parsingOptions || {};\n mergedOptions.parsingOptions.conditionNames = mergedOptions.parsingOptions\n .conditionNames || [\n 'development',\n 'browser',\n 'module',\n 'import',\n 'require',\n 'default',\n ];\n\n // Add branch options if not provided\n const branchOptions = mergedOptions.branchOptions || {};\n // If --branch is set, enable branching\n branchOptions.enabled =\n flags.enableBranching ??\n gtConfig.branchOptions?.enabled ??\n (flags.branch ? true : false);\n branchOptions.currentBranch =\n flags.branch ?? gtConfig.branchOptions?.currentBranch ?? undefined;\n branchOptions.autoDetectBranches = flags.disableBranchDetection\n ? false\n : (gtConfig.branchOptions?.autoDetectBranches ?? true);\n branchOptions.remoteName =\n flags.remoteName ??\n gtConfig.branchOptions?.remoteName ??\n DEFAULT_GIT_REMOTE_NAME;\n mergedOptions.branchOptions = branchOptions;\n\n // Map -m/--message flag to tagMessage\n if (flags.message) {\n mergedOptions.tagMessage = flags.message;\n }\n\n // Resolve tag:\n // --tag (bare) or -m without --tag: try git SHA, fall back to random hex\n // --tag <value>: use as-is\n // No flags: no tag\n if (flags.tag === true || (!flags.tag && mergedOptions.tagMessage)) {\n try {\n mergedOptions.tag = execSync('git rev-parse --short HEAD', {\n encoding: 'utf-8',\n }).trim();\n // If no message provided, use git commit message\n if (!mergedOptions.tagMessage) {\n mergedOptions.tagMessage = execSync('git log -1 --format=%s', {\n encoding: 'utf-8',\n }).trim();\n }\n } catch {\n // Not in a git repo or git unavailable — fall back to random hex\n mergedOptions.tag = crypto.randomBytes(4).toString('hex');\n }\n }\n\n mergedOptions.configDirectory = path.join(cwd, '.gt');\n\n validateSettings(mergedOptions);\n\n configureApiClient({\n projectId: mergedOptions.projectId,\n apiKey: mergedOptions.apiKey,\n userTokenProvider: mergedOptions.userTokenProvider,\n baseUrl: mergedOptions.baseUrl,\n customMapping: mergedOptions.customMapping,\n });\n\n return mergedOptions;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generateSettings.js","names":[],"sources":["../../src/config/generateSettings.ts"],"sourcesContent":["import {\n displayProjectId,\n exitSync,\n logErrorAndExit,\n warnApiKeyInConfig,\n warnDeprecatedField,\n} from '../console/logging.js';\nimport { loadConfig, withJsonExtension } from '../fs/config/loadConfig.js';\nimport { FilesOptions, Settings } from '../types/index.js';\nimport {\n defaultBaseUrl,\n libraryDefaultLocale,\n} from 'generaltranslation/internal';\nimport { resolveFiles } from '../fs/config/parseFilesConfig.js';\nimport { validateSettings } from './validateSettings.js';\nimport {\n DEFAULT_GIT_REMOTE_NAME,\n GT_DASHBOARD_URL,\n} from '../utils/constants.js';\nimport { resolveProjectId } from '../fs/utils.js';\nimport { createUserTokenProvider } from '../auth/oauth.js';\nimport crypto from 'node:crypto';\nimport { execSync } from 'node:child_process';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport chalk from 'chalk';\nimport { resolveConfig } from './resolveConfig.js';\nimport { configureApiClient } from '../utils/api.js';\nimport { generatePreset } from './optionPresets.js';\nimport { GT_PARSING_FLAGS_DEFAULT } from './defaults.js';\nimport { normalizeFilesOptions } from '../formats/files/transformFormat.js';\nimport { logger } from '../console/logger.js';\n\nexport const DEFAULT_SRC_PATTERNS = [\n 'src/**/*.{js,jsx,ts,tsx}',\n 'app/**/*.{js,jsx,ts,tsx}',\n 'pages/**/*.{js,jsx,ts,tsx}',\n 'components/**/*.{js,jsx,ts,tsx}',\n];\n\nexport const DEFAULT_PYTHON_SRC_PATTERNS = ['**/*.py'];\nexport const DEFAULT_PYTHON_SRC_EXCLUDES = [\n 'venv/**',\n '.venv/**',\n '__pycache__/**',\n '**/migrations/**',\n '**/tests/**',\n '**/test_*.py',\n '**/*_test.py',\n];\n\ntype GenerateSettingsInput = Partial<Omit<Settings, 'tag'>> & {\n config?: string;\n projectId?: string;\n locales?: string[];\n options?: Settings['options'];\n files?: unknown;\n publish?: boolean;\n omitConfigIds?: boolean;\n saveLocal?: boolean;\n tag?: string | boolean;\n message?: string;\n branch?: string;\n enableBranching?: boolean;\n disableBranchDetection?: boolean;\n remoteName?: string;\n experimentalLocalizeStaticImports?: boolean;\n experimentalLocalizeStaticUrls?: boolean;\n experimentalLocalizeRelativeAssets?: boolean;\n experimentalHideDefaultLocale?: boolean;\n experimentalFlattenJsonFiles?: boolean;\n experimentalClearLocaleDirs?: boolean;\n clearLocaleDirsExclude?: string[];\n [key: string]: unknown;\n};\n\nfunction hasConfiguredTranslationFiles(files: unknown): boolean {\n if (!files || typeof files !== 'object' || Array.isArray(files)) {\n return false;\n }\n return Object.keys(files).some((key) => key !== 'gt');\n}\n\n/**\n * Generates settings from any\n * @param flags - The CLI flags to generate settings from\n * @param cwd - The current working directory\n * @param options - Additional options\n * @param options.requireConfig - If true, exit with an error when no config file is found\n * @param options.resolvedConfig - Config content to validate instead of the file, e.g. what setup is about to write\n * @returns The generated settings\n */\nexport async function generateSettings(\n flags: GenerateSettingsInput,\n cwd: string = process.cwd(),\n options?: {\n requireConfig?: boolean;\n resolvedConfig?: Record<string, unknown>;\n }\n): Promise<Settings> {\n // Load config file\n let gtConfig: GenerateSettingsInput = {};\n\n if (flags.config) {\n flags.config = withJsonExtension(flags.config);\n }\n if (options?.resolvedConfig) {\n gtConfig = options.resolvedConfig as GenerateSettingsInput;\n } else if (flags.config) {\n gtConfig = loadConfig(flags.config);\n } else {\n const config = resolveConfig(cwd);\n if (config) {\n gtConfig = config.config as GenerateSettingsInput;\n flags.config = config.path;\n } else {\n if (options?.requireConfig) {\n return logErrorAndExit(\n 'No gt.config.json file was found. Run `npx gt init` to create one, pass --config, or run this command from your project root.'\n );\n }\n gtConfig = {};\n }\n }\n\n // Warn if apiKey is present in gt.config.json\n if (gtConfig.apiKey) {\n warnApiKeyInConfig(flags.config ?? 'gt.config.json');\n exitSync(1);\n }\n const projectIdEnv = resolveProjectId();\n // Resolve mismatched projectIds\n if (\n gtConfig.projectId &&\n flags.projectId &&\n gtConfig.projectId !== flags.projectId\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but the CLI flag uses ${chalk.green(flags.projectId)}. Use the same projectId in all configs.`\n );\n } else if (\n gtConfig.projectId &&\n projectIdEnv &&\n gtConfig.projectId !== projectIdEnv\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but GT_PROJECT_ID uses ${chalk.green(projectIdEnv)}. Use the same projectId in all configs.`\n );\n }\n\n if (\n flags.options?.docsUrlPattern &&\n !flags.options?.docsUrlPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static URLs could not be localized because the URL pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated URL.'\n );\n }\n\n if (\n flags.options?.docsImportPattern &&\n !flags.options?.docsImportPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static imports could not be localized because the import pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated import path.'\n );\n }\n\n if (flags.options?.copyFiles) {\n for (const file of flags.options.copyFiles) {\n if (!file.includes('[locale]')) {\n logErrorAndExit(\n 'Files could not be copied because the file path is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the copied path.'\n );\n }\n }\n }\n\n // Warn on deprecated includeSourceCodeContext\n const configuredFiles = gtConfig.files as FilesOptions | undefined;\n if (configuredFiles?.gt?.includeSourceCodeContext != null) {\n warnDeprecatedField(\n 'files.gt.includeSourceCodeContext',\n 'files.gt.parsingFlags.includeSourceCodeContext'\n );\n }\n\n // merge options\n const mergedOptions: Settings = { ...gtConfig, ...flags } as Settings;\n\n if (\n !hasConfiguredTranslationFiles(mergedOptions.files) &&\n !['package.json', 'pyproject.toml', 'requirements.txt', 'setup.py'].some(\n (file) => fs.existsSync(path.join(cwd, file))\n )\n ) {\n logger.warn(\n chalk.yellow(\n 'No package.json or Python project file found in the current directory. Run this command from the root of your project.'\n )\n );\n }\n\n // Add defaultLocale if not provided\n mergedOptions.defaultLocale =\n mergedOptions.defaultLocale || libraryDefaultLocale;\n\n // merge locales\n mergedOptions.locales = Array.from(\n new Set([...(gtConfig.locales || []), ...(flags.locales || [])])\n );\n // Separate defaultLocale from locales\n mergedOptions.locales = mergedOptions.locales.filter(\n (locale) => locale !== mergedOptions.defaultLocale\n );\n\n // Add projectId if not provided\n mergedOptions.projectId = mergedOptions.projectId || resolveProjectId();\n\n // Add baseUrl if not provided\n mergedOptions.baseUrl = mergedOptions.baseUrl || defaultBaseUrl;\n\n // The API client prefers apiKey when both are set; the provider is lazy.\n mergedOptions.apiKey = mergedOptions.apiKey || process.env.GT_API_KEY;\n mergedOptions.userTokenProvider = createUserTokenProvider({\n baseUrl: mergedOptions.baseUrl,\n });\n\n // Add dashboardUrl if not provided\n mergedOptions.dashboardUrl = mergedOptions.dashboardUrl || GT_DASHBOARD_URL;\n\n // Add locales if not provided\n mergedOptions.locales = mergedOptions.locales || [];\n\n // Only set config path if one was actually found or explicitly provided.\n // Do not default to a phantom path — that would cause downstream writes\n // (e.g. updateConfig) to silently create a config file.\n if (!mergedOptions.config) {\n mergedOptions.config = '';\n }\n\n // Display projectId if present\n if (mergedOptions.projectId) {\n displayProjectId(mergedOptions.projectId);\n }\n\n // Add stageTranslations if not provided\n // For human review, always stage the project\n mergedOptions.stageTranslations = mergedOptions.stageTranslations ?? false;\n\n // Top-level default for whether translated files require approval before use.\n // Effective policy is hash-changing, so reject anything but a real boolean.\n if (\n mergedOptions.requiresReview !== undefined &&\n typeof mergedOptions.requiresReview !== 'boolean'\n ) {\n logErrorAndExit(\n 'requiresReview in gt.config.json must be a boolean. Use files.<type>.requiresReview for glob-scoped overrides.'\n );\n }\n mergedOptions.requiresReview = mergedOptions.requiresReview ?? false;\n\n // Add publish — only set if explicitly configured or passed via flag.\n // When neither is set, leave undefined so the publish step knows\n // there is no global publish intent.\n if (flags.publish) {\n mergedOptions.publish = true;\n } else if (gtConfig.publish !== undefined) {\n mergedOptions.publish = gtConfig.publish;\n } else {\n mergedOptions.publish = undefined;\n }\n\n mergedOptions.omitConfigIds =\n flags.omitConfigIds === true || gtConfig.omitConfigIds === true;\n\n // Don't default src here — each pipeline (JS/Python) has its own defaults.\n // Only set src if the user explicitly provided it via flags or config.\n\n // Resolve all glob patterns in the files object\n const compositePatterns = Object.entries(\n mergedOptions.options?.jsonSchema || {}\n )\n .filter(([, schema]) => schema.composite)\n .map(([key]) => key);\n mergedOptions.files = mergedOptions.files\n ? resolveFiles(\n normalizeFilesOptions(mergedOptions.files as FilesOptions),\n mergedOptions.defaultLocale,\n mergedOptions.locales,\n cwd,\n compositePatterns,\n mergedOptions.requiresReview\n )\n : {\n resolvedPaths: {},\n placeholderPaths: {},\n transformPaths: {},\n transformFormats: {},\n publishPaths: new Set<string>(),\n unpublishPaths: new Set<string>(),\n requiresReviewPaths: new Set<string>(),\n parsingFlags: {},\n gtJson: {\n parsingFlags: GT_PARSING_FLAGS_DEFAULT,\n },\n };\n\n mergedOptions.options = {\n ...mergedOptions.options,\n mintlify: {\n ...mergedOptions.options?.mintlify,\n inferTitleFromFilename:\n gtConfig.options?.mintlify?.inferTitleFromFilename ||\n mergedOptions.options?.mintlify?.inferTitleFromFilename,\n },\n experimentalLocalizeStaticImports:\n gtConfig.options?.experimentalLocalizeStaticImports ||\n flags.experimentalLocalizeStaticImports,\n experimentalLocalizeStaticUrls:\n gtConfig.options?.experimentalLocalizeStaticUrls ||\n flags.experimentalLocalizeStaticUrls,\n experimentalLocalizeRelativeAssets:\n gtConfig.options?.experimentalLocalizeRelativeAssets ||\n flags.experimentalLocalizeRelativeAssets,\n experimentalHideDefaultLocale:\n gtConfig.options?.experimentalHideDefaultLocale ||\n flags.experimentalHideDefaultLocale,\n experimentalFlattenJsonFiles:\n gtConfig.options?.experimentalFlattenJsonFiles ||\n flags.experimentalFlattenJsonFiles,\n experimentalClearLocaleDirs:\n gtConfig.options?.experimentalClearLocaleDirs ||\n flags.experimentalClearLocaleDirs,\n clearLocaleDirsExclude:\n gtConfig.options?.clearLocaleDirsExclude || flags.clearLocaleDirsExclude,\n saveLocal: flags.saveLocal ?? gtConfig.options?.saveLocal ?? false,\n };\n\n if (\n mergedOptions.omitConfigIds &&\n (mergedOptions.publish === true ||\n mergedOptions.files.gtJson.publish === true)\n ) {\n logger.warn(\n chalk.yellow(\n 'Config IDs will be omitted even though CDN publishing is enabled. Remote cache/CDN consumers may load the latest available translations instead of a pinned version.'\n )\n );\n }\n\n // Add additional options if provided\n if (mergedOptions.options) {\n if (mergedOptions.options.jsonSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.jsonSchema)) {\n const jsonSchema = mergedOptions.options.jsonSchema[fileGlob];\n if (jsonSchema.preset) {\n mergedOptions.options.jsonSchema[fileGlob] = {\n ...generatePreset(jsonSchema.preset, 'json'),\n ...jsonSchema,\n };\n }\n }\n }\n if (mergedOptions.options.yamlSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.yamlSchema)) {\n const yamlSchema = mergedOptions.options.yamlSchema[fileGlob];\n if (yamlSchema.preset) {\n mergedOptions.options.yamlSchema[fileGlob] = {\n ...generatePreset(yamlSchema.preset, 'yaml'),\n ...yamlSchema,\n };\n }\n }\n }\n }\n\n // Add parsing options if not provided\n mergedOptions.parsingOptions = mergedOptions.parsingOptions || {};\n mergedOptions.parsingOptions.conditionNames = mergedOptions.parsingOptions\n .conditionNames || [\n 'development',\n 'browser',\n 'module',\n 'import',\n 'require',\n 'default',\n ];\n\n // Add branch options if not provided\n const branchOptions = mergedOptions.branchOptions || {};\n // If --branch is set, enable branching\n branchOptions.enabled =\n flags.enableBranching ??\n gtConfig.branchOptions?.enabled ??\n (flags.branch ? true : false);\n branchOptions.currentBranch =\n flags.branch ?? gtConfig.branchOptions?.currentBranch ?? undefined;\n branchOptions.autoDetectBranches = flags.disableBranchDetection\n ? false\n : (gtConfig.branchOptions?.autoDetectBranches ?? true);\n branchOptions.remoteName =\n flags.remoteName ??\n gtConfig.branchOptions?.remoteName ??\n DEFAULT_GIT_REMOTE_NAME;\n mergedOptions.branchOptions = branchOptions;\n\n // Map -m/--message flag to tagMessage\n if (flags.message) {\n mergedOptions.tagMessage = flags.message;\n }\n\n // Resolve tag:\n // --tag (bare) or -m without --tag: try git SHA, fall back to random hex\n // --tag <value>: use as-is\n // No flags: no tag\n if (flags.tag === true || (!flags.tag && mergedOptions.tagMessage)) {\n try {\n mergedOptions.tag = execSync('git rev-parse --short HEAD', {\n encoding: 'utf-8',\n }).trim();\n // If no message provided, use git commit message\n if (!mergedOptions.tagMessage) {\n mergedOptions.tagMessage = execSync('git log -1 --format=%s', {\n encoding: 'utf-8',\n }).trim();\n }\n } catch {\n // Not in a git repo or git unavailable — fall back to random hex\n mergedOptions.tag = crypto.randomBytes(4).toString('hex');\n }\n }\n\n mergedOptions.configDirectory = path.join(cwd, '.gt');\n\n validateSettings(mergedOptions);\n\n configureApiClient({\n projectId: mergedOptions.projectId,\n apiKey: mergedOptions.apiKey,\n userTokenProvider: mergedOptions.userTokenProvider,\n baseUrl: mergedOptions.baseUrl,\n customMapping: mergedOptions.customMapping,\n });\n\n return mergedOptions;\n}\n"],"mappings":"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"}
|
|
@@ -8,10 +8,6 @@ function resolveConfig(cwd) {
|
|
|
8
8
|
path: path.join(cwd, configFilepath),
|
|
9
9
|
config: loadConfig(path.join(cwd, configFilepath))
|
|
10
10
|
};
|
|
11
|
-
if (fs.existsSync(path.join(cwd, "src/gt.config.json"))) return {
|
|
12
|
-
path: path.join(cwd, "src/gt.config.json"),
|
|
13
|
-
config: loadConfig(path.join(cwd, "src/gt.config.json"))
|
|
14
|
-
};
|
|
15
11
|
if (fs.existsSync(path.join(cwd, ".gt/gt.config.json"))) return {
|
|
16
12
|
path: path.join(cwd, ".gt/gt.config.json"),
|
|
17
13
|
config: loadConfig(path.join(cwd, ".gt/gt.config.json"))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveConfig.js","names":[],"sources":["../../src/config/resolveConfig.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { loadConfig } from '../fs/config/loadConfig.js';\n\nexport function resolveConfig(cwd: string): {\n path: string;\n config: Record<string, unknown>;\n} | null {\n const configFilepath = 'gt.config.json';\n if (fs.existsSync(path.join(cwd, configFilepath))) {\n return {\n path: path.join(cwd, configFilepath),\n config: loadConfig(path.join(cwd, configFilepath)),\n };\n }\n
|
|
1
|
+
{"version":3,"file":"resolveConfig.js","names":[],"sources":["../../src/config/resolveConfig.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { loadConfig } from '../fs/config/loadConfig.js';\n\nexport function resolveConfig(cwd: string): {\n path: string;\n config: Record<string, unknown>;\n} | null {\n const configFilepath = 'gt.config.json';\n if (fs.existsSync(path.join(cwd, configFilepath))) {\n return {\n path: path.join(cwd, configFilepath),\n config: loadConfig(path.join(cwd, configFilepath)),\n };\n }\n // Support config under .gt for parity with .locadex\n if (fs.existsSync(path.join(cwd, '.gt/gt.config.json'))) {\n return {\n path: path.join(cwd, '.gt/gt.config.json'),\n config: loadConfig(path.join(cwd, '.gt/gt.config.json')),\n };\n }\n // Backward compatibility: support legacy .locadex directory\n if (fs.existsSync(path.join(cwd, '.locadex/gt.config.json'))) {\n return {\n path: path.join(cwd, '.locadex/gt.config.json'),\n config: loadConfig(path.join(cwd, '.locadex/gt.config.json')),\n };\n }\n return null;\n}\n"],"mappings":";;;;AAIA,SAAgB,cAAc,KAGrB;CACP,MAAM,iBAAiB;AACvB,KAAI,GAAG,WAAW,KAAK,KAAK,KAAK,eAAe,CAAC,CAC/C,QAAO;EACL,MAAM,KAAK,KAAK,KAAK,eAAe;EACpC,QAAQ,WAAW,KAAK,KAAK,KAAK,eAAe,CAAC;EACnD;AAGH,KAAI,GAAG,WAAW,KAAK,KAAK,KAAK,qBAAqB,CAAC,CACrD,QAAO;EACL,MAAM,KAAK,KAAK,KAAK,qBAAqB;EAC1C,QAAQ,WAAW,KAAK,KAAK,KAAK,qBAAqB,CAAC;EACzD;AAGH,KAAI,GAAG,WAAW,KAAK,KAAK,KAAK,0BAA0B,CAAC,CAC1D,QAAO;EACL,MAAM,KAAK,KAAK,KAAK,0BAA0B;EAC/C,QAAQ,WAAW,KAAK,KAAK,KAAK,0BAA0B,CAAC;EAC9D;AAEH,QAAO"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { logErrorAndExit } from "../console/logging.js";
|
|
2
|
+
import { isModelProvider, supportedModelProviders } from "generaltranslation/internal";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import fs from "node:fs";
|
|
4
|
-
import { createDiagnosticMessage } from "generaltranslation/diagnostics";
|
|
5
|
-
import { isModelProvider, supportedModelProviders } from "generaltranslation/internal";
|
|
5
|
+
import { createDiagnosticMessage as createDiagnosticMessage$1 } from "generaltranslation/diagnostics";
|
|
6
6
|
import { isSupersetLocale, isValidLocale } from "@generaltranslation/format";
|
|
7
7
|
//#region src/config/validateSettings.ts
|
|
8
8
|
function validateSettings(settings) {
|
|
9
|
-
if (settings.modelProvider !== void 0 && !isModelProvider(settings.modelProvider)) return logErrorAndExit(createDiagnosticMessage({
|
|
9
|
+
if (settings.modelProvider !== void 0 && !isModelProvider(settings.modelProvider)) return logErrorAndExit(createDiagnosticMessage$1({
|
|
10
10
|
source: "gt",
|
|
11
11
|
severity: "Error",
|
|
12
12
|
whatHappened: "The configured model provider is not supported",
|
|
@@ -21,7 +21,8 @@ function validateSettings(settings) {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
function validateConfigExists() {
|
|
24
|
-
|
|
24
|
+
const configPath = "gt.config.json";
|
|
25
|
+
if (fs.existsSync(path.resolve(configPath))) return configPath;
|
|
25
26
|
return logErrorAndExit("No gt.config.json file was found. Run this command from your project root, pass --config, or run npx gt init to create one.");
|
|
26
27
|
}
|
|
27
28
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validateSettings.js","names":[],"sources":["../../src/config/validateSettings.ts"],"sourcesContent":["import { isValidLocale, isSupersetLocale } from '@generaltranslation/format';\nimport { createDiagnosticMessage } from 'generaltranslation/diagnostics';\nimport {\n isModelProvider,\n supportedModelProviders,\n} from 'generaltranslation/internal';\nimport type { Settings } from '../types/index.js';\nimport { logErrorAndExit } from '../console/logging.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nexport function validateSettings(\n settings: Omit<Settings, 'modelProvider'> & { modelProvider?: unknown }\n): asserts settings is Settings {\n if (\n settings.modelProvider !== undefined &&\n !isModelProvider(settings.modelProvider)\n ) {\n return logErrorAndExit(\n createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened: 'The configured model provider is not supported',\n details: String(settings.modelProvider),\n fix: `Use one of: ${supportedModelProviders.join(', ')}`,\n })\n );\n }\n\n // Validate locales\n for (const locale of settings.locales) {\n if (!isValidLocale(locale, settings.customMapping)) {\n return logErrorAndExit(\n `Locale \"${locale}\" is not valid in the provided locales: \"${settings?.locales?.join()}\". Use a valid BCP 47 locale code or add a custom mapping in gt.config.json.`\n );\n }\n }\n if (\n settings.defaultLocale &&\n !isValidLocale(settings.defaultLocale, settings.customMapping)\n ) {\n return logErrorAndExit(\n `defaultLocale \"${settings.defaultLocale}\" is not valid. Use a valid BCP 47 locale code or add a custom mapping in gt.config.json.`\n );\n }\n\n // defaultLocale cannot be a superset of any other locale\n if (\n settings.defaultLocale &&\n settings.locales.some(\n (locale) =>\n isSupersetLocale(settings.defaultLocale, locale) &&\n !isSupersetLocale(locale, settings.defaultLocale)\n )\n ) {\n const locale = settings.locales.find((locale) =>\n isSupersetLocale(settings.defaultLocale, locale)\n );\n return logErrorAndExit(\n `defaultLocale \"${settings.defaultLocale}\" is broader than configured locale \"${locale}\". Change defaultLocale to a more specific locale.`\n );\n }\n}\n\nexport function validateConfigExists() {\n const
|
|
1
|
+
{"version":3,"file":"validateSettings.js","names":["createDiagnosticMessage"],"sources":["../../src/config/validateSettings.ts"],"sourcesContent":["import { isValidLocale, isSupersetLocale } from '@generaltranslation/format';\nimport { createDiagnosticMessage } from 'generaltranslation/diagnostics';\nimport {\n isModelProvider,\n supportedModelProviders,\n} from 'generaltranslation/internal';\nimport type { Settings } from '../types/index.js';\nimport { logErrorAndExit } from '../console/logging.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nexport function validateSettings(\n settings: Omit<Settings, 'modelProvider'> & { modelProvider?: unknown }\n): asserts settings is Settings {\n if (\n settings.modelProvider !== undefined &&\n !isModelProvider(settings.modelProvider)\n ) {\n return logErrorAndExit(\n createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened: 'The configured model provider is not supported',\n details: String(settings.modelProvider),\n fix: `Use one of: ${supportedModelProviders.join(', ')}`,\n })\n );\n }\n\n // Validate locales\n for (const locale of settings.locales) {\n if (!isValidLocale(locale, settings.customMapping)) {\n return logErrorAndExit(\n `Locale \"${locale}\" is not valid in the provided locales: \"${settings?.locales?.join()}\". Use a valid BCP 47 locale code or add a custom mapping in gt.config.json.`\n );\n }\n }\n if (\n settings.defaultLocale &&\n !isValidLocale(settings.defaultLocale, settings.customMapping)\n ) {\n return logErrorAndExit(\n `defaultLocale \"${settings.defaultLocale}\" is not valid. Use a valid BCP 47 locale code or add a custom mapping in gt.config.json.`\n );\n }\n\n // defaultLocale cannot be a superset of any other locale\n if (\n settings.defaultLocale &&\n settings.locales.some(\n (locale) =>\n isSupersetLocale(settings.defaultLocale, locale) &&\n !isSupersetLocale(locale, settings.defaultLocale)\n )\n ) {\n const locale = settings.locales.find((locale) =>\n isSupersetLocale(settings.defaultLocale, locale)\n );\n return logErrorAndExit(\n `defaultLocale \"${settings.defaultLocale}\" is broader than configured locale \"${locale}\". Change defaultLocale to a more specific locale.`\n );\n }\n}\n\nexport function validateConfigExists() {\n const configPath = 'gt.config.json';\n if (fs.existsSync(path.resolve(configPath))) return configPath;\n return logErrorAndExit(\n 'No gt.config.json file was found. Run this command from your project root, pass --config, or run npx gt init to create one.'\n );\n}\n"],"mappings":";;;;;;;AAWA,SAAgB,iBACd,UAC8B;AAC9B,KACE,SAAS,kBAAkB,KAAA,KAC3B,CAAC,gBAAgB,SAAS,cAAc,CAExC,QAAO,gBACLA,0BAAwB;EACtB,QAAQ;EACR,UAAU;EACV,cAAc;EACd,SAAS,OAAO,SAAS,cAAc;EACvC,KAAK,eAAe,wBAAwB,KAAK,KAAK;EACvD,CAAC,CACH;AAIH,MAAK,MAAM,UAAU,SAAS,QAC5B,KAAI,CAAC,cAAc,QAAQ,SAAS,cAAc,CAChD,QAAO,gBACL,WAAW,OAAO,2CAA2C,UAAU,SAAS,MAAM,CAAC,8EACxF;AAGL,KACE,SAAS,iBACT,CAAC,cAAc,SAAS,eAAe,SAAS,cAAc,CAE9D,QAAO,gBACL,kBAAkB,SAAS,cAAc,2FAC1C;AAIH,KACE,SAAS,iBACT,SAAS,QAAQ,MACd,WACC,iBAAiB,SAAS,eAAe,OAAO,IAChD,CAAC,iBAAiB,QAAQ,SAAS,cAAc,CACpD,EACD;EACA,MAAM,SAAS,SAAS,QAAQ,MAAM,WACpC,iBAAiB,SAAS,eAAe,OAAO,CACjD;AACD,SAAO,gBACL,kBAAkB,SAAS,cAAc,uCAAuC,OAAO,oDACxF;;;AAIL,SAAgB,uBAAuB;CACrC,MAAM,aAAa;AACnB,KAAI,GAAG,WAAW,KAAK,QAAQ,WAAW,CAAC,CAAE,QAAO;AACpD,QAAO,gBACL,8HACD"}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
export type LocaleOption = {
|
|
2
|
+
code: string;
|
|
3
|
+
label: string;
|
|
4
|
+
name: string;
|
|
5
|
+
nativeName: string;
|
|
6
|
+
searchable: string;
|
|
7
|
+
};
|
|
2
8
|
export declare function getLocaleOptions(): LocaleOption[];
|
|
3
|
-
export declare function getLocaleCodeWidth(): number;
|
|
4
9
|
export declare function getFilteredLocaleOptions(query: string): LocaleOption[];
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { getLocaleProperties } from "generaltranslation";
|
|
2
2
|
import { listSupportedLocales } from "@generaltranslation/supported-locales";
|
|
3
|
-
//#region src/console/
|
|
3
|
+
//#region src/console/localeOptions.ts
|
|
4
4
|
let localeOptionsCache;
|
|
5
|
-
let localeCodeWidthCache;
|
|
6
5
|
function getLocaleOptions() {
|
|
7
6
|
localeOptionsCache ??= listSupportedLocales().map((code) => {
|
|
8
7
|
const properties = getLocaleProperties(code);
|
|
@@ -16,10 +15,6 @@ function getLocaleOptions() {
|
|
|
16
15
|
});
|
|
17
16
|
return localeOptionsCache;
|
|
18
17
|
}
|
|
19
|
-
function getLocaleCodeWidth() {
|
|
20
|
-
localeCodeWidthCache ??= getLocaleOptions().reduce((max, option) => Math.max(max, option.code.length), 0);
|
|
21
|
-
return localeCodeWidthCache;
|
|
22
|
-
}
|
|
23
18
|
function getFilteredLocaleOptions(query) {
|
|
24
19
|
const normalizedQuery = query.trim().toLowerCase();
|
|
25
20
|
const options = getLocaleOptions();
|
|
@@ -37,6 +32,6 @@ function getLocaleSearchRank(option, query) {
|
|
|
37
32
|
return 4;
|
|
38
33
|
}
|
|
39
34
|
//#endregion
|
|
40
|
-
export { getFilteredLocaleOptions,
|
|
35
|
+
export { getFilteredLocaleOptions, getLocaleOptions };
|
|
41
36
|
|
|
42
|
-
//# sourceMappingURL=
|
|
37
|
+
//# sourceMappingURL=localeOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localeOptions.js","names":[],"sources":["../../src/console/localeOptions.ts"],"sourcesContent":["import { listSupportedLocales } from '@generaltranslation/supported-locales';\nimport { getLocaleProperties } from 'generaltranslation';\n\nexport type LocaleOption = {\n code: string;\n label: string;\n name: string;\n nativeName: string;\n searchable: string;\n};\n\nlet localeOptionsCache: LocaleOption[] | undefined;\n\nexport function getLocaleOptions(): LocaleOption[] {\n localeOptionsCache ??= listSupportedLocales().map((code) => {\n const properties = getLocaleProperties(code);\n const label =\n properties.name === properties.nativeName\n ? `${code} ${properties.name}`\n : `${code} ${properties.name} / ${properties.nativeName}`;\n\n return {\n code,\n label,\n name: properties.name,\n nativeName: properties.nativeName,\n searchable:\n `${code} ${properties.name} ${properties.nativeName}`.toLowerCase(),\n };\n });\n return localeOptionsCache;\n}\n\nexport function getFilteredLocaleOptions(query: string) {\n const normalizedQuery = query.trim().toLowerCase();\n const options = getLocaleOptions();\n if (!normalizedQuery) return options;\n\n return options\n .filter((option) => option.searchable.includes(normalizedQuery))\n .sort(\n (left, right) =>\n getLocaleSearchRank(left, normalizedQuery) -\n getLocaleSearchRank(right, normalizedQuery)\n );\n}\n\nfunction getLocaleSearchRank(option: LocaleOption, query: string) {\n const code = option.code.toLowerCase();\n const name = option.name.toLowerCase();\n const nativeName = option.nativeName.toLowerCase();\n\n if (code === query) return 0;\n if (code.startsWith(query)) return 1;\n if (name.startsWith(query)) return 2;\n if (nativeName.startsWith(query)) return 3;\n return 4;\n}\n"],"mappings":";;;AAWA,IAAI;AAEJ,SAAgB,mBAAmC;AACjD,wBAAuB,sBAAsB,CAAC,KAAK,SAAS;EAC1D,MAAM,aAAa,oBAAoB,KAAK;AAM5C,SAAO;GACL;GACA,OANA,WAAW,SAAS,WAAW,aAC3B,GAAG,KAAK,IAAI,WAAW,SACvB,GAAG,KAAK,IAAI,WAAW,KAAK,KAAK,WAAW;GAKhD,MAAM,WAAW;GACjB,YAAY,WAAW;GACvB,YACE,GAAG,KAAK,GAAG,WAAW,KAAK,GAAG,WAAW,aAAa,aAAa;GACtE;GACD;AACF,QAAO;;AAGT,SAAgB,yBAAyB,OAAe;CACtD,MAAM,kBAAkB,MAAM,MAAM,CAAC,aAAa;CAClD,MAAM,UAAU,kBAAkB;AAClC,KAAI,CAAC,gBAAiB,QAAO;AAE7B,QAAO,QACJ,QAAQ,WAAW,OAAO,WAAW,SAAS,gBAAgB,CAAC,CAC/D,MACE,MAAM,UACL,oBAAoB,MAAM,gBAAgB,GAC1C,oBAAoB,OAAO,gBAAgB,CAC9C;;AAGL,SAAS,oBAAoB,QAAsB,OAAe;CAChE,MAAM,OAAO,OAAO,KAAK,aAAa;CACtC,MAAM,OAAO,OAAO,KAAK,aAAa;CACtC,MAAM,aAAa,OAAO,WAAW,aAAa;AAElD,KAAI,SAAS,MAAO,QAAO;AAC3B,KAAI,KAAK,WAAW,MAAM,CAAE,QAAO;AACnC,KAAI,KAAK,WAAW,MAAM,CAAE,QAAO;AACnC,KAAI,WAAW,WAAW,MAAM,CAAE,QAAO;AACzC,QAAO"}
|
package/dist/console/logger.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ declare class Logger {
|
|
|
17
17
|
private logFormat;
|
|
18
18
|
private logLevel;
|
|
19
19
|
private quiet;
|
|
20
|
+
private animatedProgress;
|
|
20
21
|
private consoleOutput;
|
|
21
22
|
private consoleLoggers;
|
|
22
23
|
private clackOutput;
|
|
@@ -40,6 +41,12 @@ declare class Logger {
|
|
|
40
41
|
* GT_LOG_FILE still captures everything.
|
|
41
42
|
*/
|
|
42
43
|
setQuiet(quiet: boolean): void;
|
|
44
|
+
/**
|
|
45
|
+
* Noninteractive setup reports progress as plain log lines instead of
|
|
46
|
+
* redrawing spinners and progress bars on the console. Returns the previous
|
|
47
|
+
* mode so a run can restore it.
|
|
48
|
+
*/
|
|
49
|
+
setAnimatedProgress(animated: boolean): boolean;
|
|
43
50
|
isQuiet(): boolean;
|
|
44
51
|
trace(message: string): void;
|
|
45
52
|
debug(message: string): void;
|
package/dist/console/logger.js
CHANGED
|
@@ -1,34 +1,8 @@
|
|
|
1
|
-
import { endTerminalSession } from "./terminalSession.js";
|
|
2
1
|
import { intro, log, outro, progress, spinner } from "@clack/prompts";
|
|
3
2
|
import chalk from "chalk";
|
|
4
3
|
import { randomUUID } from "node:crypto";
|
|
5
4
|
import { destination, pino } from "pino";
|
|
6
5
|
//#region src/console/logger.ts
|
|
7
|
-
function wrapTerminalSessionAware(target) {
|
|
8
|
-
const start = target.start.bind(target);
|
|
9
|
-
const stop = target.stop.bind(target);
|
|
10
|
-
const message = target.message.bind(target);
|
|
11
|
-
target.start = (msg) => {
|
|
12
|
-
endTerminalSession();
|
|
13
|
-
return start(msg);
|
|
14
|
-
};
|
|
15
|
-
target.stop = (msg, code) => {
|
|
16
|
-
endTerminalSession();
|
|
17
|
-
return stop(msg, code);
|
|
18
|
-
};
|
|
19
|
-
target.message = (msg) => {
|
|
20
|
-
endTerminalSession();
|
|
21
|
-
return message(msg);
|
|
22
|
-
};
|
|
23
|
-
if ("advance" in target) {
|
|
24
|
-
const advance = target.advance.bind(target);
|
|
25
|
-
target.advance = (amount, msg) => {
|
|
26
|
-
endTerminalSession();
|
|
27
|
-
return advance(amount, msg);
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
return target;
|
|
31
|
-
}
|
|
32
6
|
const LOG_LEVEL_RANK = {
|
|
33
7
|
debug: 10,
|
|
34
8
|
info: 20,
|
|
@@ -94,6 +68,7 @@ const logger = class Logger {
|
|
|
94
68
|
logFormat;
|
|
95
69
|
logLevel;
|
|
96
70
|
quiet = false;
|
|
71
|
+
animatedProgress = true;
|
|
97
72
|
consoleOutput = "stdout";
|
|
98
73
|
consoleLoggers = {};
|
|
99
74
|
clackOutput;
|
|
@@ -158,112 +133,100 @@ const logger = class Logger {
|
|
|
158
133
|
this.quiet = quiet;
|
|
159
134
|
if (this.pinoLogger) this.pinoLogger.level = quiet ? LOG_LEVEL_RANK[this.logLevel] > LOG_LEVEL_RANK[QUIET_FLOOR_LEVEL] ? this.logLevel : QUIET_FLOOR_LEVEL : this.logLevel;
|
|
160
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Noninteractive setup reports progress as plain log lines instead of
|
|
138
|
+
* redrawing spinners and progress bars on the console. Returns the previous
|
|
139
|
+
* mode so a run can restore it.
|
|
140
|
+
*/
|
|
141
|
+
setAnimatedProgress(animated) {
|
|
142
|
+
const previous = this.animatedProgress;
|
|
143
|
+
this.animatedProgress = animated;
|
|
144
|
+
return previous;
|
|
145
|
+
}
|
|
161
146
|
isQuiet() {
|
|
162
147
|
return this.quiet;
|
|
163
148
|
}
|
|
164
149
|
trace(message) {
|
|
165
|
-
if (!this.quiet) if (this.logFormat === "default") {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
});
|
|
171
|
-
} else this.pinoLogger?.trace(message);
|
|
150
|
+
if (!this.quiet) if (this.logFormat === "default") log.message(message, {
|
|
151
|
+
symbol: chalk.dim("•"),
|
|
152
|
+
...this.clackOutput
|
|
153
|
+
});
|
|
154
|
+
else this.pinoLogger?.trace(message);
|
|
172
155
|
this.fileLogger?.trace(message);
|
|
173
156
|
}
|
|
174
157
|
debug(message) {
|
|
175
|
-
if (!this.quiet) if (this.logFormat === "default") {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
});
|
|
181
|
-
} else this.pinoLogger?.debug(message);
|
|
158
|
+
if (!this.quiet) if (this.logFormat === "default") log.message(message, {
|
|
159
|
+
symbol: chalk.dim("◆"),
|
|
160
|
+
...this.clackOutput
|
|
161
|
+
});
|
|
162
|
+
else this.pinoLogger?.debug(message);
|
|
182
163
|
this.fileLogger?.debug(message);
|
|
183
164
|
}
|
|
184
165
|
info(message) {
|
|
185
|
-
if (!this.quiet) if (this.logFormat === "default")
|
|
186
|
-
|
|
187
|
-
log.info(message, this.clackOutput);
|
|
188
|
-
} else this.pinoLogger?.info(message);
|
|
166
|
+
if (!this.quiet) if (this.logFormat === "default") log.info(message, this.clackOutput);
|
|
167
|
+
else this.pinoLogger?.info(message);
|
|
189
168
|
this.fileLogger?.info(message);
|
|
190
169
|
}
|
|
191
170
|
warn(message) {
|
|
192
|
-
if (this.logFormat === "default")
|
|
193
|
-
|
|
194
|
-
log.warn(message, this.clackOutput);
|
|
195
|
-
} else this.pinoLogger?.warn(message);
|
|
171
|
+
if (this.logFormat === "default") log.warn(message, this.clackOutput);
|
|
172
|
+
else this.pinoLogger?.warn(message);
|
|
196
173
|
this.fileLogger?.warn(message);
|
|
197
174
|
}
|
|
198
175
|
error(message) {
|
|
199
|
-
if (this.logFormat === "default")
|
|
200
|
-
|
|
201
|
-
log.error(message, this.clackOutput);
|
|
202
|
-
} else this.pinoLogger?.error(message);
|
|
176
|
+
if (this.logFormat === "default") log.error(message, this.clackOutput);
|
|
177
|
+
else this.pinoLogger?.error(message);
|
|
203
178
|
this.fileLogger?.error(message);
|
|
204
179
|
}
|
|
205
180
|
fatal(message) {
|
|
206
|
-
if (this.logFormat === "default")
|
|
207
|
-
|
|
208
|
-
log.error(message, this.clackOutput);
|
|
209
|
-
} else this.pinoLogger?.fatal(message);
|
|
181
|
+
if (this.logFormat === "default") log.error(message, this.clackOutput);
|
|
182
|
+
else this.pinoLogger?.fatal(message);
|
|
210
183
|
this.fileLogger?.fatal(message);
|
|
211
184
|
}
|
|
212
185
|
silent(message) {
|
|
213
186
|
this.fileLogger?.silent(message);
|
|
214
187
|
}
|
|
215
188
|
success(message) {
|
|
216
|
-
if (!this.quiet) if (this.logFormat === "default")
|
|
217
|
-
|
|
218
|
-
log.success(message, this.clackOutput);
|
|
219
|
-
} else this.pinoLogger?.info(message);
|
|
189
|
+
if (!this.quiet) if (this.logFormat === "default") log.success(message, this.clackOutput);
|
|
190
|
+
else this.pinoLogger?.info(message);
|
|
220
191
|
this.fileLogger?.info(message);
|
|
221
192
|
}
|
|
222
193
|
step(message) {
|
|
223
|
-
if (!this.quiet) if (this.logFormat === "default")
|
|
224
|
-
|
|
225
|
-
log.step(message, this.clackOutput);
|
|
226
|
-
} else this.pinoLogger?.info(message);
|
|
194
|
+
if (!this.quiet) if (this.logFormat === "default") log.step(message, this.clackOutput);
|
|
195
|
+
else this.pinoLogger?.info(message);
|
|
227
196
|
this.fileLogger?.info(message);
|
|
228
197
|
}
|
|
229
198
|
message(message, symbol) {
|
|
230
|
-
if (!this.quiet) if (this.logFormat === "default") {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
} : this.clackOutput);
|
|
236
|
-
} else this.pinoLogger?.info(message);
|
|
199
|
+
if (!this.quiet) if (this.logFormat === "default") log.message(message, symbol ? {
|
|
200
|
+
symbol,
|
|
201
|
+
...this.clackOutput
|
|
202
|
+
} : this.clackOutput);
|
|
203
|
+
else this.pinoLogger?.info(message);
|
|
237
204
|
this.fileLogger?.info(message);
|
|
238
205
|
}
|
|
239
206
|
createSpinner(indicator = "timer") {
|
|
240
|
-
if (this.quiet) return new MockSpinner(this);
|
|
241
|
-
if (this.logFormat === "default") return
|
|
207
|
+
if (this.quiet || !this.animatedProgress) return new MockSpinner(this);
|
|
208
|
+
if (this.logFormat === "default") return spinner({
|
|
242
209
|
indicator,
|
|
243
210
|
...this.clackOutput
|
|
244
|
-
})
|
|
211
|
+
});
|
|
245
212
|
else return new MockSpinner(this);
|
|
246
213
|
}
|
|
247
214
|
createProgressBar(total) {
|
|
248
|
-
if (this.quiet) return new MockProgress(total, this);
|
|
249
|
-
if (this.logFormat === "default") return
|
|
215
|
+
if (this.quiet || !this.animatedProgress) return new MockProgress(total, this);
|
|
216
|
+
if (this.logFormat === "default") return progress({
|
|
250
217
|
max: total,
|
|
251
218
|
...this.clackOutput
|
|
252
|
-
})
|
|
219
|
+
});
|
|
253
220
|
else return new MockProgress(total, this);
|
|
254
221
|
}
|
|
255
222
|
startCommand(message) {
|
|
256
|
-
if (!this.quiet) if (this.logFormat === "default")
|
|
257
|
-
|
|
258
|
-
intro(chalk.cyan(message), this.clackOutput);
|
|
259
|
-
} else this.info(`╭─ ${message}`);
|
|
223
|
+
if (!this.quiet) if (this.logFormat === "default") intro(chalk.cyan(message), this.clackOutput);
|
|
224
|
+
else this.info(`╭─ ${message}`);
|
|
260
225
|
this.fileLogger?.info(`[START] ${message}`);
|
|
261
226
|
}
|
|
262
227
|
endCommand(message) {
|
|
263
|
-
if (!this.quiet) if (this.logFormat === "default")
|
|
264
|
-
|
|
265
|
-
outro(chalk.cyan(message), this.clackOutput);
|
|
266
|
-
} else this.info(`╰─ ${message}`);
|
|
228
|
+
if (!this.quiet) if (this.logFormat === "default") outro(chalk.cyan(message), this.clackOutput);
|
|
229
|
+
else this.info(`╰─ ${message}`);
|
|
267
230
|
this.fileLogger?.info(`[END] ${message}`);
|
|
268
231
|
}
|
|
269
232
|
flush() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","names":[],"sources":["../../src/console/logger.ts"],"sourcesContent":["import chalk from 'chalk';\nimport { randomUUID } from 'node:crypto';\nimport { pino, destination } from 'pino';\nimport {\n log as clackLog,\n spinner,\n progress,\n intro,\n outro,\n} from '@clack/prompts';\nimport { endTerminalSession } from './terminalSession.js';\n\nimport type { Logger as PinoLogger } from 'pino';\nimport type { SpinnerResult, ProgressResult } from '@clack/prompts';\nimport type { Writable } from 'node:stream';\n\nfunction wrapTerminalSessionAware<T extends SpinnerResult | ProgressResult>(\n target: T\n): T {\n const start = target.start.bind(target);\n const stop = target.stop.bind(target);\n const message = target.message.bind(target);\n target.start = (msg?: string) => {\n endTerminalSession();\n return start(msg);\n };\n target.stop = (msg?: string, code?: number) => {\n endTerminalSession();\n return (stop as (m?: string, c?: number) => void)(msg, code);\n };\n target.message = (msg?: string) => {\n endTerminalSession();\n return message(msg);\n };\n if ('advance' in target) {\n const advance = target.advance.bind(target);\n target.advance = (amount: number, msg?: string) => {\n endTerminalSession();\n return advance(amount, msg);\n };\n }\n return target;\n}\n\nexport type LogFormat = 'default' | 'json';\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type ConsoleOutput = 'stdout' | 'stderr';\n\n// Numeric ordering used to decide the quiet floor without lowering an\n// already-more-restrictive level chosen via GT_LOG_LEVEL.\nconst LOG_LEVEL_RANK: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\n// Under --quiet, informational output is suppressed but warnings and errors\n// still print, so the console never drops below this level.\nconst QUIET_FLOOR_LEVEL: LogLevel = 'warn';\n\n// Mock spinner that logs to console instead of updating terminal UI\nclass MockSpinner implements SpinnerResult {\n private currentMessage: string = '';\n private logger: Logger;\n isCancelled: boolean = false;\n\n constructor(logger: Logger) {\n this.logger = logger;\n }\n\n start(message?: string): void {\n if (message) {\n this.currentMessage = message;\n this.logger.info(`[Spinner] ${message}`);\n }\n }\n\n stop(message?: string): void {\n const msg = message || this.currentMessage;\n if (msg) {\n this.logger.info(`[Spinner] ${msg}`);\n }\n this.currentMessage = '';\n }\n\n message(message?: string): void {\n if (message) {\n this.currentMessage = message;\n this.logger.info(`[Spinner] ${message}`);\n }\n }\n}\n\n// Mock progress bar that logs to console instead of updating terminal UI\nclass MockProgress implements ProgressResult {\n private max: number;\n private current: number = 0;\n private logger: Logger;\n isCancelled: boolean = false;\n\n constructor(max: number, logger: Logger) {\n this.max = max;\n this.logger = logger;\n }\n\n start(message?: string): void {\n const msg = message || 'Starting progress';\n this.logger.info(`[Progress] ${msg} (0/${this.max})`);\n }\n\n stop(message?: string): void {\n const msg = message || 'Complete';\n this.logger.info(`[Progress] ${msg} (${this.current}/${this.max})`);\n }\n\n message(message?: string): void {\n if (message) {\n this.logger.info(`[Progress] ${message} (${this.current}/${this.max})`);\n }\n }\n\n advance(amount: number, message?: string): void {\n this.current += amount;\n const msg = message || 'Progress';\n this.logger.info(`[Progress] ${msg} (${this.current}/${this.max})`);\n }\n}\n\n/**\n * GT_LOG_FORMAT: default | json.\n * - If default, logs will be pretty-printed using @clack/prompts.\n * - If json, logs will be written in JSON format to the console.\n * GT_LOG_FILE: If specified, logs will be written to the file.\n * GT_LOG_LEVEL: The level of logs to write. If not specified, defaults to 'info'.\n * - Valid levels: debug, info, warn, error.\n */\nclass Logger {\n private static instance: Logger;\n private pinoLogger: PinoLogger | null = null;\n private fileLogger: PinoLogger | null = null;\n private logFormat: LogFormat;\n private logLevel: LogLevel;\n private quiet = false;\n private consoleOutput: ConsoleOutput = 'stdout';\n // One JSON console logger per stream so switching direction within a\n // process reuses the existing SonicBoom instead of recreating it.\n private consoleLoggers: Partial<Record<ConsoleOutput, PinoLogger>> = {};\n // Clack writes to stdout unless told otherwise; unset keeps its default so\n // ordinary commands are untouched while stderr routing redirects it.\n private clackOutput: { output: Writable } | undefined;\n\n private constructor() {\n // Read configuration from environment variables\n const format = (\n process.env.GT_LOG_FORMAT || 'default'\n ).toLowerCase() as LogFormat;\n const logFile = process.env.GT_LOG_FILE;\n const logLevel = process.env.GT_LOG_LEVEL || 'info';\n\n if (format !== 'default' && format !== 'json') {\n console.error('Invalid log format');\n process.exit(1);\n }\n if (\n logLevel !== 'debug' &&\n logLevel !== 'info' &&\n logLevel !== 'warn' &&\n logLevel !== 'error'\n ) {\n console.error('Invalid log level');\n process.exit(1);\n }\n\n this.logFormat = format;\n this.logLevel = logLevel as LogLevel;\n\n // Console output (stdout) - only for JSON format\n // For 'default' format, we use @clack/prompts directly\n if (format === 'json') {\n this.pinoLogger = this.createPinoLogger(process.stdout.fd, logLevel);\n this.consoleLoggers.stdout = this.pinoLogger;\n }\n\n // File output (if specified) - always JSON format\n if (logFile) {\n this.fileLogger = this.createPinoLogger(logFile, logLevel);\n }\n }\n\n private createPinoLogger(dest: number | string, level: string): PinoLogger {\n return pino(\n {\n level,\n mixin: () => ({\n logId: randomUUID(),\n }),\n },\n destination(dest)\n );\n }\n\n static getInstance(): Logger {\n if (!Logger.instance) {\n Logger.instance = new Logger();\n }\n return Logger.instance;\n }\n\n /**\n * Route console output (Clack chatter and the JSON console logger) to\n * stdout (the historical default) or stderr, so a command whose stdout is\n * machine-readable can keep diagnostics off it. Reversible per invocation;\n * a no-op when the direction is unchanged. The JSON logger keeps its\n * current level and is flushed before switching. File logging is unaffected.\n */\n setConsoleOutput(output: ConsoleOutput): void {\n if (output === this.consoleOutput) return;\n this.consoleOutput = output;\n this.clackOutput =\n output === 'stderr' ? { output: process.stderr } : undefined;\n if (this.pinoLogger) {\n this.pinoLogger.flush();\n const next =\n this.consoleLoggers[output] ??\n this.createPinoLogger(process[output].fd, this.pinoLogger.level);\n next.level = this.pinoLogger.level;\n this.consoleLoggers[output] = next;\n this.pinoLogger = next;\n }\n }\n\n /**\n * Enable or disable quiet mode. When quiet, informational chatter\n * (info/step/success/message/debug/trace, spinners, progress bars, and\n * command intro/outro markers) is suppressed on the console, while warnings\n * and errors still print. Quiet wins over GT_LOG_LEVEL but never lowers an\n * already-more-restrictive level. File logging is unaffected so an explicit\n * GT_LOG_FILE still captures everything.\n */\n setQuiet(quiet: boolean): void {\n this.quiet = quiet;\n if (this.pinoLogger) {\n this.pinoLogger.level = quiet\n ? LOG_LEVEL_RANK[this.logLevel] > LOG_LEVEL_RANK[QUIET_FLOOR_LEVEL]\n ? this.logLevel\n : QUIET_FLOOR_LEVEL\n : this.logLevel;\n }\n }\n\n isQuiet(): boolean {\n return this.quiet;\n }\n\n // Standard logging methods\n trace(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n endTerminalSession();\n // @clack/prompts doesn't have trace, use message\n clackLog.message(message, {\n symbol: chalk.dim('•'),\n ...this.clackOutput,\n });\n } else {\n this.pinoLogger?.trace(message);\n }\n }\n this.fileLogger?.trace(message);\n }\n\n debug(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n endTerminalSession();\n // @clack/prompts doesn't have debug, use message\n clackLog.message(message, {\n symbol: chalk.dim('◆'),\n ...this.clackOutput,\n });\n } else {\n this.pinoLogger?.debug(message);\n }\n }\n this.fileLogger?.debug(message);\n }\n\n info(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n endTerminalSession();\n clackLog.info(message, this.clackOutput);\n } else {\n this.pinoLogger?.info(message);\n }\n }\n this.fileLogger?.info(message);\n }\n\n warn(message: string): void {\n if (this.logFormat === 'default') {\n endTerminalSession();\n clackLog.warn(message, this.clackOutput);\n } else {\n this.pinoLogger?.warn(message);\n }\n this.fileLogger?.warn(message);\n }\n\n error(message: string): void {\n if (this.logFormat === 'default') {\n endTerminalSession();\n clackLog.error(message, this.clackOutput);\n } else {\n this.pinoLogger?.error(message);\n }\n this.fileLogger?.error(message);\n }\n\n fatal(message: string): void {\n if (this.logFormat === 'default') {\n endTerminalSession();\n // @clack/prompts doesn't have fatal, use error\n clackLog.error(message, this.clackOutput);\n } else {\n this.pinoLogger?.fatal(message);\n }\n this.fileLogger?.fatal(message);\n }\n\n silent(message: string): void {\n // Silent doesn't log to console, only to file\n this.fileLogger?.silent(message);\n }\n\n // @clack/prompts specific methods (for 'default' format)\n success(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n endTerminalSession();\n clackLog.success(message, this.clackOutput);\n } else {\n this.pinoLogger?.info(message); // Map to info for non-default formats\n }\n }\n this.fileLogger?.info(message);\n }\n\n step(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n endTerminalSession();\n clackLog.step(message, this.clackOutput);\n } else {\n this.pinoLogger?.info(message); // Map to info for non-default formats\n }\n }\n this.fileLogger?.info(message);\n }\n\n message(message: string, symbol?: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n endTerminalSession();\n clackLog.message(\n message,\n symbol ? { symbol, ...this.clackOutput } : this.clackOutput\n );\n } else {\n this.pinoLogger?.info(message); // Map to info for non-default formats\n }\n }\n this.fileLogger?.info(message);\n }\n\n // Spinner functionality\n createSpinner(indicator: 'dots' | 'timer' = 'timer'): SpinnerResult {\n // Quiet mode suppresses spinner UI; the mock routes through the gated\n // info() so nothing reaches the console while file logging is preserved.\n if (this.quiet) {\n return new MockSpinner(this);\n }\n if (this.logFormat === 'default') {\n return wrapTerminalSessionAware(\n spinner({ indicator, ...this.clackOutput })\n );\n } else {\n return new MockSpinner(this);\n }\n }\n\n // Progress bar functionality\n createProgressBar(total: number): ProgressResult {\n if (this.quiet) {\n return new MockProgress(total, this);\n }\n if (this.logFormat === 'default') {\n return wrapTerminalSessionAware(\n progress({ max: total, ...this.clackOutput })\n );\n } else {\n return new MockProgress(total, this);\n }\n }\n\n // Command start/end markers\n startCommand(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n endTerminalSession();\n intro(chalk.cyan(message), this.clackOutput);\n } else {\n this.info(`╭─ ${message}`);\n }\n }\n this.fileLogger?.info(`[START] ${message}`);\n }\n\n endCommand(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n endTerminalSession();\n outro(chalk.cyan(message), this.clackOutput);\n } else {\n this.info(`╰─ ${message}`);\n }\n }\n this.fileLogger?.info(`[END] ${message}`);\n }\n\n // Flush logs to ensure they're written before process exit\n flush(): void {\n this.pinoLogger?.flush();\n this.fileLogger?.flush();\n }\n}\n\nexport const logger = Logger.getInstance();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"logger.js","names":[],"sources":["../../src/console/logger.ts"],"sourcesContent":["import chalk from 'chalk';\nimport { randomUUID } from 'node:crypto';\nimport { pino, destination } from 'pino';\nimport {\n log as clackLog,\n spinner,\n progress,\n intro,\n outro,\n} from '@clack/prompts';\nimport type { Logger as PinoLogger } from 'pino';\nimport type { SpinnerResult, ProgressResult } from '@clack/prompts';\nimport type { Writable } from 'node:stream';\n\nexport type LogFormat = 'default' | 'json';\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type ConsoleOutput = 'stdout' | 'stderr';\n\n// Numeric ordering used to decide the quiet floor without lowering an\n// already-more-restrictive level chosen via GT_LOG_LEVEL.\nconst LOG_LEVEL_RANK: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\n// Under --quiet, informational output is suppressed but warnings and errors\n// still print, so the console never drops below this level.\nconst QUIET_FLOOR_LEVEL: LogLevel = 'warn';\n\n// Mock spinner that logs to console instead of updating terminal UI\nclass MockSpinner implements SpinnerResult {\n private currentMessage: string = '';\n private logger: Logger;\n isCancelled: boolean = false;\n\n constructor(logger: Logger) {\n this.logger = logger;\n }\n\n start(message?: string): void {\n if (message) {\n this.currentMessage = message;\n this.logger.info(`[Spinner] ${message}`);\n }\n }\n\n stop(message?: string): void {\n const msg = message || this.currentMessage;\n if (msg) {\n this.logger.info(`[Spinner] ${msg}`);\n }\n this.currentMessage = '';\n }\n\n message(message?: string): void {\n if (message) {\n this.currentMessage = message;\n this.logger.info(`[Spinner] ${message}`);\n }\n }\n}\n\n// Mock progress bar that logs to console instead of updating terminal UI\nclass MockProgress implements ProgressResult {\n private max: number;\n private current: number = 0;\n private logger: Logger;\n isCancelled: boolean = false;\n\n constructor(max: number, logger: Logger) {\n this.max = max;\n this.logger = logger;\n }\n\n start(message?: string): void {\n const msg = message || 'Starting progress';\n this.logger.info(`[Progress] ${msg} (0/${this.max})`);\n }\n\n stop(message?: string): void {\n const msg = message || 'Complete';\n this.logger.info(`[Progress] ${msg} (${this.current}/${this.max})`);\n }\n\n message(message?: string): void {\n if (message) {\n this.logger.info(`[Progress] ${message} (${this.current}/${this.max})`);\n }\n }\n\n advance(amount: number, message?: string): void {\n this.current += amount;\n const msg = message || 'Progress';\n this.logger.info(`[Progress] ${msg} (${this.current}/${this.max})`);\n }\n}\n\n/**\n * GT_LOG_FORMAT: default | json.\n * - If default, logs will be pretty-printed using @clack/prompts.\n * - If json, logs will be written in JSON format to the console.\n * GT_LOG_FILE: If specified, logs will be written to the file.\n * GT_LOG_LEVEL: The level of logs to write. If not specified, defaults to 'info'.\n * - Valid levels: debug, info, warn, error.\n */\nclass Logger {\n private static instance: Logger;\n private pinoLogger: PinoLogger | null = null;\n private fileLogger: PinoLogger | null = null;\n private logFormat: LogFormat;\n private logLevel: LogLevel;\n private quiet = false;\n private animatedProgress = true;\n private consoleOutput: ConsoleOutput = 'stdout';\n // One JSON console logger per stream so switching direction within a\n // process reuses the existing SonicBoom instead of recreating it.\n private consoleLoggers: Partial<Record<ConsoleOutput, PinoLogger>> = {};\n // Clack writes to stdout unless told otherwise; unset keeps its default so\n // ordinary commands are untouched while stderr routing redirects it.\n private clackOutput: { output: Writable } | undefined;\n\n private constructor() {\n // Read configuration from environment variables\n const format = (\n process.env.GT_LOG_FORMAT || 'default'\n ).toLowerCase() as LogFormat;\n const logFile = process.env.GT_LOG_FILE;\n const logLevel = process.env.GT_LOG_LEVEL || 'info';\n\n if (format !== 'default' && format !== 'json') {\n console.error('Invalid log format');\n process.exit(1);\n }\n if (\n logLevel !== 'debug' &&\n logLevel !== 'info' &&\n logLevel !== 'warn' &&\n logLevel !== 'error'\n ) {\n console.error('Invalid log level');\n process.exit(1);\n }\n\n this.logFormat = format;\n this.logLevel = logLevel as LogLevel;\n\n // Console output (stdout) - only for JSON format\n // For 'default' format, we use @clack/prompts directly\n if (format === 'json') {\n this.pinoLogger = this.createPinoLogger(process.stdout.fd, logLevel);\n this.consoleLoggers.stdout = this.pinoLogger;\n }\n\n // File output (if specified) - always JSON format\n if (logFile) {\n this.fileLogger = this.createPinoLogger(logFile, logLevel);\n }\n }\n\n private createPinoLogger(dest: number | string, level: string): PinoLogger {\n return pino(\n {\n level,\n mixin: () => ({\n logId: randomUUID(),\n }),\n },\n destination(dest)\n );\n }\n\n static getInstance(): Logger {\n if (!Logger.instance) {\n Logger.instance = new Logger();\n }\n return Logger.instance;\n }\n\n /**\n * Route console output (Clack chatter and the JSON console logger) to\n * stdout (the historical default) or stderr, so a command whose stdout is\n * machine-readable can keep diagnostics off it. Reversible per invocation;\n * a no-op when the direction is unchanged. The JSON logger keeps its\n * current level and is flushed before switching. File logging is unaffected.\n */\n setConsoleOutput(output: ConsoleOutput): void {\n if (output === this.consoleOutput) return;\n this.consoleOutput = output;\n this.clackOutput =\n output === 'stderr' ? { output: process.stderr } : undefined;\n if (this.pinoLogger) {\n this.pinoLogger.flush();\n const next =\n this.consoleLoggers[output] ??\n this.createPinoLogger(process[output].fd, this.pinoLogger.level);\n next.level = this.pinoLogger.level;\n this.consoleLoggers[output] = next;\n this.pinoLogger = next;\n }\n }\n\n /**\n * Enable or disable quiet mode. When quiet, informational chatter\n * (info/step/success/message/debug/trace, spinners, progress bars, and\n * command intro/outro markers) is suppressed on the console, while warnings\n * and errors still print. Quiet wins over GT_LOG_LEVEL but never lowers an\n * already-more-restrictive level. File logging is unaffected so an explicit\n * GT_LOG_FILE still captures everything.\n */\n setQuiet(quiet: boolean): void {\n this.quiet = quiet;\n if (this.pinoLogger) {\n this.pinoLogger.level = quiet\n ? LOG_LEVEL_RANK[this.logLevel] > LOG_LEVEL_RANK[QUIET_FLOOR_LEVEL]\n ? this.logLevel\n : QUIET_FLOOR_LEVEL\n : this.logLevel;\n }\n }\n\n /**\n * Noninteractive setup reports progress as plain log lines instead of\n * redrawing spinners and progress bars on the console. Returns the previous\n * mode so a run can restore it.\n */\n setAnimatedProgress(animated: boolean): boolean {\n const previous = this.animatedProgress;\n this.animatedProgress = animated;\n return previous;\n }\n\n isQuiet(): boolean {\n return this.quiet;\n }\n\n // Standard logging methods\n trace(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n // @clack/prompts doesn't have trace, use message\n clackLog.message(message, {\n symbol: chalk.dim('•'),\n ...this.clackOutput,\n });\n } else {\n this.pinoLogger?.trace(message);\n }\n }\n this.fileLogger?.trace(message);\n }\n\n debug(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n // @clack/prompts doesn't have debug, use message\n clackLog.message(message, {\n symbol: chalk.dim('◆'),\n ...this.clackOutput,\n });\n } else {\n this.pinoLogger?.debug(message);\n }\n }\n this.fileLogger?.debug(message);\n }\n\n info(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n clackLog.info(message, this.clackOutput);\n } else {\n this.pinoLogger?.info(message);\n }\n }\n this.fileLogger?.info(message);\n }\n\n warn(message: string): void {\n if (this.logFormat === 'default') {\n clackLog.warn(message, this.clackOutput);\n } else {\n this.pinoLogger?.warn(message);\n }\n this.fileLogger?.warn(message);\n }\n\n error(message: string): void {\n if (this.logFormat === 'default') {\n clackLog.error(message, this.clackOutput);\n } else {\n this.pinoLogger?.error(message);\n }\n this.fileLogger?.error(message);\n }\n\n fatal(message: string): void {\n if (this.logFormat === 'default') {\n // @clack/prompts doesn't have fatal, use error\n clackLog.error(message, this.clackOutput);\n } else {\n this.pinoLogger?.fatal(message);\n }\n this.fileLogger?.fatal(message);\n }\n\n silent(message: string): void {\n // Silent doesn't log to console, only to file\n this.fileLogger?.silent(message);\n }\n\n // @clack/prompts specific methods (for 'default' format)\n success(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n clackLog.success(message, this.clackOutput);\n } else {\n this.pinoLogger?.info(message); // Map to info for non-default formats\n }\n }\n this.fileLogger?.info(message);\n }\n\n step(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n clackLog.step(message, this.clackOutput);\n } else {\n this.pinoLogger?.info(message); // Map to info for non-default formats\n }\n }\n this.fileLogger?.info(message);\n }\n\n message(message: string, symbol?: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n clackLog.message(\n message,\n symbol ? { symbol, ...this.clackOutput } : this.clackOutput\n );\n } else {\n this.pinoLogger?.info(message); // Map to info for non-default formats\n }\n }\n this.fileLogger?.info(message);\n }\n\n // Spinner functionality\n createSpinner(indicator: 'dots' | 'timer' = 'timer'): SpinnerResult {\n // Quiet mode suppresses spinner UI; the mock routes through the gated\n // info() so nothing reaches the console while file logging is preserved.\n if (this.quiet || !this.animatedProgress) {\n return new MockSpinner(this);\n }\n if (this.logFormat === 'default') {\n return spinner({ indicator, ...this.clackOutput });\n } else {\n return new MockSpinner(this);\n }\n }\n\n // Progress bar functionality\n createProgressBar(total: number): ProgressResult {\n if (this.quiet || !this.animatedProgress) {\n return new MockProgress(total, this);\n }\n if (this.logFormat === 'default') {\n return progress({ max: total, ...this.clackOutput });\n } else {\n return new MockProgress(total, this);\n }\n }\n\n // Command start/end markers\n startCommand(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n intro(chalk.cyan(message), this.clackOutput);\n } else {\n this.info(`╭─ ${message}`);\n }\n }\n this.fileLogger?.info(`[START] ${message}`);\n }\n\n endCommand(message: string): void {\n if (!this.quiet) {\n if (this.logFormat === 'default') {\n outro(chalk.cyan(message), this.clackOutput);\n } else {\n this.info(`╰─ ${message}`);\n }\n }\n this.fileLogger?.info(`[END] ${message}`);\n }\n\n // Flush logs to ensure they're written before process exit\n flush(): void {\n this.pinoLogger?.flush();\n this.fileLogger?.flush();\n }\n}\n\nexport const logger = Logger.getInstance();\n"],"mappings":"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"}
|