abapgit-agent 1.14.3 → 1.14.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abapgit-agent",
3
- "version": "1.14.3",
3
+ "version": "1.14.4",
4
4
  "description": "ABAP Git Agent - Pull and activate ABAP code via abapGit from any git repository",
5
5
  "files": [
6
6
  "bin/",
@@ -66,29 +66,44 @@ module.exports = {
66
66
  const allFiles = [...new Set([...abapFiles, ...depFiles])];
67
67
  cfg.global.files = allFiles.map(f => `/${f}`);
68
68
 
69
- // Exclude dependency files from reporting — they are included only for
70
- // cross-reference resolution. Only the originally changed files are reported on.
71
- const abapFilesSet = new Set(abapFiles);
72
- const excludedDeps = depFiles
73
- .filter(f => !abapFilesSet.has(f))
74
- .map(f => `/${f}`);
75
- if (excludedDeps.length > 0) {
76
- cfg.global.exclude = [...new Set([...(cfg.global.exclude || []), ...excludedDeps])];
77
- }
78
-
79
69
  const scopedConfig = '.abaplint-local.json';
80
70
  fs.writeFileSync(scopedConfig, JSON.stringify(cfg, null, 2));
81
71
 
82
72
  // ── Run abaplint ──────────────────────────────────────────────────────────
73
+ // Dep files are included in the scoped config so abaplint can resolve
74
+ // cross-references (e.g. implement_methods needs the interface source).
75
+ // When producing checkstyle output (CI mode), post-filter the XML to only
76
+ // keep <file> blocks for the originally changed files — suppressing any
77
+ // pre-existing issues in dependency files that were not part of this change.
83
78
  try {
84
- const formatArgs = outformat ? `--outformat ${outformat}` : '';
85
- const fileArgs = outfile ? `--outfile ${outfile}` : '';
86
- const result = spawnSync(
87
- `npx @abaplint/cli@latest ${scopedConfig} ${formatArgs} ${fileArgs}`,
88
- { stdio: 'inherit', shell: true }
89
- );
90
- if (result.status !== 0) {
91
- process.exitCode = result.status;
79
+ if (outformat === 'checkstyle') {
80
+ // Run to a temp file, filter, then write to the final destination.
81
+ const tempOut = '.abaplint-raw.xml';
82
+ const abapFilesSet = new Set(abapFiles.map(f => path.resolve(f)));
83
+ try {
84
+ const result = spawnSync(
85
+ `npx @abaplint/cli@latest ${scopedConfig} --outformat checkstyle --outfile ${tempOut}`,
86
+ { stdio: 'pipe', shell: true }
87
+ );
88
+ const raw = fs.existsSync(tempOut) ? fs.readFileSync(tempOut, 'utf8') : '<checkstyle version="8.0"/>';
89
+ const filtered = filterCheckstyleToFiles(raw, abapFilesSet);
90
+ if (outfile) {
91
+ fs.writeFileSync(outfile, filtered);
92
+ } else {
93
+ process.stdout.write(filtered);
94
+ }
95
+ const issueCount = (filtered.match(/<error /g) || []).length;
96
+ if (issueCount > 0) process.exitCode = 1;
97
+ } finally {
98
+ if (fs.existsSync(tempOut)) fs.unlinkSync(tempOut);
99
+ }
100
+ } else {
101
+ // Interactive: inherit stdio so abaplint's human-readable output flows through.
102
+ const result = spawnSync(
103
+ `npx @abaplint/cli@latest ${scopedConfig}`,
104
+ { stdio: 'inherit', shell: true }
105
+ );
106
+ if (result.status !== 0) process.exitCode = result.status;
92
107
  }
93
108
  } finally {
94
109
  fs.unlinkSync(scopedConfig);
@@ -178,7 +193,7 @@ function resolveDependencies(abapFiles, fileIndex) {
178
193
 
179
194
  // Patterns to extract referenced object names from ABAP source
180
195
  const patterns = [
181
- /^\s*INTERFACES\s+(\w+)\s*\./gim,
196
+ /^\s*INTERFACES:?\s+(\w+)\s*\./gim,
182
197
  /INHERITING\s+FROM\s+(\w+)/gim,
183
198
  /TYPE\s+REF\s+TO\s+(\w+)/gim,
184
199
  ];
@@ -238,6 +253,30 @@ function resolveDependencies(abapFiles, fileIndex) {
238
253
  return [...deps];
239
254
  }
240
255
 
256
+ /**
257
+ * Filter a checkstyle XML string to only include <file> blocks whose name
258
+ * attribute resolves to one of the files in the given Set of absolute paths.
259
+ * The outer <checkstyle> wrapper is preserved; the version attribute is kept.
260
+ */
261
+ function filterCheckstyleToFiles(xml, abapFilesSet) {
262
+ // Extract the opening <checkstyle ...> tag (preserves version= attribute).
263
+ const headerMatch = xml.match(/^[\s\S]*?(<checkstyle[^>]*>)/);
264
+ const header = headerMatch ? headerMatch[1] : '<checkstyle version="8.0">';
265
+
266
+ // Match each <file name="...">...</file> block (including self-closing).
267
+ const fileBlockRe = /<file\s+name="([^"]*)"[\s\S]*?<\/file>|<file\s+name="([^"]*)"\s*\/>/g;
268
+ const kept = [];
269
+ let match;
270
+ while ((match = fileBlockRe.exec(xml)) !== null) {
271
+ const filePath = match[1] || match[2];
272
+ if (abapFilesSet.has(path.resolve(filePath))) {
273
+ kept.push(match[0]);
274
+ }
275
+ }
276
+
277
+ return `<?xml version="1.0" encoding="UTF-8"?>\n${header}\n${kept.join('\n')}${kept.length ? '\n' : ''}</checkstyle>\n`;
278
+ }
279
+
241
280
  /**
242
281
  * Keep only files that look like ABAP source files
243
282
  * (name.type.abap or name.type.subtype.abap).