abapgit-agent 1.14.1 → 1.14.3
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 +1 -1
- package/src/commands/lint.js +61 -9
package/package.json
CHANGED
package/src/commands/lint.js
CHANGED
|
@@ -61,10 +61,21 @@ module.exports = {
|
|
|
61
61
|
// Scope to changed files + their direct dependencies (interfaces, superclasses)
|
|
62
62
|
// so abaplint can resolve cross-references without including the whole repo.
|
|
63
63
|
const abapDir = cfg.global.files.replace(/\/\*\*.*$/, '').replace(/^\//, '') || 'abap';
|
|
64
|
-
const
|
|
64
|
+
const fileIndex = buildFileIndex(abapDir);
|
|
65
|
+
const depFiles = resolveDependencies(abapFiles, fileIndex);
|
|
65
66
|
const allFiles = [...new Set([...abapFiles, ...depFiles])];
|
|
66
67
|
cfg.global.files = allFiles.map(f => `/${f}`);
|
|
67
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
|
+
|
|
68
79
|
const scopedConfig = '.abaplint-local.json';
|
|
69
80
|
fs.writeFileSync(scopedConfig, JSON.stringify(cfg, null, 2));
|
|
70
81
|
|
|
@@ -123,6 +134,30 @@ function runGit(cmd) {
|
|
|
123
134
|
}
|
|
124
135
|
}
|
|
125
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Build a map of basename → full path for all .abap and .xml files
|
|
139
|
+
* found recursively under abapDir. Used for dependency resolution so
|
|
140
|
+
* that projects with nested folder structures (e.g. src/module/pkg/foo.clas.abap)
|
|
141
|
+
* are handled correctly — not just flat abap/ layouts.
|
|
142
|
+
*/
|
|
143
|
+
function buildFileIndex(abapDir) {
|
|
144
|
+
const index = new Map(); // basename (lowercase) → full path
|
|
145
|
+
function walk(dir) {
|
|
146
|
+
let entries;
|
|
147
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
148
|
+
for (const entry of entries) {
|
|
149
|
+
const full = path.join(dir, entry.name);
|
|
150
|
+
if (entry.isDirectory()) {
|
|
151
|
+
walk(full);
|
|
152
|
+
} else if (entry.name.endsWith('.abap') || entry.name.endsWith('.xml')) {
|
|
153
|
+
index.set(entry.name.toLowerCase(), full);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
walk(abapDir);
|
|
158
|
+
return index;
|
|
159
|
+
}
|
|
160
|
+
|
|
126
161
|
/**
|
|
127
162
|
* Resolve direct dependencies of the given ABAP files by scanning their source
|
|
128
163
|
* for interface/superclass/type references and mapping them to local files.
|
|
@@ -132,11 +167,12 @@ function runGit(cmd) {
|
|
|
132
167
|
* INHERITING FROM <name> → <name>.clas.abap + <name>.clas.xml
|
|
133
168
|
* TYPE REF TO <name> → <name>.intf.abap or <name>.clas.abap (whichever exists)
|
|
134
169
|
*
|
|
135
|
-
*
|
|
170
|
+
* Uses a filename index built from a recursive walk of abapDir so that
|
|
171
|
+
* deeply nested project structures are handled correctly.
|
|
136
172
|
* XML companion files are always included alongside their .abap counterpart
|
|
137
173
|
* so xml_consistency checks can run.
|
|
138
174
|
*/
|
|
139
|
-
function resolveDependencies(abapFiles,
|
|
175
|
+
function resolveDependencies(abapFiles, fileIndex) {
|
|
140
176
|
const deps = new Set();
|
|
141
177
|
const visited = new Set(abapFiles); // don't re-scan changed files as deps
|
|
142
178
|
|
|
@@ -162,16 +198,31 @@ function resolveDependencies(abapFiles, abapDir) {
|
|
|
162
198
|
while ((match = pattern.exec(source)) !== null) {
|
|
163
199
|
const name = match[1].toLowerCase();
|
|
164
200
|
for (const suffix of [`${name}.intf`, `${name}.clas`]) {
|
|
165
|
-
const abapFile =
|
|
166
|
-
const xmlFile =
|
|
167
|
-
if (
|
|
201
|
+
const abapFile = fileIndex.get(`${suffix}.abap`);
|
|
202
|
+
const xmlFile = fileIndex.get(`${suffix}.xml`);
|
|
203
|
+
if (abapFile) {
|
|
168
204
|
deps.add(abapFile);
|
|
169
|
-
if (
|
|
205
|
+
if (xmlFile) deps.add(xmlFile);
|
|
170
206
|
// Recurse into this dep if not yet visited
|
|
171
207
|
if (!visited.has(abapFile)) {
|
|
172
208
|
visited.add(abapFile);
|
|
173
209
|
queue.push(abapFile);
|
|
174
210
|
}
|
|
211
|
+
// For interfaces, also include the canonical concrete implementation
|
|
212
|
+
// (zif_foo → zcl_foo) so rules like unused_variables can fully type-check.
|
|
213
|
+
if (suffix.endsWith('.intf')) {
|
|
214
|
+
const implName = name.replace(/^zif_/, 'zcl_');
|
|
215
|
+
const implFile = fileIndex.get(`${implName}.clas.abap`);
|
|
216
|
+
const implXml = fileIndex.get(`${implName}.clas.xml`);
|
|
217
|
+
if (implFile) {
|
|
218
|
+
deps.add(implFile);
|
|
219
|
+
if (implXml) deps.add(implXml);
|
|
220
|
+
if (!visited.has(implFile)) {
|
|
221
|
+
visited.add(implFile);
|
|
222
|
+
queue.push(implFile);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
175
226
|
break; // intf matched — don't also try clas
|
|
176
227
|
}
|
|
177
228
|
}
|
|
@@ -179,8 +230,9 @@ function resolveDependencies(abapFiles, abapDir) {
|
|
|
179
230
|
}
|
|
180
231
|
|
|
181
232
|
// Always include the XML companion of each scanned file
|
|
182
|
-
const
|
|
183
|
-
|
|
233
|
+
const xmlBasename = path.basename(file).replace(/\.abap$/, '.xml').toLowerCase();
|
|
234
|
+
const xmlCompanion = fileIndex.get(xmlBasename);
|
|
235
|
+
if (xmlCompanion) deps.add(xmlCompanion);
|
|
184
236
|
}
|
|
185
237
|
|
|
186
238
|
return [...deps];
|