gitnexus 1.6.12-rc.25 → 1.6.12-rc.26
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/dist/core/ingestion/cobol-processor.d.ts +2 -2
- package/dist/core/ingestion/cobol-processor.js +11 -21
- package/dist/core/ingestion/languages/cobol/copy-target.d.ts +8 -0
- package/dist/core/ingestion/languages/cobol/copy-target.js +94 -0
- package/dist/core/ingestion/languages/cobol/scope-resolver.js +4 -32
- package/package.json +1 -1
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* does its own extraction, and writes directly to the graph.
|
|
7
7
|
*
|
|
8
8
|
* Pipeline:
|
|
9
|
-
* 1. Separate programs from copybooks
|
|
10
|
-
* 2.
|
|
9
|
+
* 1. Separate programs from copybooks and JCL
|
|
10
|
+
* 2. Index file content by path for COPY expansion
|
|
11
11
|
* 3. For each program: expand COPY statements, then run regex extraction
|
|
12
12
|
* 4. Map CobolRegexResults to graph nodes and relationships
|
|
13
13
|
* 5. Optionally process JCL files for job-step cross-references
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* does its own extraction, and writes directly to the graph.
|
|
7
7
|
*
|
|
8
8
|
* Pipeline:
|
|
9
|
-
* 1. Separate programs from copybooks
|
|
10
|
-
* 2.
|
|
9
|
+
* 1. Separate programs from copybooks and JCL
|
|
10
|
+
* 2. Index file content by path for COPY expansion
|
|
11
11
|
* 3. For each program: expand COPY statements, then run regex extraction
|
|
12
12
|
* 4. Map CobolRegexResults to graph nodes and relationships
|
|
13
13
|
* 5. Optionally process JCL files for job-step cross-references
|
|
@@ -19,6 +19,7 @@ import { SupportedLanguages } from '../../_shared/index.js';
|
|
|
19
19
|
import { preprocessCobolSource, extractCobolSymbolsWithRegex, } from './cobol/cobol-preprocessor.js';
|
|
20
20
|
import { expandCopies } from './cobol/cobol-copy-expander.js';
|
|
21
21
|
import { processJclFiles } from './cobol/jcl-processor.js';
|
|
22
|
+
import { resolveCobolCopyTarget } from './languages/cobol/copy-target.js';
|
|
22
23
|
import { logger } from '../logger.js';
|
|
23
24
|
// ---------------------------------------------------------------------------
|
|
24
25
|
// File detection
|
|
@@ -74,36 +75,22 @@ export const processCobol = (graph, files, allPathSet) => {
|
|
|
74
75
|
};
|
|
75
76
|
// ── 1. Separate programs, copybooks, and JCL ───────────────────────
|
|
76
77
|
const programs = [];
|
|
77
|
-
const copybooks = [];
|
|
78
78
|
const jclFiles = [];
|
|
79
79
|
for (const file of files) {
|
|
80
80
|
const ext = path.extname(file.path).toLowerCase();
|
|
81
81
|
if (JCL_EXTENSIONS.has(ext)) {
|
|
82
82
|
jclFiles.push(file);
|
|
83
83
|
}
|
|
84
|
-
else if (isCopybook(file.path)) {
|
|
85
|
-
copybooks.push(file);
|
|
86
|
-
}
|
|
87
|
-
else if (COBOL_EXTENSIONS.has(ext)) {
|
|
84
|
+
else if (COBOL_EXTENSIONS.has(ext) && !isCopybook(file.path)) {
|
|
88
85
|
programs.push(file);
|
|
89
86
|
}
|
|
90
87
|
}
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
for (const cb of copybooks) {
|
|
94
|
-
const name = path.basename(cb.path, path.extname(cb.path)).toUpperCase();
|
|
95
|
-
copybookMap.set(name, { content: cb.content, path: cb.path });
|
|
96
|
-
}
|
|
97
|
-
// Build reverse lookup: path -> content for O(1) readCopy
|
|
88
|
+
// Path → content for every COBOL/JCL file we ingested. Resolution picks
|
|
89
|
+
// the path (#2967 / census lockstep); this map only supplies the body.
|
|
98
90
|
const copybookByPath = new Map();
|
|
99
|
-
for (const
|
|
100
|
-
copybookByPath.set(
|
|
91
|
+
for (const f of files) {
|
|
92
|
+
copybookByPath.set(f.path, f.content);
|
|
101
93
|
}
|
|
102
|
-
// Resolve and read callbacks for expandCopies
|
|
103
|
-
const resolveCopy = (name) => {
|
|
104
|
-
const entry = copybookMap.get(name.toUpperCase());
|
|
105
|
-
return entry ? entry.path : null;
|
|
106
|
-
};
|
|
107
94
|
// Memoize preprocessed copybook content for the duration of this
|
|
108
95
|
// processCobol call. A single copybook is COPYed by many programs (and at
|
|
109
96
|
// many COPY sites within a program); without this cache
|
|
@@ -142,6 +129,9 @@ export const processCobol = (graph, files, allPathSet) => {
|
|
|
142
129
|
continue;
|
|
143
130
|
// Preprocess: clean patch markers
|
|
144
131
|
const cleaned = preprocessCobolSource(file.content);
|
|
132
|
+
// Per-program so COPY EXTERNAL in src/PROG.cbl cannot land on
|
|
133
|
+
// vendor/EXTERNAL.cpy when a copybooks/ dir is present (#2967).
|
|
134
|
+
const resolveCopy = (name) => resolveCobolCopyTarget(name, file.path, allPathSet);
|
|
145
135
|
// Expand COPY statements
|
|
146
136
|
const { expandedContent, copyResolutions } = expandCopies(cleaned, file.path, resolveCopy, readCopy);
|
|
147
137
|
// Extract symbols from expanded source
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a COBOL `COPY` member name against the workspace file set.
|
|
3
|
+
*
|
|
4
|
+
* `fromFile` is the importing program (census) or the program currently
|
|
5
|
+
* being expanded (processor). It is used only to include the importer's
|
|
6
|
+
* directory in the preferred class when a well-known copybook dir exists.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveCobolCopyTarget(targetRaw: string, fromFile: string, allFilePaths: ReadonlySet<string>): string | null;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared COBOL `COPY` target resolution (#2967 / #2908).
|
|
3
|
+
*
|
|
4
|
+
* Both the census (`cobolScopeResolver.resolveImportTarget`) and the live
|
|
5
|
+
* regex processor (`resolveCopy` → `cobol-copy` IMPORTS) must answer the
|
|
6
|
+
* same path for the same `(name, fromFile, allFilePaths)`. Greening only
|
|
7
|
+
* the census would leave analyze fabricating an IMPORTS edge onto a vendor
|
|
8
|
+
* decoy the compiler would never have searched.
|
|
9
|
+
*
|
|
10
|
+
* Well-known copybook directory segments (`copybooks`, `COPYBOOKS`, `cpy`,
|
|
11
|
+
* `copy`) plus the importer's own directory are the preferred class. When
|
|
12
|
+
* the file set contains at least one of those well-known segments, a `COPY`
|
|
13
|
+
* name resolves only inside that class (first in Set-iteration order).
|
|
14
|
+
* `vendor/EXTERNAL.cpy` therefore misses while `copybooks/CUSTREC.cpy` hits.
|
|
15
|
+
*
|
|
16
|
+
* When the file set has no such directory, fail-open to today's two-tier
|
|
17
|
+
* first-wins basename index — shops whose copybooks *are* the tree have
|
|
18
|
+
* nothing to prefer. The importer directory is *not* enough on its own to
|
|
19
|
+
* leave fail-open (`{vendor/EXTERNAL.cpy, src/PROG.cbl}` must still answer
|
|
20
|
+
* `vendor/EXTERNAL.cpy`).
|
|
21
|
+
*
|
|
22
|
+
* The two-tier index is still memoized on the `allFilePaths` Set identity
|
|
23
|
+
* (#2908). Preferred-class filtering happens at pick time from the stored
|
|
24
|
+
* per-name arrays, so a pass still scans the set once.
|
|
25
|
+
*/
|
|
26
|
+
import path from 'node:path';
|
|
27
|
+
import { perFileSet } from '../../import-resolvers/per-file-set.js';
|
|
28
|
+
const COPYBOOK_EXTENSIONS = new Set(['.cpy', '.copybook']);
|
|
29
|
+
const COBOL_SOURCE_EXTENSIONS = new Set(['.cbl', '.cob', '.cobol']);
|
|
30
|
+
/** Path-component names that mark a conventional copybook directory. */
|
|
31
|
+
const PREFERRED_DIR_NAMES = new Set(['copybooks', 'COPYBOOKS', 'cpy', 'copy']);
|
|
32
|
+
function pathHasPreferredDir(fp) {
|
|
33
|
+
const parts = fp.split('/');
|
|
34
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
35
|
+
if (PREFERRED_DIR_NAMES.has(parts[i]))
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
function pushUnique(map, key, fp) {
|
|
41
|
+
const list = map.get(key);
|
|
42
|
+
if (list === undefined) {
|
|
43
|
+
map.set(key, [fp]);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
list.push(fp);
|
|
47
|
+
}
|
|
48
|
+
const getCobolCopyIndex = perFileSet((allFilePaths) => {
|
|
49
|
+
const copybooks = new Map();
|
|
50
|
+
const sources = new Map();
|
|
51
|
+
let hasPreferredDir = false;
|
|
52
|
+
for (const fp of allFilePaths) {
|
|
53
|
+
const extRaw = path.extname(fp);
|
|
54
|
+
const extLower = extRaw.toLowerCase();
|
|
55
|
+
const tier = COPYBOOK_EXTENSIONS.has(extLower)
|
|
56
|
+
? copybooks
|
|
57
|
+
: COBOL_SOURCE_EXTENSIONS.has(extLower)
|
|
58
|
+
? sources
|
|
59
|
+
: undefined;
|
|
60
|
+
if (tier === undefined)
|
|
61
|
+
continue;
|
|
62
|
+
if (!hasPreferredDir && pathHasPreferredDir(fp))
|
|
63
|
+
hasPreferredDir = true;
|
|
64
|
+
const basename = path.basename(fp, extRaw).toUpperCase();
|
|
65
|
+
pushUnique(tier, basename, fp);
|
|
66
|
+
}
|
|
67
|
+
return { copybooks, sources, hasPreferredDir };
|
|
68
|
+
});
|
|
69
|
+
function pickCopyPath(paths, fromFile, hasPreferredDir) {
|
|
70
|
+
if (paths === undefined || paths.length === 0)
|
|
71
|
+
return null;
|
|
72
|
+
if (!hasPreferredDir)
|
|
73
|
+
return paths[0] ?? null;
|
|
74
|
+
const fromDir = path.dirname(fromFile);
|
|
75
|
+
for (const fp of paths) {
|
|
76
|
+
if (pathHasPreferredDir(fp) || path.dirname(fp) === fromDir)
|
|
77
|
+
return fp;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolve a COBOL `COPY` member name against the workspace file set.
|
|
83
|
+
*
|
|
84
|
+
* `fromFile` is the importing program (census) or the program currently
|
|
85
|
+
* being expanded (processor). It is used only to include the importer's
|
|
86
|
+
* directory in the preferred class when a well-known copybook dir exists.
|
|
87
|
+
*/
|
|
88
|
+
export function resolveCobolCopyTarget(targetRaw, fromFile, allFilePaths) {
|
|
89
|
+
const upper = targetRaw.toUpperCase();
|
|
90
|
+
const index = getCobolCopyIndex(allFilePaths);
|
|
91
|
+
return (pickCopyPath(index.copybooks.get(upper), fromFile, index.hasPreferredDir) ??
|
|
92
|
+
pickCopyPath(index.sources.get(upper), fromFile, index.hasPreferredDir) ??
|
|
93
|
+
null);
|
|
94
|
+
}
|
|
@@ -8,46 +8,18 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Reference: `languages/python/scope-resolver.ts`.
|
|
10
10
|
*/
|
|
11
|
-
import path from 'node:path';
|
|
12
11
|
import { SupportedLanguages } from '../../../../_shared/index.js';
|
|
13
|
-
import { perFileSet } from '../../import-resolvers/per-file-set.js';
|
|
14
12
|
import { populateClassOwnedMembers } from '../../scope-resolution/scope/walkers.js';
|
|
15
13
|
import { cobolProvider } from '../cobol.js';
|
|
16
|
-
|
|
17
|
-
const COPYBOOK_EXTENSIONS = new Set(['.cpy', '.copybook']);
|
|
18
|
-
// COBOL source files, searched only after every copybook has missed.
|
|
19
|
-
const COBOL_SOURCE_EXTENSIONS = new Set(['.cbl', '.cob', '.cobol']);
|
|
20
|
-
const getCobolCopyIndex = perFileSet((allFilePaths) => {
|
|
21
|
-
const copybooks = new Map();
|
|
22
|
-
const sources = new Map();
|
|
23
|
-
// One pass builds both tiers: the two scans walked the same files and
|
|
24
|
-
// classified each by the same extension test.
|
|
25
|
-
for (const fp of allFilePaths) {
|
|
26
|
-
const ext = path.extname(fp).toLowerCase();
|
|
27
|
-
const tier = COPYBOOK_EXTENSIONS.has(ext)
|
|
28
|
-
? copybooks
|
|
29
|
-
: COBOL_SOURCE_EXTENSIONS.has(ext)
|
|
30
|
-
? sources
|
|
31
|
-
: undefined;
|
|
32
|
-
if (tier === undefined)
|
|
33
|
-
continue;
|
|
34
|
-
const basename = path.basename(fp, ext).toUpperCase();
|
|
35
|
-
// First in Set-iteration order wins, as the scans' first-match `return` did.
|
|
36
|
-
if (!tier.has(basename))
|
|
37
|
-
tier.set(basename, fp);
|
|
38
|
-
}
|
|
39
|
-
return { copybooks, sources };
|
|
40
|
-
});
|
|
14
|
+
import { resolveCobolCopyTarget } from './copy-target.js';
|
|
41
15
|
const cobolScopeResolver = {
|
|
42
16
|
language: SupportedLanguages.Cobol,
|
|
43
17
|
languageProvider: cobolProvider,
|
|
44
18
|
importEdgeReason: 'cobol-scope: copy',
|
|
45
19
|
// ── Resolve COPY bookname to file path ─────────────────────────────
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// Copybooks first, then COBOL sources — the tier order IS the tie-break.
|
|
50
|
-
return index.copybooks.get(upper) ?? index.sources.get(upper) ?? null;
|
|
20
|
+
// Shared with the regex processor's resolveCopy (#2967 lockstep).
|
|
21
|
+
resolveImportTarget: (targetRaw, fromFile, allFilePaths) => {
|
|
22
|
+
return resolveCobolCopyTarget(targetRaw, fromFile, allFilePaths);
|
|
51
23
|
},
|
|
52
24
|
// COBOL has no binding-merge rules beyond the default (local-first-then-imports).
|
|
53
25
|
mergeBindings: (existing) => [...existing],
|
package/package.json
CHANGED