From 03cbea61e7ca77a814099d6b41f98df7041ee6f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 21:17:07 +0000 Subject: [PATCH 1/2] Initial plan From 6f4ddaa323d2c8401827cf3f0a38ea8a76ee2d69 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 21:24:52 +0000 Subject: [PATCH 2/2] fix: address all SonarQube findings for PR #81 - Extract nested ternary in prog.ts (S3358) - Convert for loops to for-of in build-utils.ts (S4138 x3) - Extract walk helper to reduce cognitive complexity in build-utils.ts (S3776 x2) - Consolidate fs imports to node:fs in cli.ts (S7772, S3863) - Replace findIndex with indexOf in cli.ts (S7753) - Remove useless empty objects in config-loader.ts (S7744 x2) - Extract helpers to reduce loadConfig cognitive complexity (S3776) Co-authored-by: ThePlenkov <6381507+ThePlenkov@users.noreply.github.com> --- packages/adt-cli/src/lib/cli.ts | 7 +- packages/adt-config/src/config-loader.ts | 87 ++++++++----------- .../src/lib/handlers/objects/prog.ts | 11 +-- packages/ts-xsd/src/xml/build-utils.ts | 76 ++++++++-------- 4 files changed, 83 insertions(+), 98 deletions(-) diff --git a/packages/adt-cli/src/lib/cli.ts b/packages/adt-cli/src/lib/cli.ts index eaff6cb..1ac9f7e 100644 --- a/packages/adt-cli/src/lib/cli.ts +++ b/packages/adt-cli/src/lib/cli.ts @@ -25,11 +25,10 @@ import { refreshCommand } from './commands/auth/refresh'; // Add '@abapify/adt-export/commands/export' to adt.config.ts commands array to enable import { createCliLogger, AVAILABLE_COMPONENTS } from './utils/logger-config'; import { setCliContext } from './utils/adt-client-v2'; -import { existsSync } from 'fs'; +import { existsSync, readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; import { loadCommandPlugins, loadStaticPlugins } from './plugin-loader'; import type { CliCommandPlugin } from '@abapify/adt-plugin'; -import { readFileSync } from 'fs'; -import { resolve } from 'path'; // Check for insecure SSL flag in stored session and apply it globally function applyInsecureSslFlag(): void { @@ -79,7 +78,7 @@ ${globalOptions} } function getResolvedConfigPath(argv: string[]): string | undefined { - const configArgIndex = argv.findIndex((arg) => arg === '--config'); + const configArgIndex = argv.indexOf('--config'); if (configArgIndex !== -1) { return argv[configArgIndex + 1]; } diff --git a/packages/adt-config/src/config-loader.ts b/packages/adt-config/src/config-loader.ts index 9ada6b4..ebbff16 100644 --- a/packages/adt-config/src/config-loader.ts +++ b/packages/adt-config/src/config-loader.ts @@ -182,8 +182,8 @@ function mergeLocalConfig( ...baseConfig, ...overlayConfig, destinations: { - ...(baseConfig.destinations || {}), - ...(overlayConfig.destinations || {}), + ...baseConfig.destinations, + ...overlayConfig.destinations, }, }; } @@ -226,6 +226,35 @@ export interface LoadConfigOptions { cwd?: string; } +/** + * Try loading a config file from an explicit path (JSON or TS/JS). + */ +async function loadExplicitConfig( + configPath: string, +): Promise { + if (configPath.endsWith('.json')) { + return loadJsonConfig(configPath); + } + return loadTsConfig(configPath); +} + +/** + * Discover a config file by trying multiple extensions in priority order: + * .ts → .js → .json + */ +async function discoverConfig( + basePath: string, + baseName: string, +): Promise { + const tsConfig = await loadTsConfig(join(basePath, `${baseName}.ts`)); + if (tsConfig) return tsConfig; + + const jsConfig = await loadTsConfig(join(basePath, `${baseName}.js`)); + if (jsConfig) return jsConfig; + + return loadJsonConfig(join(basePath, `${baseName}.json`)); +} + /** * Load configuration with precedence: * 1. Explicit configPath (if provided via --config flag) @@ -246,17 +275,9 @@ export async function loadConfig( // If explicit config path provided, use it directly if (opts.configPath) { const configPath = resolve(cwd, opts.configPath); - if (configPath.endsWith('.json')) { - const jsonConfig = loadJsonConfig(configPath); - if (jsonConfig) { - return createLoadedConfig(mergeWithGlobal(jsonConfig)); - } - } else { - // Assume TS/JS config - const tsConfig = await loadTsConfig(configPath); - if (tsConfig) { - return createLoadedConfig(mergeWithGlobal(tsConfig)); - } + const config = await loadExplicitConfig(configPath); + if (config) { + return createLoadedConfig(mergeWithGlobal(config)); } // Config path specified but file not found/loadable - continue with auto-discovery console.warn( @@ -265,46 +286,10 @@ export async function loadConfig( } // Discover base local config (adt.config.*) - let baseLocalConfig: AdtConfig | null = null; - - const tsConfigPath = join(cwd, 'adt.config.ts'); - const tsConfig = await loadTsConfig(tsConfigPath); - if (tsConfig) { - baseLocalConfig = tsConfig; - } else { - const jsConfigPath = join(cwd, 'adt.config.js'); - const jsConfig = await loadTsConfig(jsConfigPath); - if (jsConfig) { - baseLocalConfig = jsConfig; - } else { - const jsonConfigPath = join(cwd, 'adt.config.json'); - const jsonConfig = loadJsonConfig(jsonConfigPath); - if (jsonConfig) { - baseLocalConfig = jsonConfig; - } - } - } + const baseLocalConfig = await discoverConfig(cwd, 'adt.config'); // Discover optional local override (.adt/config.*) - let localOverrideConfig: AdtConfig | null = null; - - const dotAdtTsConfigPath = join(cwd, '.adt', 'config.ts'); - const dotAdtTsConfig = await loadTsConfig(dotAdtTsConfigPath); - if (dotAdtTsConfig) { - localOverrideConfig = dotAdtTsConfig; - } else { - const dotAdtJsConfigPath = join(cwd, '.adt', 'config.js'); - const dotAdtJsConfig = await loadTsConfig(dotAdtJsConfigPath); - if (dotAdtJsConfig) { - localOverrideConfig = dotAdtJsConfig; - } else { - const dotAdtJsonConfigPath = join(cwd, '.adt', 'config.json'); - const dotAdtJsonConfig = loadJsonConfig(dotAdtJsonConfigPath); - if (dotAdtJsonConfig) { - localOverrideConfig = dotAdtJsonConfig; - } - } - } + const localOverrideConfig = await discoverConfig(join(cwd, '.adt'), 'config'); // Discover optional destinations directory (.adt/destinations/*.json) const destinationsDirConfig = loadDestinationsDirectory(cwd); diff --git a/packages/adt-plugin-abapgit/src/lib/handlers/objects/prog.ts b/packages/adt-plugin-abapgit/src/lib/handlers/objects/prog.ts index 70204e2..0168328 100644 --- a/packages/adt-plugin-abapgit/src/lib/handlers/objects/prog.ts +++ b/packages/adt-plugin-abapgit/src/lib/handlers/objects/prog.ts @@ -31,11 +31,12 @@ export const programHandler = createHandler(AdkProgram, { fromAbapGit: ({ PROGDIR, TPOOL }) => { // Extract description from TPOOL (text pool) if available // TPOOL.item can be a single object or an array - const descriptionEntry = Array.isArray(TPOOL?.item) - ? TPOOL.item.find((t: { ID?: string }) => t.ID === 'R') - : TPOOL?.item?.ID === 'R' - ? TPOOL.item - : undefined; + let descriptionEntry: { ID?: string; ENTRY?: string } | undefined; + if (Array.isArray(TPOOL?.item)) { + descriptionEntry = TPOOL.item.find((t: { ID?: string }) => t.ID === 'R'); + } else if (TPOOL?.item?.ID === 'R') { + descriptionEntry = TPOOL.item; + } // SUBC can be parsed as number or string depending on XML parser const subc = String(PROGDIR?.SUBC ?? ''); diff --git a/packages/ts-xsd/src/xml/build-utils.ts b/packages/ts-xsd/src/xml/build-utils.ts index 8a5cfef..a74e8fc 100644 --- a/packages/ts-xsd/src/xml/build-utils.ts +++ b/packages/ts-xsd/src/xml/build-utils.ts @@ -148,6 +148,42 @@ export function createRootElement( * element to be emitted without a prefix, the xmlns declaration is still * required for the document to be in the correct namespace. */ +/** + * Collect prefixes used by elements and attributes in the tree (recursive walk). + */ +function collectUsedPrefixes( + node: XmlElement, + usedPrefixes: Set, +): void { + // Check the element's own tag for a prefix + const tagParts = node.tagName.split(':'); + if (tagParts.length === 2) { + usedPrefixes.add(tagParts[0]); + } + + // Check attributes (skip xmlns declarations themselves) + if (node.attributes) { + for (const attr of Array.from(node.attributes)) { + if (attr.name.startsWith('xmlns:') || attr.name === 'xmlns') { + continue; + } + const attrParts = attr.name.split(':'); + if (attrParts.length === 2) { + usedPrefixes.add(attrParts[0]); + } + } + } + + // Recurse into child elements + if (node.childNodes) { + for (const child of Array.from(node.childNodes)) { + if (child.nodeType === 1) { + collectUsedPrefixes(child as XmlElement, usedPrefixes); + } + } + } +} + export function stripUnusedNamespaces( root: XmlElement, schema?: SchemaLike, @@ -164,49 +200,13 @@ export function stripUnusedNamespaces( } } - function walk(node: XmlElement): void { - // Check the element's own tag for a prefix - const tagParts = node.tagName.split(':'); - if (tagParts.length === 2) { - usedPrefixes.add(tagParts[0]); - } - - // Check attributes (skip xmlns declarations themselves) - const attrs = node.attributes; - if (attrs) { - for (let i = 0; i < attrs.length; i++) { - const attr = attrs[i]; - if (attr.name.startsWith('xmlns:') || attr.name === 'xmlns') { - continue; - } - const attrParts = attr.name.split(':'); - if (attrParts.length === 2) { - usedPrefixes.add(attrParts[0]); - } - } - } - - // Recurse into child elements - const children = node.childNodes; - if (children) { - for (let i = 0; i < children.length; i++) { - const child = children[i]; - if (child.nodeType === 1) { - // ELEMENT_NODE - walk(child as XmlElement); - } - } - } - } - - walk(root); + collectUsedPrefixes(root, usedPrefixes); // Remove xmlns:prefix declarations where prefix is not used const toRemove: string[] = []; const attrs = root.attributes; if (attrs) { - for (let i = 0; i < attrs.length; i++) { - const attr = attrs[i]; + for (const attr of Array.from(attrs)) { if (attr.name.startsWith('xmlns:')) { const pfx = attr.name.substring(6); if (!usedPrefixes.has(pfx)) {