gitnexus 1.4.7 → 1.4.9
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/README.md +29 -1
- package/dist/cli/ai-context.d.ts +1 -1
- package/dist/cli/ai-context.js +1 -1
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +54 -21
- package/dist/cli/index-repo.d.ts +15 -0
- package/dist/cli/index-repo.js +115 -0
- package/dist/cli/index.js +13 -3
- package/dist/cli/setup.js +90 -10
- package/dist/cli/wiki.d.ts +4 -0
- package/dist/cli/wiki.js +174 -53
- package/dist/config/supported-languages.d.ts +33 -1
- package/dist/config/supported-languages.js +32 -0
- package/dist/core/embeddings/embedder.d.ts +6 -1
- package/dist/core/embeddings/embedder.js +65 -5
- package/dist/core/embeddings/embedding-pipeline.js +11 -9
- package/dist/core/embeddings/http-client.d.ts +31 -0
- package/dist/core/embeddings/http-client.js +179 -0
- package/dist/core/embeddings/index.d.ts +1 -0
- package/dist/core/embeddings/index.js +1 -0
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/graph/graph.js +9 -1
- package/dist/core/graph/types.d.ts +11 -2
- package/dist/core/ingestion/call-processor.d.ts +66 -2
- package/dist/core/ingestion/call-processor.js +650 -30
- package/dist/core/ingestion/call-routing.d.ts +9 -18
- package/dist/core/ingestion/call-routing.js +0 -19
- package/dist/core/ingestion/cobol/cobol-copy-expander.d.ts +57 -0
- package/dist/core/ingestion/cobol/cobol-copy-expander.js +385 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +210 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.js +1509 -0
- package/dist/core/ingestion/cobol/jcl-parser.d.ts +68 -0
- package/dist/core/ingestion/cobol/jcl-parser.js +217 -0
- package/dist/core/ingestion/cobol/jcl-processor.d.ts +33 -0
- package/dist/core/ingestion/cobol/jcl-processor.js +229 -0
- package/dist/core/ingestion/cobol-processor.d.ts +54 -0
- package/dist/core/ingestion/cobol-processor.js +1186 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +17 -0
- package/dist/core/ingestion/entry-point-scoring.js +52 -28
- package/dist/core/ingestion/export-detection.d.ts +47 -8
- package/dist/core/ingestion/export-detection.js +29 -50
- package/dist/core/ingestion/field-extractor.d.ts +29 -0
- package/dist/core/ingestion/field-extractor.js +25 -0
- package/dist/core/ingestion/field-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/c-cpp.js +108 -0
- package/dist/core/ingestion/field-extractors/configs/csharp.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/csharp.js +73 -0
- package/dist/core/ingestion/field-extractors/configs/dart.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/dart.js +76 -0
- package/dist/core/ingestion/field-extractors/configs/go.d.ts +11 -0
- package/dist/core/ingestion/field-extractors/configs/go.js +64 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +44 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.js +134 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.js +118 -0
- package/dist/core/ingestion/field-extractors/configs/php.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/php.js +67 -0
- package/dist/core/ingestion/field-extractors/configs/python.d.ts +12 -0
- package/dist/core/ingestion/field-extractors/configs/python.js +91 -0
- package/dist/core/ingestion/field-extractors/configs/ruby.d.ts +16 -0
- package/dist/core/ingestion/field-extractors/configs/ruby.js +75 -0
- package/dist/core/ingestion/field-extractors/configs/rust.d.ts +9 -0
- package/dist/core/ingestion/field-extractors/configs/rust.js +55 -0
- package/dist/core/ingestion/field-extractors/configs/swift.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/swift.js +63 -0
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +60 -0
- package/dist/core/ingestion/field-extractors/generic.d.ts +46 -0
- package/dist/core/ingestion/field-extractors/generic.js +111 -0
- package/dist/core/ingestion/field-extractors/typescript.d.ts +77 -0
- package/dist/core/ingestion/field-extractors/typescript.js +291 -0
- package/dist/core/ingestion/field-types.d.ts +59 -0
- package/dist/core/ingestion/field-types.js +2 -0
- package/dist/core/ingestion/framework-detection.d.ts +97 -2
- package/dist/core/ingestion/framework-detection.js +114 -14
- package/dist/core/ingestion/heritage-processor.js +62 -66
- package/dist/core/ingestion/import-processor.d.ts +9 -10
- package/dist/core/ingestion/import-processor.js +150 -196
- package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.d.ts +6 -9
- package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.js +20 -2
- package/dist/core/ingestion/import-resolvers/dart.d.ts +7 -0
- package/dist/core/ingestion/import-resolvers/dart.js +44 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/go.d.ts +4 -5
- package/dist/core/ingestion/{resolvers → import-resolvers}/go.js +17 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/jvm.d.ts +10 -1
- package/dist/core/ingestion/import-resolvers/jvm.js +159 -0
- package/dist/core/ingestion/import-resolvers/php.d.ts +25 -0
- package/dist/core/ingestion/import-resolvers/php.js +80 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/python.d.ts +9 -3
- package/dist/core/ingestion/{resolvers → import-resolvers}/python.js +35 -3
- package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.d.ts +5 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.js +7 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/rust.d.ts +5 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/rust.js +41 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/standard.d.ts +15 -7
- package/dist/core/ingestion/{resolvers → import-resolvers}/standard.js +22 -3
- package/dist/core/ingestion/import-resolvers/swift.d.ts +7 -0
- package/dist/core/ingestion/import-resolvers/swift.js +23 -0
- package/dist/core/ingestion/import-resolvers/types.d.ts +44 -0
- package/dist/core/ingestion/import-resolvers/types.js +6 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/utils.d.ts +2 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/utils.js +7 -0
- package/dist/core/ingestion/language-config.d.ts +6 -0
- package/dist/core/ingestion/language-config.js +13 -0
- package/dist/core/ingestion/language-provider.d.ts +121 -0
- package/dist/core/ingestion/language-provider.js +24 -0
- package/dist/core/ingestion/languages/c-cpp.d.ts +12 -0
- package/dist/core/ingestion/languages/c-cpp.js +71 -0
- package/dist/core/ingestion/languages/cobol.d.ts +1 -0
- package/dist/core/ingestion/languages/cobol.js +26 -0
- package/dist/core/ingestion/languages/csharp.d.ts +8 -0
- package/dist/core/ingestion/languages/csharp.js +49 -0
- package/dist/core/ingestion/languages/dart.d.ts +12 -0
- package/dist/core/ingestion/languages/dart.js +58 -0
- package/dist/core/ingestion/languages/go.d.ts +11 -0
- package/dist/core/ingestion/languages/go.js +28 -0
- package/dist/core/ingestion/languages/index.d.ts +38 -0
- package/dist/core/ingestion/languages/index.js +63 -0
- package/dist/core/ingestion/languages/java.d.ts +9 -0
- package/dist/core/ingestion/languages/java.js +29 -0
- package/dist/core/ingestion/languages/kotlin.d.ts +9 -0
- package/dist/core/ingestion/languages/kotlin.js +53 -0
- package/dist/core/ingestion/languages/php.d.ts +8 -0
- package/dist/core/ingestion/languages/php.js +145 -0
- package/dist/core/ingestion/languages/python.d.ts +12 -0
- package/dist/core/ingestion/languages/python.js +39 -0
- package/dist/core/ingestion/languages/ruby.d.ts +9 -0
- package/dist/core/ingestion/languages/ruby.js +44 -0
- package/dist/core/ingestion/languages/rust.d.ts +12 -0
- package/dist/core/ingestion/languages/rust.js +44 -0
- package/dist/core/ingestion/languages/swift.d.ts +12 -0
- package/dist/core/ingestion/languages/swift.js +133 -0
- package/dist/core/ingestion/languages/typescript.d.ts +10 -0
- package/dist/core/ingestion/languages/typescript.js +60 -0
- package/dist/core/ingestion/markdown-processor.d.ts +17 -0
- package/dist/core/ingestion/markdown-processor.js +124 -0
- package/dist/core/ingestion/mro-processor.js +22 -18
- package/dist/core/ingestion/named-binding-processor.d.ts +18 -0
- package/dist/core/ingestion/named-binding-processor.js +42 -0
- package/dist/core/ingestion/named-bindings/csharp.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/csharp.js +37 -0
- package/dist/core/ingestion/named-bindings/java.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/java.js +29 -0
- package/dist/core/ingestion/named-bindings/kotlin.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/kotlin.js +36 -0
- package/dist/core/ingestion/named-bindings/php.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/php.js +61 -0
- package/dist/core/ingestion/named-bindings/python.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/python.js +49 -0
- package/dist/core/ingestion/named-bindings/rust.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/rust.js +64 -0
- package/dist/core/ingestion/named-bindings/types.d.ts +16 -0
- package/dist/core/ingestion/named-bindings/types.js +6 -0
- package/dist/core/ingestion/named-bindings/typescript.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/typescript.js +58 -0
- package/dist/core/ingestion/parsing-processor.d.ts +6 -2
- package/dist/core/ingestion/parsing-processor.js +125 -85
- package/dist/core/ingestion/pipeline.d.ts +10 -0
- package/dist/core/ingestion/pipeline.js +1235 -317
- package/dist/core/ingestion/resolution-context.d.ts +5 -0
- package/dist/core/ingestion/resolution-context.js +8 -5
- package/dist/core/ingestion/route-extractors/expo.d.ts +1 -0
- package/dist/core/ingestion/route-extractors/expo.js +36 -0
- package/dist/core/ingestion/route-extractors/middleware.d.ts +47 -0
- package/dist/core/ingestion/route-extractors/middleware.js +143 -0
- package/dist/core/ingestion/route-extractors/nextjs.d.ts +3 -0
- package/dist/core/ingestion/route-extractors/nextjs.js +76 -0
- package/dist/core/ingestion/route-extractors/php.d.ts +7 -0
- package/dist/core/ingestion/route-extractors/php.js +21 -0
- package/dist/core/ingestion/route-extractors/response-shapes.d.ts +20 -0
- package/dist/core/ingestion/route-extractors/response-shapes.js +290 -0
- package/dist/core/ingestion/symbol-table.d.ts +16 -0
- package/dist/core/ingestion/symbol-table.js +20 -6
- package/dist/core/ingestion/tree-sitter-queries.d.ts +10 -9
- package/dist/core/ingestion/tree-sitter-queries.js +274 -11
- package/dist/core/ingestion/type-env.d.ts +42 -18
- package/dist/core/ingestion/type-env.js +481 -106
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +5 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +119 -0
- package/dist/core/ingestion/type-extractors/csharp.js +149 -16
- package/dist/core/ingestion/type-extractors/dart.d.ts +15 -0
- package/dist/core/ingestion/type-extractors/dart.js +371 -0
- package/dist/core/ingestion/type-extractors/jvm.js +169 -66
- package/dist/core/ingestion/type-extractors/rust.js +35 -1
- package/dist/core/ingestion/type-extractors/shared.d.ts +1 -15
- package/dist/core/ingestion/type-extractors/shared.js +14 -112
- package/dist/core/ingestion/type-extractors/swift.js +338 -7
- package/dist/core/ingestion/type-extractors/types.d.ts +40 -8
- package/dist/core/ingestion/type-extractors/typescript.js +141 -9
- package/dist/core/ingestion/utils/ast-helpers.d.ts +83 -0
- package/dist/core/ingestion/utils/ast-helpers.js +817 -0
- package/dist/core/ingestion/utils/call-analysis.d.ts +73 -0
- package/dist/core/ingestion/utils/call-analysis.js +527 -0
- package/dist/core/ingestion/utils/event-loop.d.ts +5 -0
- package/dist/core/ingestion/utils/event-loop.js +5 -0
- package/dist/core/ingestion/utils/language-detection.d.ts +9 -0
- package/dist/core/ingestion/utils/language-detection.js +70 -0
- package/dist/core/ingestion/utils/verbose.d.ts +1 -0
- package/dist/core/ingestion/utils/verbose.js +7 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +55 -5
- package/dist/core/ingestion/workers/parse-worker.js +415 -225
- package/dist/core/lbug/csv-generator.js +51 -1
- package/dist/core/lbug/lbug-adapter.d.ts +10 -0
- package/dist/core/lbug/lbug-adapter.js +75 -4
- package/dist/core/lbug/schema.d.ts +8 -4
- package/dist/core/lbug/schema.js +65 -4
- package/dist/core/tree-sitter/parser-loader.js +7 -1
- package/dist/core/wiki/cursor-client.d.ts +31 -0
- package/dist/core/wiki/cursor-client.js +127 -0
- package/dist/core/wiki/generator.d.ts +28 -9
- package/dist/core/wiki/generator.js +115 -18
- package/dist/core/wiki/graph-queries.d.ts +4 -0
- package/dist/core/wiki/graph-queries.js +7 -1
- package/dist/core/wiki/llm-client.d.ts +2 -0
- package/dist/core/wiki/llm-client.js +8 -4
- package/dist/core/wiki/prompts.d.ts +3 -3
- package/dist/core/wiki/prompts.js +6 -0
- package/dist/mcp/core/embedder.js +11 -3
- package/dist/mcp/core/lbug-adapter.d.ts +5 -0
- package/dist/mcp/core/lbug-adapter.js +23 -2
- package/dist/mcp/local/local-backend.d.ts +38 -5
- package/dist/mcp/local/local-backend.js +804 -63
- package/dist/mcp/resources.js +2 -0
- package/dist/mcp/tools.js +73 -4
- package/dist/server/api.d.ts +19 -1
- package/dist/server/api.js +66 -6
- package/dist/storage/git.d.ts +12 -0
- package/dist/storage/git.js +21 -0
- package/dist/storage/repo-manager.d.ts +3 -0
- package/package.json +25 -16
- package/dist/core/ingestion/named-binding-extraction.d.ts +0 -61
- package/dist/core/ingestion/named-binding-extraction.js +0 -363
- package/dist/core/ingestion/resolvers/index.d.ts +0 -18
- package/dist/core/ingestion/resolvers/index.js +0 -13
- package/dist/core/ingestion/resolvers/jvm.js +0 -87
- package/dist/core/ingestion/resolvers/php.d.ts +0 -15
- package/dist/core/ingestion/resolvers/php.js +0 -35
- package/dist/core/ingestion/type-extractors/index.d.ts +0 -22
- package/dist/core/ingestion/type-extractors/index.js +0 -31
- package/dist/core/ingestion/utils.d.ts +0 -138
- package/dist/core/ingestion/utils.js +0 -1290
- package/scripts/patch-tree-sitter-swift.cjs +0 -74
|
@@ -10,25 +10,16 @@
|
|
|
10
10
|
* two packages have separate build targets (Node native vs WASM/browser).
|
|
11
11
|
* Keep both copies in sync until a shared package is introduced.
|
|
12
12
|
*/
|
|
13
|
+
import type { SyntaxNode } from './utils/ast-helpers.js';
|
|
13
14
|
/** null = this call was not routed; fall through to default call handling */
|
|
14
15
|
export type CallRoutingResult = RubyCallRouting | null;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
kotlin: CallRouter;
|
|
23
|
-
go: CallRouter;
|
|
24
|
-
rust: CallRouter;
|
|
25
|
-
csharp: CallRouter;
|
|
26
|
-
php: CallRouter;
|
|
27
|
-
swift: CallRouter;
|
|
28
|
-
cpp: CallRouter;
|
|
29
|
-
c: CallRouter;
|
|
30
|
-
ruby: typeof routeRubyCall;
|
|
31
|
-
};
|
|
16
|
+
/**
|
|
17
|
+
* Per-language call router.
|
|
18
|
+
* IMPORTANT: Call-routed imports bypass preprocessImportPath(), so any router that
|
|
19
|
+
* returns an importPath MUST validate it independently (length cap, control-char
|
|
20
|
+
* rejection). See routeRubyCall for the reference implementation.
|
|
21
|
+
*/
|
|
22
|
+
export type CallRouter = (calledName: string, callNode: SyntaxNode) => CallRoutingResult;
|
|
32
23
|
export type RubyCallRouting = {
|
|
33
24
|
kind: 'import';
|
|
34
25
|
importPath: string;
|
|
@@ -65,4 +56,4 @@ export interface RubyPropertyItem {
|
|
|
65
56
|
* @param callNode - The tree-sitter `call` AST node
|
|
66
57
|
* @returns A discriminated union describing the call's semantic role
|
|
67
58
|
*/
|
|
68
|
-
export declare function routeRubyCall(calledName: string, callNode:
|
|
59
|
+
export declare function routeRubyCall(calledName: string, callNode: SyntaxNode): RubyCallRouting;
|
|
@@ -10,25 +10,6 @@
|
|
|
10
10
|
* two packages have separate build targets (Node native vs WASM/browser).
|
|
11
11
|
* Keep both copies in sync until a shared package is introduced.
|
|
12
12
|
*/
|
|
13
|
-
import { SupportedLanguages } from '../../config/supported-languages.js';
|
|
14
|
-
/** No-op router: returns null for every call (passthrough to normal processing) */
|
|
15
|
-
const noRouting = () => null;
|
|
16
|
-
/** Per-language call routing. noRouting = no special routing (normal call processing) */
|
|
17
|
-
export const callRouters = {
|
|
18
|
-
[SupportedLanguages.JavaScript]: noRouting,
|
|
19
|
-
[SupportedLanguages.TypeScript]: noRouting,
|
|
20
|
-
[SupportedLanguages.Python]: noRouting,
|
|
21
|
-
[SupportedLanguages.Java]: noRouting,
|
|
22
|
-
[SupportedLanguages.Kotlin]: noRouting,
|
|
23
|
-
[SupportedLanguages.Go]: noRouting,
|
|
24
|
-
[SupportedLanguages.Rust]: noRouting,
|
|
25
|
-
[SupportedLanguages.CSharp]: noRouting,
|
|
26
|
-
[SupportedLanguages.PHP]: noRouting,
|
|
27
|
-
[SupportedLanguages.Swift]: noRouting,
|
|
28
|
-
[SupportedLanguages.CPlusPlus]: noRouting,
|
|
29
|
-
[SupportedLanguages.C]: noRouting,
|
|
30
|
-
[SupportedLanguages.Ruby]: routeRubyCall,
|
|
31
|
-
};
|
|
32
13
|
// ── Pre-allocated singletons for common return values ────────────────────────
|
|
33
14
|
const CALL_RESULT = { kind: 'call' };
|
|
34
15
|
const SKIP_RESULT = { kind: 'skip' };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COBOL COPY statement expansion engine.
|
|
3
|
+
*
|
|
4
|
+
* Expands COPY statements by inlining copybook content, applying REPLACING
|
|
5
|
+
* transformations (LEADING, TRAILING, EXACT), and handling nested copies
|
|
6
|
+
* with cycle detection.
|
|
7
|
+
*
|
|
8
|
+
* This is a preprocessing step that runs BEFORE extractCobolSymbolsWithRegex.
|
|
9
|
+
* The caller should run preprocessCobolSource first to clean patch markers.
|
|
10
|
+
*
|
|
11
|
+
* Supported syntax:
|
|
12
|
+
* COPY CPSESP.
|
|
13
|
+
* COPY "WORKGRID.CPY".
|
|
14
|
+
* COPY CPSESP REPLACING LEADING "ESP-" BY "LK-ESP-"
|
|
15
|
+
* LEADING "KPSESPL" BY "LK-KPSESPL".
|
|
16
|
+
* COPY ANAZI REPLACING "ANAZI-KEY" BY "LK-KEY".
|
|
17
|
+
*/
|
|
18
|
+
export interface CopyReplacing {
|
|
19
|
+
type: 'LEADING' | 'TRAILING' | 'EXACT';
|
|
20
|
+
from: string;
|
|
21
|
+
to: string;
|
|
22
|
+
isPseudotext?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface CopyResolution {
|
|
25
|
+
copyTarget: string;
|
|
26
|
+
resolvedPath: string | null;
|
|
27
|
+
line: number;
|
|
28
|
+
replacing: CopyReplacing[];
|
|
29
|
+
library?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface CopyExpansionResult {
|
|
32
|
+
expandedContent: string;
|
|
33
|
+
copyResolutions: CopyResolution[];
|
|
34
|
+
}
|
|
35
|
+
export declare const DEFAULT_MAX_DEPTH = 10;
|
|
36
|
+
/**
|
|
37
|
+
* Parse REPLACING clause text into structured replacements.
|
|
38
|
+
*
|
|
39
|
+
* Input examples:
|
|
40
|
+
* LEADING "ESP-" BY "LK-ESP-" LEADING "KPSESPL" BY "LK-KPSESPL"
|
|
41
|
+
* "ANAZI-KEY" BY "LK-KEY"
|
|
42
|
+
* TRAILING "-IN" BY "-OUT"
|
|
43
|
+
* ==CUST-== BY ==WS-CUST-==
|
|
44
|
+
* ==OLD-TEXT== BY ====
|
|
45
|
+
*/
|
|
46
|
+
export declare function parseReplacingClause(text: string): CopyReplacing[];
|
|
47
|
+
/**
|
|
48
|
+
* Expand COBOL COPY statements by inlining copybook content.
|
|
49
|
+
*
|
|
50
|
+
* @param content - Source COBOL content (after preprocessCobolSource)
|
|
51
|
+
* @param filePath - Path of the source file (for diagnostics)
|
|
52
|
+
* @param resolveFile - Maps a COPY target name to a filesystem path, or null if not found
|
|
53
|
+
* @param readFile - Reads file content by path, or null if unreadable
|
|
54
|
+
* @param maxDepth - Maximum nesting depth for recursive expansion (default: 10)
|
|
55
|
+
* @returns Expanded content and resolution metadata
|
|
56
|
+
*/
|
|
57
|
+
export declare function expandCopies(content: string, filePath: string, resolveFile: (name: string) => string | null, readFile: (path: string) => string | null, maxDepth?: number): CopyExpansionResult;
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COBOL COPY statement expansion engine.
|
|
3
|
+
*
|
|
4
|
+
* Expands COPY statements by inlining copybook content, applying REPLACING
|
|
5
|
+
* transformations (LEADING, TRAILING, EXACT), and handling nested copies
|
|
6
|
+
* with cycle detection.
|
|
7
|
+
*
|
|
8
|
+
* This is a preprocessing step that runs BEFORE extractCobolSymbolsWithRegex.
|
|
9
|
+
* The caller should run preprocessCobolSource first to clean patch markers.
|
|
10
|
+
*
|
|
11
|
+
* Supported syntax:
|
|
12
|
+
* COPY CPSESP.
|
|
13
|
+
* COPY "WORKGRID.CPY".
|
|
14
|
+
* COPY CPSESP REPLACING LEADING "ESP-" BY "LK-ESP-"
|
|
15
|
+
* LEADING "KPSESPL" BY "LK-KPSESPL".
|
|
16
|
+
* COPY ANAZI REPLACING "ANAZI-KEY" BY "LK-KEY".
|
|
17
|
+
*/
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Constants
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
export const DEFAULT_MAX_DEPTH = 10;
|
|
22
|
+
/** COBOL identifier pattern: starts with letter, contains letters, digits, hyphens. */
|
|
23
|
+
const RE_COBOL_IDENTIFIER = /\b([A-Z][A-Z0-9-]*)\b/gi;
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Private helpers
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
/**
|
|
28
|
+
* Strip inline comments (Italian-style `|` comments).
|
|
29
|
+
* Only strips if `|` appears in the code area (col 7+).
|
|
30
|
+
*/
|
|
31
|
+
function stripInlineComment(line) {
|
|
32
|
+
let inQuote = null;
|
|
33
|
+
for (let i = 0; i < line.length; i++) {
|
|
34
|
+
const ch = line[i];
|
|
35
|
+
if (inQuote) {
|
|
36
|
+
if (ch === inQuote)
|
|
37
|
+
inQuote = null;
|
|
38
|
+
}
|
|
39
|
+
else if (ch === '"' || ch === "'") {
|
|
40
|
+
inQuote = ch;
|
|
41
|
+
}
|
|
42
|
+
else if (ch === '|') {
|
|
43
|
+
return line.substring(0, i);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return line;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check if a line is a COBOL comment (indicator in col 7 is `*` or `/`).
|
|
50
|
+
*/
|
|
51
|
+
function isCommentLine(line) {
|
|
52
|
+
return line.length >= 7 && (line[6] === '*' || line[6] === '/');
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if a line is a continuation line (indicator in col 7 is `-`).
|
|
56
|
+
*/
|
|
57
|
+
function isContinuationLine(line) {
|
|
58
|
+
return line.length >= 7 && line[6] === '-';
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Merge continuation lines into their predecessors.
|
|
62
|
+
* Returns an array of logical lines with their original starting line numbers.
|
|
63
|
+
*/
|
|
64
|
+
function mergeLogicalLines(rawLines) {
|
|
65
|
+
const logical = [];
|
|
66
|
+
for (let i = 0; i < rawLines.length; i++) {
|
|
67
|
+
const raw = rawLines[i];
|
|
68
|
+
// Skip comment lines
|
|
69
|
+
if (isCommentLine(raw)) {
|
|
70
|
+
logical.push({ text: '', lineNum: i + 1 });
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
// Continuation: merge into previous logical line
|
|
74
|
+
if (isContinuationLine(raw)) {
|
|
75
|
+
if (logical.length > 0) {
|
|
76
|
+
const prev = logical[logical.length - 1];
|
|
77
|
+
const continuation = raw.length > 7 ? raw.substring(7).trimStart() : '';
|
|
78
|
+
prev.text += continuation;
|
|
79
|
+
}
|
|
80
|
+
// Push empty placeholder to preserve line count
|
|
81
|
+
logical.push({ text: '', lineNum: i + 1 });
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
// Normal line: strip inline comments
|
|
85
|
+
const cleaned = stripInlineComment(raw);
|
|
86
|
+
logical.push({ text: cleaned, lineNum: i + 1 });
|
|
87
|
+
}
|
|
88
|
+
return logical;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Parse REPLACING clause text into structured replacements.
|
|
92
|
+
*
|
|
93
|
+
* Input examples:
|
|
94
|
+
* LEADING "ESP-" BY "LK-ESP-" LEADING "KPSESPL" BY "LK-KPSESPL"
|
|
95
|
+
* "ANAZI-KEY" BY "LK-KEY"
|
|
96
|
+
* TRAILING "-IN" BY "-OUT"
|
|
97
|
+
* ==CUST-== BY ==WS-CUST-==
|
|
98
|
+
* ==OLD-TEXT== BY ====
|
|
99
|
+
*/
|
|
100
|
+
export function parseReplacingClause(text) {
|
|
101
|
+
const replacings = [];
|
|
102
|
+
if (!text || text.trim().length === 0)
|
|
103
|
+
return replacings;
|
|
104
|
+
const tokens = [];
|
|
105
|
+
const tokenRe = /==((?:[^=]|=[^=])*)==|"([^"]*)"|(\S+)/g;
|
|
106
|
+
let tm;
|
|
107
|
+
while ((tm = tokenRe.exec(text)) !== null) {
|
|
108
|
+
if (tm[1] !== undefined) {
|
|
109
|
+
// Pseudotext: trim leading/trailing whitespace
|
|
110
|
+
tokens.push({ value: tm[1].trim(), isPseudotext: true });
|
|
111
|
+
}
|
|
112
|
+
else if (tm[2] !== undefined) {
|
|
113
|
+
tokens.push({ value: tm[2], isPseudotext: false });
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
tokens.push({ value: tm[3], isPseudotext: false });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Parse token stream: [LEADING|TRAILING]? <from> BY <to>
|
|
120
|
+
let i = 0;
|
|
121
|
+
while (i < tokens.length) {
|
|
122
|
+
let type = 'EXACT';
|
|
123
|
+
// Check for type modifier (only on non-pseudotext tokens)
|
|
124
|
+
if (!tokens[i].isPseudotext) {
|
|
125
|
+
const upper = tokens[i].value.toUpperCase();
|
|
126
|
+
if (upper === 'LEADING') {
|
|
127
|
+
type = 'LEADING';
|
|
128
|
+
i++;
|
|
129
|
+
}
|
|
130
|
+
else if (upper === 'TRAILING') {
|
|
131
|
+
type = 'TRAILING';
|
|
132
|
+
i++;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (i >= tokens.length)
|
|
136
|
+
break;
|
|
137
|
+
const fromToken = tokens[i];
|
|
138
|
+
i++;
|
|
139
|
+
// Pseudotext always forces EXACT type
|
|
140
|
+
if (fromToken.isPseudotext)
|
|
141
|
+
type = 'EXACT';
|
|
142
|
+
// Expect BY keyword
|
|
143
|
+
if (i >= tokens.length)
|
|
144
|
+
break;
|
|
145
|
+
if (tokens[i].value.toUpperCase() !== 'BY') {
|
|
146
|
+
// Malformed — skip this token and try to resync
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
i++; // skip BY
|
|
150
|
+
if (i >= tokens.length)
|
|
151
|
+
break;
|
|
152
|
+
const toToken = tokens[i];
|
|
153
|
+
i++;
|
|
154
|
+
replacings.push({ type, from: fromToken.value, to: toToken.value, isPseudotext: fromToken.isPseudotext || undefined });
|
|
155
|
+
}
|
|
156
|
+
return replacings;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Scan logical lines for COPY statements.
|
|
160
|
+
* COPY statements can span multiple lines and terminate with a period.
|
|
161
|
+
*/
|
|
162
|
+
function parseCopyStatements(logicalLines) {
|
|
163
|
+
const results = [];
|
|
164
|
+
let accumulator = null;
|
|
165
|
+
let startLine = 0;
|
|
166
|
+
let endLine = 0;
|
|
167
|
+
for (let i = 0; i < logicalLines.length; i++) {
|
|
168
|
+
const { text, lineNum } = logicalLines[i];
|
|
169
|
+
if (text.length === 0)
|
|
170
|
+
continue;
|
|
171
|
+
// Check for COPY keyword start (not inside a string context)
|
|
172
|
+
const copyStart = text.match(/\bCOPY\b/i);
|
|
173
|
+
if (accumulator === null) {
|
|
174
|
+
if (!copyStart)
|
|
175
|
+
continue;
|
|
176
|
+
// Start accumulating from the COPY keyword onwards
|
|
177
|
+
const copyIdx = copyStart.index;
|
|
178
|
+
accumulator = text.substring(copyIdx);
|
|
179
|
+
startLine = lineNum;
|
|
180
|
+
endLine = lineNum;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
// Continue accumulating
|
|
184
|
+
accumulator += ' ' + text.trim();
|
|
185
|
+
endLine = lineNum;
|
|
186
|
+
}
|
|
187
|
+
// Check if statement terminates (period at end of accumulated text)
|
|
188
|
+
if (accumulator !== null && /\.\s*$/.test(accumulator)) {
|
|
189
|
+
const parsed = parseSingleCopyStatement(accumulator, startLine, endLine);
|
|
190
|
+
if (parsed) {
|
|
191
|
+
results.push(parsed);
|
|
192
|
+
}
|
|
193
|
+
accumulator = null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// If there's an unterminated COPY (missing period), try to parse what we have
|
|
197
|
+
if (accumulator !== null) {
|
|
198
|
+
const parsed = parseSingleCopyStatement(accumulator, startLine, endLine);
|
|
199
|
+
if (parsed) {
|
|
200
|
+
results.push(parsed);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return results;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Parse a single complete COPY statement string.
|
|
207
|
+
*
|
|
208
|
+
* Formats:
|
|
209
|
+
* COPY target.
|
|
210
|
+
* COPY "target".
|
|
211
|
+
* COPY target REPLACING ... .
|
|
212
|
+
*/
|
|
213
|
+
function parseSingleCopyStatement(stmt, startLine, endLine) {
|
|
214
|
+
// Strip terminating period
|
|
215
|
+
const text = stmt.replace(/\.\s*$/, '').trim();
|
|
216
|
+
// Extract target: COPY <target> or COPY "<target>" or COPY '<target>'
|
|
217
|
+
// Optionally followed by IN/OF <library-name> (COBOL-85 standard: IN and OF are synonyms)
|
|
218
|
+
const targetMatch = text.match(/^COPY\s+(?:"([^"]+)"|'([^']+)'|([A-Z][A-Z0-9-]*))(?:\s+(?:IN|OF)\s+([A-Z][A-Z0-9-]*))?/i);
|
|
219
|
+
if (!targetMatch)
|
|
220
|
+
return null;
|
|
221
|
+
const target = targetMatch[1] ?? targetMatch[2] ?? targetMatch[3];
|
|
222
|
+
const library = targetMatch[4] || undefined;
|
|
223
|
+
// Extract REPLACING clause if present
|
|
224
|
+
let replacing = [];
|
|
225
|
+
const replacingIdx = text.search(/\bREPLACING\b/i);
|
|
226
|
+
if (replacingIdx >= 0) {
|
|
227
|
+
const replacingText = text.substring(replacingIdx + 'REPLACING'.length);
|
|
228
|
+
replacing = parseReplacingClause(replacingText);
|
|
229
|
+
}
|
|
230
|
+
return { startLine, endLine, target, replacing, library };
|
|
231
|
+
}
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
// REPLACING application
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
/**
|
|
236
|
+
* Apply REPLACING transformations to copybook content.
|
|
237
|
+
*
|
|
238
|
+
* LEADING: replace prefix in COBOL identifiers.
|
|
239
|
+
* TRAILING: replace suffix in COBOL identifiers.
|
|
240
|
+
* EXACT: replace exact token matches.
|
|
241
|
+
*/
|
|
242
|
+
function applyReplacing(content, replacings) {
|
|
243
|
+
if (replacings.length === 0)
|
|
244
|
+
return content;
|
|
245
|
+
// First pass: handle EXACT replacements that contain spaces or non-identifier
|
|
246
|
+
// characters (pseudotext). These cannot be handled by identifier-level matching.
|
|
247
|
+
let result = content;
|
|
248
|
+
for (const r of replacings) {
|
|
249
|
+
if (r.type === 'EXACT' && (r.isPseudotext || r.from.includes(' ') || !/^[A-Z][A-Z0-9-]*$/i.test(r.from))) {
|
|
250
|
+
const escaped = r.from.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
251
|
+
const re = new RegExp(escaped, 'gi');
|
|
252
|
+
result = result.replace(re, r.to);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Second pass: identifier-level replacements (LEADING, TRAILING, single-word EXACT)
|
|
256
|
+
const identifierReplacings = replacings.filter(r => !(r.type === 'EXACT' && (r.isPseudotext || r.from.includes(' ') || !/^[A-Z][A-Z0-9-]*$/i.test(r.from))));
|
|
257
|
+
if (identifierReplacings.length === 0)
|
|
258
|
+
return result;
|
|
259
|
+
return result.replace(RE_COBOL_IDENTIFIER, (match) => {
|
|
260
|
+
for (const r of identifierReplacings) {
|
|
261
|
+
const upper = match.toUpperCase();
|
|
262
|
+
const from = r.from.toUpperCase();
|
|
263
|
+
const to = r.to.toUpperCase();
|
|
264
|
+
switch (r.type) {
|
|
265
|
+
case 'LEADING':
|
|
266
|
+
if (upper.startsWith(from)) {
|
|
267
|
+
return to + match.substring(from.length);
|
|
268
|
+
}
|
|
269
|
+
break;
|
|
270
|
+
case 'TRAILING':
|
|
271
|
+
if (upper.endsWith(from)) {
|
|
272
|
+
return match.substring(0, match.length - from.length) + to;
|
|
273
|
+
}
|
|
274
|
+
break;
|
|
275
|
+
case 'EXACT':
|
|
276
|
+
if (upper === from) {
|
|
277
|
+
return to;
|
|
278
|
+
}
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return match;
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
// Main expansion engine
|
|
287
|
+
// ---------------------------------------------------------------------------
|
|
288
|
+
/**
|
|
289
|
+
* Expand COBOL COPY statements by inlining copybook content.
|
|
290
|
+
*
|
|
291
|
+
* @param content - Source COBOL content (after preprocessCobolSource)
|
|
292
|
+
* @param filePath - Path of the source file (for diagnostics)
|
|
293
|
+
* @param resolveFile - Maps a COPY target name to a filesystem path, or null if not found
|
|
294
|
+
* @param readFile - Reads file content by path, or null if unreadable
|
|
295
|
+
* @param maxDepth - Maximum nesting depth for recursive expansion (default: 10)
|
|
296
|
+
* @returns Expanded content and resolution metadata
|
|
297
|
+
*/
|
|
298
|
+
export function expandCopies(content, filePath, resolveFile, readFile, maxDepth = DEFAULT_MAX_DEPTH) {
|
|
299
|
+
const allResolutions = [];
|
|
300
|
+
const warnedCircular = new Set();
|
|
301
|
+
let totalExpansions = 0;
|
|
302
|
+
const MAX_TOTAL_EXPANSIONS = 500;
|
|
303
|
+
const expanded = expandRecursive(content, filePath, 0, new Set());
|
|
304
|
+
return {
|
|
305
|
+
expandedContent: expanded,
|
|
306
|
+
copyResolutions: allResolutions,
|
|
307
|
+
};
|
|
308
|
+
/**
|
|
309
|
+
* Recursively expand COPY statements in content.
|
|
310
|
+
*
|
|
311
|
+
* @param src - Source content to expand
|
|
312
|
+
* @param srcPath - Path of the file being expanded (for cycle detection logging)
|
|
313
|
+
* @param depth - Current recursion depth
|
|
314
|
+
* @param visited - Set of already-visited copybook paths (cycle detection)
|
|
315
|
+
*/
|
|
316
|
+
function expandRecursive(src, srcPath, depth, visited) {
|
|
317
|
+
const rawLines = src.split(/\r?\n/);
|
|
318
|
+
const logicalLines = mergeLogicalLines(rawLines);
|
|
319
|
+
const copyStatements = parseCopyStatements(logicalLines);
|
|
320
|
+
// No COPY statements — return as-is
|
|
321
|
+
if (copyStatements.length === 0)
|
|
322
|
+
return src;
|
|
323
|
+
// Process COPY statements in reverse order so line numbers stay valid
|
|
324
|
+
// as we splice content
|
|
325
|
+
const outputLines = [...rawLines];
|
|
326
|
+
for (let ci = copyStatements.length - 1; ci >= 0; ci--) {
|
|
327
|
+
const cs = copyStatements[ci];
|
|
328
|
+
// Resolve the copybook path
|
|
329
|
+
const resolvedPath = resolveFile(cs.target);
|
|
330
|
+
// Record resolution metadata
|
|
331
|
+
allResolutions.push({
|
|
332
|
+
copyTarget: cs.target,
|
|
333
|
+
resolvedPath,
|
|
334
|
+
line: cs.startLine,
|
|
335
|
+
replacing: cs.replacing,
|
|
336
|
+
library: cs.library,
|
|
337
|
+
});
|
|
338
|
+
// Cannot resolve — keep original lines
|
|
339
|
+
if (resolvedPath === null) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
// Cycle detection
|
|
343
|
+
if (visited.has(resolvedPath)) {
|
|
344
|
+
if (!warnedCircular.has(resolvedPath)) {
|
|
345
|
+
warnedCircular.add(resolvedPath);
|
|
346
|
+
console.warn(`[cobol-copy-expander] Circular COPY detected: ${cs.target} (${resolvedPath}) ` +
|
|
347
|
+
`includes itself. Skipping expansion.`);
|
|
348
|
+
}
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
// Max depth exceeded — keep unexpanded
|
|
352
|
+
if (depth >= maxDepth) {
|
|
353
|
+
console.warn(`[cobol-copy-expander] Max expansion depth (${maxDepth}) reached for ` +
|
|
354
|
+
`COPY ${cs.target} in ${srcPath}. Skipping expansion.`);
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
// Guard against exponential breadth amplification (N copybooks each with N COPYs)
|
|
358
|
+
if (++totalExpansions > MAX_TOTAL_EXPANSIONS) {
|
|
359
|
+
if (!warnedCircular.has('__max_total__')) {
|
|
360
|
+
warnedCircular.add('__max_total__');
|
|
361
|
+
console.warn(`[cobol-copy-expander] Max total expansions (${MAX_TOTAL_EXPANSIONS}) reached ` +
|
|
362
|
+
`in ${srcPath}. Skipping further expansions.`);
|
|
363
|
+
}
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
// Read the copybook content
|
|
367
|
+
const copybookContent = readFile(resolvedPath);
|
|
368
|
+
if (copybookContent === null) {
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
// Apply REPLACING transformations
|
|
372
|
+
const replaced = applyReplacing(copybookContent, cs.replacing);
|
|
373
|
+
// Recurse into the copybook for nested COPYs
|
|
374
|
+
const nestedVisited = new Set(visited);
|
|
375
|
+
nestedVisited.add(resolvedPath);
|
|
376
|
+
const expandedCopybook = expandRecursive(replaced, resolvedPath, depth + 1, nestedVisited);
|
|
377
|
+
// Splice: replace the COPY statement lines with expanded content
|
|
378
|
+
// startLine/endLine are 1-based; convert to 0-based array index
|
|
379
|
+
const expansionLines = expandedCopybook.split('\n');
|
|
380
|
+
const removeCount = cs.endLine - cs.startLine + 1;
|
|
381
|
+
outputLines.splice(cs.startLine - 1, removeCount, ...expansionLines);
|
|
382
|
+
}
|
|
383
|
+
return outputLines.join('\n');
|
|
384
|
+
}
|
|
385
|
+
}
|