@veewo/gitnexus 1.5.0-rc.4 → 1.5.1
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/benchmark/agent-context/runner.js +3 -0
- package/dist/benchmark/agent-context/runner.test.js +22 -0
- package/dist/benchmark/agent-context/tool-runner.d.ts +7 -6
- package/dist/benchmark/agent-safe-query-context/io.d.ts +2 -0
- package/dist/benchmark/agent-safe-query-context/io.js +86 -0
- package/dist/benchmark/agent-safe-query-context/io.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/io.test.js +13 -0
- package/dist/benchmark/agent-safe-query-context/report.d.ts +57 -0
- package/dist/benchmark/agent-safe-query-context/report.js +159 -0
- package/dist/benchmark/agent-safe-query-context/report.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/report.test.js +362 -0
- package/dist/benchmark/agent-safe-query-context/runner.d.ts +44 -0
- package/dist/benchmark/agent-safe-query-context/runner.js +406 -0
- package/dist/benchmark/agent-safe-query-context/runner.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/runner.test.js +290 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.d.ts +20 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.js +225 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.test.js +122 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.d.ts +47 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.js +128 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.test.js +155 -0
- package/dist/benchmark/agent-safe-query-context/telemetry-tool.d.ts +9 -0
- package/dist/benchmark/agent-safe-query-context/telemetry-tool.js +77 -0
- package/dist/benchmark/agent-safe-query-context/types.d.ts +61 -0
- package/dist/benchmark/agent-safe-query-context/types.js +8 -0
- package/dist/benchmark/analyze-runner.d.ts +1 -1
- package/dist/benchmark/analyze-runner.js +4 -3
- package/dist/benchmark/analyze-runner.test.js +7 -0
- package/dist/benchmark/runtime-poc/provenance-artifact.d.ts +47 -0
- package/dist/benchmark/runtime-poc/provenance-artifact.js +89 -0
- package/dist/benchmark/runtime-poc/runner.d.ts +31 -0
- package/dist/benchmark/runtime-poc/runner.js +163 -0
- package/dist/benchmark/u2-e2e/hydration-policy-repeatability-runner.d.ts +8 -0
- package/dist/benchmark/u2-e2e/hydration-policy-repeatability-runner.js +21 -0
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.d.ts +0 -1
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.js +53 -51
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.test.js +0 -1
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.d.ts +1 -1
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.js +82 -18
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.js +1 -2
- package/dist/benchmark/u2-e2e/retrieval-runner.js +15 -7
- package/dist/benchmark/u2-e2e/retrieval-runner.test.js +46 -0
- package/dist/cli/ai-context.d.ts +0 -1
- package/dist/cli/ai-context.js +5 -6
- package/dist/cli/ai-context.test.js +8 -0
- package/dist/cli/analyze-options.js +58 -34
- package/dist/cli/analyze-options.test.js +57 -0
- package/dist/cli/analyze-runtime-summary.js +2 -0
- package/dist/cli/analyze-runtime-summary.test.js +12 -0
- package/dist/cli/analyze-summary.d.ts +4 -0
- package/dist/cli/analyze-summary.js +43 -0
- package/dist/cli/analyze-summary.test.js +65 -1
- package/dist/cli/analyze.d.ts +11 -0
- package/dist/cli/analyze.js +34 -5
- package/dist/cli/analyze.test.d.ts +1 -0
- package/dist/cli/analyze.test.js +25 -0
- package/dist/cli/benchmark-agent-context.js +1 -1
- package/dist/cli/benchmark-agent-safe-query-context.d.ts +20 -0
- package/dist/cli/benchmark-agent-safe-query-context.js +39 -0
- package/dist/cli/benchmark-agent-safe-query-context.test.d.ts +1 -0
- package/dist/cli/benchmark-agent-safe-query-context.test.js +271 -0
- package/dist/cli/benchmark-unity.js +1 -1
- package/dist/cli/benchmark-unity.test.js +5 -1
- package/dist/cli/benchmark.d.ts +29 -0
- package/dist/cli/benchmark.js +55 -0
- package/dist/cli/index.js +27 -2
- package/dist/cli/rule-lab.d.ts +3 -7
- package/dist/cli/rule-lab.js +13 -22
- package/dist/cli/rule-lab.test.js +23 -3
- package/dist/cli/scope-manifest-config.d.ts +9 -0
- package/dist/cli/scope-manifest-config.js +37 -0
- package/dist/cli/setup.js +40 -41
- package/dist/cli/setup.test.js +14 -14
- package/dist/cli/sync-manifest.d.ts +27 -0
- package/dist/cli/sync-manifest.js +200 -0
- package/dist/cli/sync-manifest.test.d.ts +1 -0
- package/dist/cli/sync-manifest.test.js +88 -0
- package/dist/cli/tool.d.ts +2 -0
- package/dist/cli/tool.js +2 -0
- package/dist/core/config/unity-config.d.ts +1 -1
- package/dist/core/config/unity-config.js +1 -1
- package/dist/core/ingestion/call-processor.d.ts +2 -1
- package/dist/core/ingestion/call-processor.js +28 -6
- package/dist/core/ingestion/heritage-processor.d.ts +2 -1
- package/dist/core/ingestion/heritage-processor.js +30 -7
- package/dist/core/ingestion/import-processor.d.ts +2 -1
- package/dist/core/ingestion/import-processor.js +28 -6
- package/dist/core/ingestion/parsing-processor.d.ts +5 -3
- package/dist/core/ingestion/parsing-processor.js +46 -13
- package/dist/core/ingestion/pipeline.js +100 -19
- package/dist/core/ingestion/unity-lifecycle-synthetic-calls.test.js +18 -20
- package/dist/core/ingestion/unity-parity-seed.d.ts +2 -1
- package/dist/core/ingestion/unity-parity-seed.js +8 -0
- package/dist/core/ingestion/unity-resource-processor.d.ts +11 -0
- package/dist/core/ingestion/unity-resource-processor.js +102 -0
- package/dist/core/ingestion/unity-resource-processor.test.js +449 -0
- package/dist/core/ingestion/unity-runtime-binding-rules.d.ts +16 -1
- package/dist/core/ingestion/unity-runtime-binding-rules.js +193 -42
- package/dist/core/ingestion/workers/parse-worker.d.ts +2 -0
- package/dist/core/ingestion/workers/parse-worker.js +50 -6
- package/dist/core/lbug/csv-generator.test.js +2 -2
- package/dist/core/tree-sitter/csharp-define-profile.d.ts +6 -0
- package/dist/core/tree-sitter/csharp-define-profile.js +43 -0
- package/dist/core/tree-sitter/csharp-preproc-normalizer.d.ts +14 -0
- package/dist/core/tree-sitter/csharp-preproc-normalizer.js +261 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +10 -0
- package/dist/core/tree-sitter/parser-loader.js +19 -0
- package/dist/core/unity/doc-contract.test.d.ts +1 -0
- package/dist/core/unity/doc-contract.test.js +30 -0
- package/dist/core/unity/prefab-source-scan.d.ts +25 -0
- package/dist/core/unity/prefab-source-scan.js +152 -0
- package/dist/core/unity/prefab-source-scan.test.d.ts +1 -0
- package/dist/core/unity/prefab-source-scan.test.js +70 -0
- package/dist/core/unity/scan-context.d.ts +12 -0
- package/dist/core/unity/scan-context.js +50 -2
- package/dist/core/unity/scan-context.test.js +74 -0
- package/dist/mcp/local/agent-safe-response.d.ts +10 -0
- package/dist/mcp/local/agent-safe-response.js +639 -0
- package/dist/mcp/local/derived-process-reader.js +1 -1
- package/dist/mcp/local/local-backend.d.ts +18 -1
- package/dist/mcp/local/local-backend.js +319 -125
- package/dist/mcp/local/process-confidence.d.ts +1 -2
- package/dist/mcp/local/process-confidence.js +0 -3
- package/dist/mcp/local/process-confidence.test.js +4 -2
- package/dist/mcp/local/process-evidence.d.ts +1 -8
- package/dist/mcp/local/process-evidence.js +1 -23
- package/dist/mcp/local/process-evidence.test.js +2 -16
- package/dist/mcp/local/process-ref.d.ts +1 -1
- package/dist/mcp/local/runtime-chain-closure-evaluator.d.ts +33 -0
- package/dist/mcp/local/runtime-chain-closure-evaluator.js +273 -0
- package/dist/mcp/local/runtime-chain-graph-candidates.d.ts +23 -0
- package/dist/mcp/local/runtime-chain-graph-candidates.js +131 -0
- package/dist/mcp/local/runtime-chain-verify.d.ts +1 -1
- package/dist/mcp/local/runtime-chain-verify.js +149 -138
- package/dist/mcp/local/runtime-chain-verify.test.js +126 -68
- package/dist/mcp/local/runtime-claim-rule-registry.d.ts +4 -0
- package/dist/mcp/local/runtime-claim-rule-registry.js +4 -0
- package/dist/mcp/local/runtime-claim-rule-registry.test.js +37 -4
- package/dist/mcp/local/runtime-claim.d.ts +11 -0
- package/dist/mcp/local/runtime-claim.js +28 -0
- package/dist/mcp/local/unity-evidence-view.d.ts +1 -1
- package/dist/mcp/local/unity-evidence-view.js +1 -1
- package/dist/mcp/local/unity-evidence-view.test.js +22 -0
- package/dist/mcp/tools.js +51 -21
- package/dist/rule-lab/analyze.d.ts +2 -1
- package/dist/rule-lab/analyze.js +94 -59
- package/dist/rule-lab/analyze.test.js +238 -20
- package/dist/rule-lab/curate.d.ts +2 -1
- package/dist/rule-lab/curate.js +24 -3
- package/dist/rule-lab/curate.test.js +65 -0
- package/dist/rule-lab/curation-input-builder.d.ts +45 -0
- package/dist/rule-lab/curation-input-builder.js +133 -0
- package/dist/rule-lab/promote.js +80 -7
- package/dist/rule-lab/promote.test.js +150 -0
- package/dist/rule-lab/review-pack.d.ts +3 -0
- package/dist/rule-lab/review-pack.js +41 -1
- package/dist/rule-lab/review-pack.test.js +67 -0
- package/dist/rule-lab/types.d.ts +29 -0
- package/dist/types/pipeline.d.ts +16 -0
- package/package.json +14 -13
- package/scripts/check-sync-manifest-traceability.mjs +203 -0
- package/scripts/run-node-tests.mjs +61 -0
- package/scripts/tree-sitter-audit-classify.mjs +172 -0
- package/skills/_shared/unity-rule-authoring-contract.md +64 -0
- package/skills/_shared/unity-runtime-process-contract.md +16 -0
- package/skills/gitnexus-cli.md +44 -4
- package/skills/gitnexus-debugging.md +9 -0
- package/skills/gitnexus-exploring.md +66 -18
- package/skills/gitnexus-guide.md +42 -3
- package/skills/gitnexus-impact-analysis.md +8 -0
- package/skills/gitnexus-pr-review.md +8 -0
- package/skills/gitnexus-refactoring.md +8 -0
- package/skills/gitnexus-unity-rule-gen.md +66 -312
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { generateId } from '../../lib/utils.js';
|
|
2
2
|
const RULE_EDGE_CONFIDENCE = 0.75;
|
|
3
|
-
|
|
3
|
+
const RULE_ANOMALY_PREVIEW_LIMIT = 5;
|
|
4
|
+
export function applyUnityRuntimeBindingRules(graph, rules, config) {
|
|
4
5
|
const ruleResults = [];
|
|
5
6
|
let totalEdges = 0;
|
|
6
7
|
const existingPairs = new Set();
|
|
@@ -27,7 +28,10 @@ export function applyUnityRuntimeBindingRules(graph, rules, _config) {
|
|
|
27
28
|
};
|
|
28
29
|
// Pre-build indexes
|
|
29
30
|
const methodsByClassId = new Map();
|
|
30
|
-
const
|
|
31
|
+
const containerNodes = [];
|
|
32
|
+
const containerLabels = config.enableContainerNodes
|
|
33
|
+
? new Set(['Class', 'Struct', 'Interface', 'Record'])
|
|
34
|
+
: new Set(['Class']);
|
|
31
35
|
for (const rel of graph.iterRelationships()) {
|
|
32
36
|
if (rel.type !== 'HAS_METHOD')
|
|
33
37
|
continue;
|
|
@@ -39,8 +43,8 @@ export function applyUnityRuntimeBindingRules(graph, rules, _config) {
|
|
|
39
43
|
methodsByClassId.set(rel.sourceId, list);
|
|
40
44
|
}
|
|
41
45
|
for (const node of graph.iterNodes()) {
|
|
42
|
-
if (node.label
|
|
43
|
-
|
|
46
|
+
if (containerLabels.has(node.label))
|
|
47
|
+
containerNodes.push(node);
|
|
44
48
|
}
|
|
45
49
|
// Collect UNITY_ASSET_GUID_REF and UNITY_COMPONENT_INSTANCE edges
|
|
46
50
|
const assetGuidRefs = [];
|
|
@@ -51,6 +55,24 @@ export function applyUnityRuntimeBindingRules(graph, rules, _config) {
|
|
|
51
55
|
else if (rel.type === 'UNITY_COMPONENT_INSTANCE')
|
|
52
56
|
componentInstances.push(rel);
|
|
53
57
|
}
|
|
58
|
+
const prefabSourceTargetsBySource = buildPrefabSourceTargetsBySource(assetGuidRefs);
|
|
59
|
+
const methodsByResourceId = buildMethodsByResourceId(componentInstances, methodsByClassId);
|
|
60
|
+
const executionState = {
|
|
61
|
+
methodsByResourceId,
|
|
62
|
+
resourceMethodLookupCache: new Map(),
|
|
63
|
+
sceneRuntimeResourceIdsCache: new Map(),
|
|
64
|
+
diagnostics: {
|
|
65
|
+
rulesEvaluated: rules.length,
|
|
66
|
+
bindingsEvaluated: 0,
|
|
67
|
+
bindingsByKind: {},
|
|
68
|
+
methodLookupCalls: 0,
|
|
69
|
+
methodLookupCacheHits: 0,
|
|
70
|
+
sceneRuntimeTraversalCalls: 0,
|
|
71
|
+
sceneRuntimeTraversalCacheHits: 0,
|
|
72
|
+
sceneRuntimeResourcesVisited: 0,
|
|
73
|
+
anomalySet: new Set(),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
54
76
|
// Pre-build scene file index: lowercase scene name → fileId[]
|
|
55
77
|
const sceneFilesByName = new Map();
|
|
56
78
|
for (const node of graph.iterNodes()) {
|
|
@@ -67,51 +89,69 @@ export function applyUnityRuntimeBindingRules(graph, rules, _config) {
|
|
|
67
89
|
for (const rule of rules) {
|
|
68
90
|
let ruleEdges = 0;
|
|
69
91
|
for (const binding of rule.resource_bindings ?? []) {
|
|
70
|
-
|
|
92
|
+
executionState.diagnostics.bindingsEvaluated += 1;
|
|
93
|
+
executionState.diagnostics.bindingsByKind[binding.kind] =
|
|
94
|
+
(executionState.diagnostics.bindingsByKind[binding.kind] || 0) + 1;
|
|
95
|
+
ruleEdges += processBinding(binding, rule.id, assetGuidRefs, componentInstances, methodsByClassId, containerNodes, sceneFilesByName, prefabSourceTargetsBySource, executionState, addSyntheticEdge);
|
|
71
96
|
}
|
|
72
97
|
if (rule.lifecycle_overrides?.additional_entry_points?.length) {
|
|
73
|
-
ruleEdges += processLifecycleOverrides(rule, methodsByClassId,
|
|
98
|
+
ruleEdges += processLifecycleOverrides(rule, methodsByClassId, containerNodes, addSyntheticEdge);
|
|
74
99
|
}
|
|
75
100
|
totalEdges += ruleEdges;
|
|
76
101
|
ruleResults.push({ ruleId: rule.id, edgesInjected: ruleEdges });
|
|
77
102
|
}
|
|
78
|
-
return {
|
|
103
|
+
return {
|
|
104
|
+
edgesInjected: totalEdges,
|
|
105
|
+
ruleResults,
|
|
106
|
+
diagnostics: finalizeRuleBindingDiagnostics(executionState.diagnostics, totalEdges),
|
|
107
|
+
};
|
|
79
108
|
}
|
|
80
|
-
function findMethodsOnResource(resourceFileId,
|
|
81
|
-
|
|
109
|
+
function findMethodsOnResource(resourceFileId, executionState, entryPoints) {
|
|
110
|
+
executionState.diagnostics.methodLookupCalls += 1;
|
|
111
|
+
const cacheKey = `${resourceFileId}::${entryPoints.join('|')}`;
|
|
112
|
+
const cached = executionState.resourceMethodLookupCache.get(cacheKey);
|
|
113
|
+
if (cached) {
|
|
114
|
+
executionState.diagnostics.methodLookupCacheHits += 1;
|
|
115
|
+
return cached;
|
|
116
|
+
}
|
|
117
|
+
const methods = executionState.methodsByResourceId.get(resourceFileId) ?? [];
|
|
118
|
+
if (methods.length === 0) {
|
|
119
|
+
executionState.resourceMethodLookupCache.set(cacheKey, []);
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
82
122
|
const entrySet = new Set(entryPoints);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
for (const method of methodsByClassId.get(classId) ?? []) {
|
|
88
|
-
if (entrySet.has(method.properties.name))
|
|
89
|
-
results.push(method);
|
|
90
|
-
}
|
|
123
|
+
const results = [];
|
|
124
|
+
for (const method of methods) {
|
|
125
|
+
if (entrySet.has(method.properties.name))
|
|
126
|
+
results.push(method);
|
|
91
127
|
}
|
|
128
|
+
executionState.resourceMethodLookupCache.set(cacheKey, results);
|
|
92
129
|
return results;
|
|
93
130
|
}
|
|
94
|
-
function processBinding(binding, ruleId, assetGuidRefs, componentInstances, methodsByClassId,
|
|
131
|
+
function processBinding(binding, ruleId, assetGuidRefs, componentInstances, methodsByClassId, containerNodes, sceneFilesByName, prefabSourceTargetsBySource, executionState, addEdge) {
|
|
95
132
|
if (binding.kind === 'asset_ref_loads_components') {
|
|
96
|
-
return processAssetRefLoadsComponents(binding, ruleId, assetGuidRefs,
|
|
133
|
+
return processAssetRefLoadsComponents(binding, ruleId, assetGuidRefs, executionState, addEdge);
|
|
97
134
|
}
|
|
98
135
|
if (binding.kind === 'method_triggers_field_load') {
|
|
99
|
-
return processMethodTriggersFieldLoad(binding, ruleId, assetGuidRefs, componentInstances, methodsByClassId,
|
|
136
|
+
return processMethodTriggersFieldLoad(binding, ruleId, assetGuidRefs, componentInstances, methodsByClassId, containerNodes, executionState, addEdge);
|
|
100
137
|
}
|
|
101
138
|
if (binding.kind === 'method_triggers_scene_load') {
|
|
102
|
-
return processMethodTriggersSceneLoad(binding, ruleId, componentInstances, methodsByClassId,
|
|
139
|
+
return processMethodTriggersSceneLoad(binding, ruleId, componentInstances, methodsByClassId, containerNodes, sceneFilesByName, prefabSourceTargetsBySource, executionState, addEdge);
|
|
103
140
|
}
|
|
104
141
|
if (binding.kind === 'method_triggers_method') {
|
|
105
|
-
return processMethodTriggersMethod(binding, ruleId, methodsByClassId,
|
|
142
|
+
return processMethodTriggersMethod(binding, ruleId, methodsByClassId, containerNodes, addEdge);
|
|
106
143
|
}
|
|
144
|
+
addAnomaly(executionState.diagnostics, `rule=${ruleId}: unsupported resource_binding kind "${binding.kind}"`);
|
|
107
145
|
return 0;
|
|
108
146
|
}
|
|
109
|
-
function processAssetRefLoadsComponents(binding, ruleId, assetGuidRefs,
|
|
147
|
+
function processAssetRefLoadsComponents(binding, ruleId, assetGuidRefs, executionState, addEdge) {
|
|
110
148
|
let count = 0;
|
|
111
149
|
const pattern = binding.ref_field_pattern ? new RegExp(binding.ref_field_pattern) : null;
|
|
112
150
|
const entryPoints = binding.target_entry_points ?? [];
|
|
113
|
-
if (!pattern || entryPoints.length === 0)
|
|
151
|
+
if (!pattern || entryPoints.length === 0) {
|
|
152
|
+
addAnomaly(executionState.diagnostics, `rule=${ruleId}: asset_ref_loads_components missing ref_field_pattern or target_entry_points`);
|
|
114
153
|
return 0;
|
|
154
|
+
}
|
|
115
155
|
const runtimeRootId = generateId('Method', 'unity-runtime-root');
|
|
116
156
|
for (const ref of assetGuidRefs) {
|
|
117
157
|
let fieldName = '';
|
|
@@ -124,7 +164,7 @@ function processAssetRefLoadsComponents(binding, ruleId, assetGuidRefs, componen
|
|
|
124
164
|
}
|
|
125
165
|
if (!pattern.test(fieldName))
|
|
126
166
|
continue;
|
|
127
|
-
const targetMethods = findMethodsOnResource(ref.targetId,
|
|
167
|
+
const targetMethods = findMethodsOnResource(ref.targetId, executionState, entryPoints);
|
|
128
168
|
for (const method of targetMethods) {
|
|
129
169
|
if (addEdge(runtimeRootId, method.id, `unity-rule-resource-load:${ruleId}`))
|
|
130
170
|
count++;
|
|
@@ -132,15 +172,17 @@ function processAssetRefLoadsComponents(binding, ruleId, assetGuidRefs, componen
|
|
|
132
172
|
}
|
|
133
173
|
return count;
|
|
134
174
|
}
|
|
135
|
-
function processMethodTriggersFieldLoad(binding, ruleId, assetGuidRefs, componentInstances, methodsByClassId,
|
|
175
|
+
function processMethodTriggersFieldLoad(binding, ruleId, assetGuidRefs, componentInstances, methodsByClassId, containerNodes, executionState, addEdge) {
|
|
136
176
|
let count = 0;
|
|
137
177
|
const classPattern = binding.host_class_pattern ? new RegExp(binding.host_class_pattern) : null;
|
|
138
178
|
const loaderMethodNames = new Set(binding.loader_methods ?? []);
|
|
139
179
|
const entryPoints = binding.target_entry_points ?? [];
|
|
140
180
|
const defaultEntryPoints = ['OnEnable', 'Awake', 'Start'];
|
|
141
181
|
const resolvedEntryPoints = entryPoints.length > 0 ? entryPoints : defaultEntryPoints;
|
|
142
|
-
if (!classPattern || loaderMethodNames.size === 0)
|
|
182
|
+
if (!classPattern || loaderMethodNames.size === 0) {
|
|
183
|
+
addAnomaly(executionState.diagnostics, `rule=${ruleId}: method_triggers_field_load missing host_class_pattern or loader_methods`);
|
|
143
184
|
return 0;
|
|
185
|
+
}
|
|
144
186
|
// Build asset ref index by source file
|
|
145
187
|
const refsBySource = new Map();
|
|
146
188
|
for (const ref of assetGuidRefs) {
|
|
@@ -148,7 +190,7 @@ function processMethodTriggersFieldLoad(binding, ruleId, assetGuidRefs, componen
|
|
|
148
190
|
list.push(ref);
|
|
149
191
|
refsBySource.set(ref.sourceId, list);
|
|
150
192
|
}
|
|
151
|
-
for (const cls of
|
|
193
|
+
for (const cls of containerNodes) {
|
|
152
194
|
if (!classPattern.test(cls.properties.name))
|
|
153
195
|
continue;
|
|
154
196
|
const methods = methodsByClassId.get(cls.id) ?? [];
|
|
@@ -164,7 +206,7 @@ function processMethodTriggersFieldLoad(binding, ruleId, assetGuidRefs, componen
|
|
|
164
206
|
// Follow asset refs from those resource files
|
|
165
207
|
for (const resourceFileId of resourceFileIds) {
|
|
166
208
|
for (const ref of refsBySource.get(resourceFileId) ?? []) {
|
|
167
|
-
const targetMethods = findMethodsOnResource(ref.targetId,
|
|
209
|
+
const targetMethods = findMethodsOnResource(ref.targetId, executionState, resolvedEntryPoints);
|
|
168
210
|
for (const loader of loaders) {
|
|
169
211
|
for (const target of targetMethods) {
|
|
170
212
|
if (addEdge(loader.id, target.id, `unity-rule-loader-bridge:${ruleId}`))
|
|
@@ -176,7 +218,7 @@ function processMethodTriggersFieldLoad(binding, ruleId, assetGuidRefs, componen
|
|
|
176
218
|
}
|
|
177
219
|
return count;
|
|
178
220
|
}
|
|
179
|
-
function processMethodTriggersSceneLoad(binding, ruleId, componentInstances, methodsByClassId,
|
|
221
|
+
function processMethodTriggersSceneLoad(binding, ruleId, componentInstances, methodsByClassId, containerNodes, sceneFilesByName, prefabSourceTargetsBySource, executionState, addEdge) {
|
|
180
222
|
const classPattern = binding.host_class_pattern ? new RegExp(binding.host_class_pattern) : null;
|
|
181
223
|
const loaderMethodNames = new Set(binding.loader_methods ?? []);
|
|
182
224
|
const sceneName = binding.scene_name;
|
|
@@ -184,13 +226,17 @@ function processMethodTriggersSceneLoad(binding, ruleId, componentInstances, met
|
|
|
184
226
|
const entryPoints = (binding.target_entry_points ?? []).length > 0
|
|
185
227
|
? binding.target_entry_points
|
|
186
228
|
: defaultEntryPoints;
|
|
187
|
-
if (!classPattern || loaderMethodNames.size === 0 || !sceneName)
|
|
229
|
+
if (!classPattern || loaderMethodNames.size === 0 || !sceneName) {
|
|
230
|
+
addAnomaly(executionState.diagnostics, `rule=${ruleId}: method_triggers_scene_load missing host_class_pattern, loader_methods, or scene_name`);
|
|
188
231
|
return 0;
|
|
232
|
+
}
|
|
189
233
|
const sceneFileIds = sceneFilesByName.get(sceneName.toLowerCase()) ?? [];
|
|
190
|
-
if (sceneFileIds.length === 0)
|
|
234
|
+
if (sceneFileIds.length === 0) {
|
|
235
|
+
addAnomaly(executionState.diagnostics, `rule=${ruleId}: scene "${sceneName}" not found in File(.unity) index`);
|
|
191
236
|
return 0;
|
|
237
|
+
}
|
|
192
238
|
let count = 0;
|
|
193
|
-
for (const cls of
|
|
239
|
+
for (const cls of containerNodes) {
|
|
194
240
|
if (!classPattern.test(cls.properties.name))
|
|
195
241
|
continue;
|
|
196
242
|
const methods = methodsByClassId.get(cls.id) ?? [];
|
|
@@ -198,18 +244,82 @@ function processMethodTriggersSceneLoad(binding, ruleId, componentInstances, met
|
|
|
198
244
|
if (loaders.length === 0)
|
|
199
245
|
continue;
|
|
200
246
|
for (const sceneFileId of sceneFileIds) {
|
|
201
|
-
const
|
|
202
|
-
for (const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
247
|
+
const runtimeResourceIds = getSceneRuntimeResourceIds(sceneFileId, prefabSourceTargetsBySource, executionState);
|
|
248
|
+
for (const runtimeResourceId of runtimeResourceIds) {
|
|
249
|
+
const targetMethods = findMethodsOnResource(runtimeResourceId, executionState, entryPoints);
|
|
250
|
+
for (const loader of loaders) {
|
|
251
|
+
for (const target of targetMethods) {
|
|
252
|
+
if (addEdge(loader.id, target.id, `unity-rule-scene-load:${ruleId}`))
|
|
253
|
+
count++;
|
|
254
|
+
}
|
|
206
255
|
}
|
|
207
256
|
}
|
|
208
257
|
}
|
|
209
258
|
}
|
|
210
259
|
return count;
|
|
211
260
|
}
|
|
212
|
-
function
|
|
261
|
+
function buildMethodsByResourceId(componentInstances, methodsByClassId) {
|
|
262
|
+
const methodsByResourceId = new Map();
|
|
263
|
+
for (const componentRef of componentInstances) {
|
|
264
|
+
const methods = methodsByClassId.get(componentRef.sourceId) ?? [];
|
|
265
|
+
if (methods.length === 0)
|
|
266
|
+
continue;
|
|
267
|
+
const list = methodsByResourceId.get(componentRef.targetId) ?? [];
|
|
268
|
+
list.push(...methods);
|
|
269
|
+
methodsByResourceId.set(componentRef.targetId, list);
|
|
270
|
+
}
|
|
271
|
+
return methodsByResourceId;
|
|
272
|
+
}
|
|
273
|
+
function buildPrefabSourceTargetsBySource(assetGuidRefs) {
|
|
274
|
+
const targetsBySource = new Map();
|
|
275
|
+
for (const ref of assetGuidRefs) {
|
|
276
|
+
let fieldName = '';
|
|
277
|
+
try {
|
|
278
|
+
const parsed = JSON.parse(String(ref.reason || '{}'));
|
|
279
|
+
fieldName = String(parsed?.fieldName || '');
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
if (fieldName !== 'm_SourcePrefab')
|
|
285
|
+
continue;
|
|
286
|
+
const targets = targetsBySource.get(ref.sourceId) ?? new Set();
|
|
287
|
+
targets.add(ref.targetId);
|
|
288
|
+
targetsBySource.set(ref.sourceId, targets);
|
|
289
|
+
}
|
|
290
|
+
const out = new Map();
|
|
291
|
+
for (const [sourceId, targets] of targetsBySource.entries()) {
|
|
292
|
+
out.set(sourceId, [...targets]);
|
|
293
|
+
}
|
|
294
|
+
return out;
|
|
295
|
+
}
|
|
296
|
+
function collectSceneRuntimeResourceIds(sceneFileId, prefabSourceTargetsBySource) {
|
|
297
|
+
const visited = new Set([sceneFileId]);
|
|
298
|
+
const queue = [sceneFileId];
|
|
299
|
+
while (queue.length > 0) {
|
|
300
|
+
const currentId = queue.shift();
|
|
301
|
+
for (const targetId of prefabSourceTargetsBySource.get(currentId) ?? []) {
|
|
302
|
+
if (visited.has(targetId))
|
|
303
|
+
continue;
|
|
304
|
+
visited.add(targetId);
|
|
305
|
+
queue.push(targetId);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return [...visited];
|
|
309
|
+
}
|
|
310
|
+
function getSceneRuntimeResourceIds(sceneFileId, prefabSourceTargetsBySource, executionState) {
|
|
311
|
+
executionState.diagnostics.sceneRuntimeTraversalCalls += 1;
|
|
312
|
+
const cached = executionState.sceneRuntimeResourceIdsCache.get(sceneFileId);
|
|
313
|
+
if (cached) {
|
|
314
|
+
executionState.diagnostics.sceneRuntimeTraversalCacheHits += 1;
|
|
315
|
+
return cached;
|
|
316
|
+
}
|
|
317
|
+
const ids = collectSceneRuntimeResourceIds(sceneFileId, prefabSourceTargetsBySource);
|
|
318
|
+
executionState.sceneRuntimeResourceIdsCache.set(sceneFileId, ids);
|
|
319
|
+
executionState.diagnostics.sceneRuntimeResourcesVisited += ids.length;
|
|
320
|
+
return ids;
|
|
321
|
+
}
|
|
322
|
+
function processMethodTriggersMethod(binding, ruleId, methodsByClassId, containerNodes, addEdge) {
|
|
213
323
|
const { source_class_pattern, source_method, target_class_pattern, target_method } = binding;
|
|
214
324
|
if (!source_class_pattern || !source_method || !target_class_pattern || !target_method)
|
|
215
325
|
return 0;
|
|
@@ -217,7 +327,7 @@ function processMethodTriggersMethod(binding, ruleId, methodsByClassId, classNod
|
|
|
217
327
|
const tgtPattern = new RegExp(target_class_pattern);
|
|
218
328
|
let sourceMethodId;
|
|
219
329
|
let targetMethodId;
|
|
220
|
-
for (const cls of
|
|
330
|
+
for (const cls of containerNodes) {
|
|
221
331
|
if (!sourceMethodId && srcPattern.test(cls.properties.name)) {
|
|
222
332
|
const match = (methodsByClassId.get(cls.id) ?? []).find(m => m.properties.name === source_method);
|
|
223
333
|
if (match)
|
|
@@ -235,7 +345,7 @@ function processMethodTriggersMethod(binding, ruleId, methodsByClassId, classNod
|
|
|
235
345
|
return 0;
|
|
236
346
|
return addEdge(sourceMethodId, targetMethodId, `unity-rule-method-bridge:${ruleId}`) ? 1 : 0;
|
|
237
347
|
}
|
|
238
|
-
function processLifecycleOverrides(rule, methodsByClassId,
|
|
348
|
+
function processLifecycleOverrides(rule, methodsByClassId, containerNodes, addEdge) {
|
|
239
349
|
const overrides = rule.lifecycle_overrides;
|
|
240
350
|
if (!overrides?.additional_entry_points?.length)
|
|
241
351
|
return 0;
|
|
@@ -243,7 +353,7 @@ function processLifecycleOverrides(rule, methodsByClassId, classNodes, addEdge)
|
|
|
243
353
|
const scopePattern = overrides.scope ? new RegExp(overrides.scope) : null;
|
|
244
354
|
let count = 0;
|
|
245
355
|
const runtimeRootId = generateId('Method', 'unity-runtime-root');
|
|
246
|
-
for (const cls of
|
|
356
|
+
for (const cls of containerNodes) {
|
|
247
357
|
if (scopePattern && !scopePattern.test(cls.properties.filePath ?? cls.properties.name))
|
|
248
358
|
continue;
|
|
249
359
|
for (const method of methodsByClassId.get(cls.id) ?? []) {
|
|
@@ -255,3 +365,44 @@ function processLifecycleOverrides(rule, methodsByClassId, classNodes, addEdge)
|
|
|
255
365
|
}
|
|
256
366
|
return count;
|
|
257
367
|
}
|
|
368
|
+
function addAnomaly(diagnostics, message) {
|
|
369
|
+
diagnostics.anomalySet.add(message);
|
|
370
|
+
}
|
|
371
|
+
function finalizeRuleBindingDiagnostics(diagnostics, edgesInjected) {
|
|
372
|
+
const anomalies = [...diagnostics.anomalySet];
|
|
373
|
+
const shouldAgentReport = anomalies.length > 0;
|
|
374
|
+
const bindingsByKind = Object.fromEntries(Object.entries(diagnostics.bindingsByKind).sort(([a], [b]) => a.localeCompare(b)));
|
|
375
|
+
const kindSummary = Object.entries(bindingsByKind)
|
|
376
|
+
.map(([kind, count]) => `${kind}=${count}`)
|
|
377
|
+
.join(', ') || 'none';
|
|
378
|
+
const summary = [
|
|
379
|
+
`rule_binding.summary: rules=${diagnostics.rulesEvaluated}, bindings=${diagnostics.bindingsEvaluated}, edges=${edgesInjected}`,
|
|
380
|
+
`rule_binding.bindings_by_kind: ${kindSummary}`,
|
|
381
|
+
`rule_binding.lookup: method_calls=${diagnostics.methodLookupCalls}, cache_hits=${diagnostics.methodLookupCacheHits}`,
|
|
382
|
+
`rule_binding.scene_closure: traversals=${diagnostics.sceneRuntimeTraversalCalls}, cache_hits=${diagnostics.sceneRuntimeTraversalCacheHits}, visited_resources=${diagnostics.sceneRuntimeResourcesVisited}`,
|
|
383
|
+
`rule_binding.agent_report: should_report=${shouldAgentReport} reason="${shouldAgentReport ? 'rule-binding anomalies detected' : 'no anomalies detected'}"`,
|
|
384
|
+
];
|
|
385
|
+
if (anomalies.length > 0) {
|
|
386
|
+
summary.push(`rule_binding.anomalies: count=${anomalies.length}`);
|
|
387
|
+
for (const anomaly of anomalies.slice(0, RULE_ANOMALY_PREVIEW_LIMIT)) {
|
|
388
|
+
summary.push(`rule_binding.anomaly: ${anomaly}`);
|
|
389
|
+
}
|
|
390
|
+
if (anomalies.length > RULE_ANOMALY_PREVIEW_LIMIT) {
|
|
391
|
+
summary.push(`rule_binding.anomaly: ... ${anomalies.length - RULE_ANOMALY_PREVIEW_LIMIT} more`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return {
|
|
395
|
+
rulesEvaluated: diagnostics.rulesEvaluated,
|
|
396
|
+
bindingsEvaluated: diagnostics.bindingsEvaluated,
|
|
397
|
+
bindingsByKind,
|
|
398
|
+
methodLookupCalls: diagnostics.methodLookupCalls,
|
|
399
|
+
methodLookupCacheHits: diagnostics.methodLookupCacheHits,
|
|
400
|
+
sceneRuntimeTraversalCalls: diagnostics.sceneRuntimeTraversalCalls,
|
|
401
|
+
sceneRuntimeTraversalCacheHits: diagnostics.sceneRuntimeTraversalCacheHits,
|
|
402
|
+
sceneRuntimeResourcesVisited: diagnostics.sceneRuntimeResourcesVisited,
|
|
403
|
+
anomalies,
|
|
404
|
+
shouldAgentReport,
|
|
405
|
+
agentReportReason: shouldAgentReport ? 'rule-binding anomalies detected' : 'no anomalies detected',
|
|
406
|
+
summary,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
@@ -97,10 +97,12 @@ export interface ParseWorkerResult {
|
|
|
97
97
|
routes: ExtractedRoute[];
|
|
98
98
|
constructorBindings: FileConstructorBindings[];
|
|
99
99
|
skippedLanguages: Record<string, number>;
|
|
100
|
+
csharpPreprocFallbackFiles: number;
|
|
100
101
|
fileCount: number;
|
|
101
102
|
}
|
|
102
103
|
export interface ParseWorkerInput {
|
|
103
104
|
path: string;
|
|
104
105
|
content: string;
|
|
106
|
+
rawContent?: string;
|
|
105
107
|
}
|
|
106
108
|
export {};
|
|
@@ -14,7 +14,7 @@ import Ruby from 'tree-sitter-ruby';
|
|
|
14
14
|
import { createRequire } from 'node:module';
|
|
15
15
|
import { SupportedLanguages } from '../../../config/supported-languages.js';
|
|
16
16
|
import { LANGUAGE_QUERIES } from '../tree-sitter-queries.js';
|
|
17
|
-
import {
|
|
17
|
+
import { TREE_SITTER_MAX_BUFFER } from '../constants.js';
|
|
18
18
|
// tree-sitter-swift is an optionalDependency — may not be installed
|
|
19
19
|
const _require = createRequire(import.meta.url);
|
|
20
20
|
let Swift = null;
|
|
@@ -166,6 +166,7 @@ const processBatch = (files, onProgress) => {
|
|
|
166
166
|
routes: [],
|
|
167
167
|
constructorBindings: [],
|
|
168
168
|
skippedLanguages: {},
|
|
169
|
+
csharpPreprocFallbackFiles: 0,
|
|
169
170
|
fileCount: 0,
|
|
170
171
|
};
|
|
171
172
|
// Group by language to minimize setLanguage calls
|
|
@@ -716,14 +717,56 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
716
717
|
if (file.content.length > TREE_SITTER_MAX_BUFFER)
|
|
717
718
|
continue;
|
|
718
719
|
let tree;
|
|
720
|
+
let usedRawContentFallback = false;
|
|
719
721
|
try {
|
|
720
|
-
|
|
722
|
+
const MAX_CHUNK = 4096;
|
|
723
|
+
tree = parser.parse((index) => {
|
|
724
|
+
if (index >= file.content.length)
|
|
725
|
+
return null;
|
|
726
|
+
return file.content.slice(index, index + MAX_CHUNK);
|
|
727
|
+
});
|
|
721
728
|
}
|
|
722
729
|
catch (err) {
|
|
723
|
-
|
|
724
|
-
|
|
730
|
+
if (file.rawContent && file.rawContent !== file.content) {
|
|
731
|
+
try {
|
|
732
|
+
const MAX_CHUNK = 4096;
|
|
733
|
+
tree = parser.parse((index) => {
|
|
734
|
+
if (index >= file.rawContent.length)
|
|
735
|
+
return null;
|
|
736
|
+
return file.rawContent.slice(index, index + MAX_CHUNK);
|
|
737
|
+
});
|
|
738
|
+
usedRawContentFallback = true;
|
|
739
|
+
}
|
|
740
|
+
catch {
|
|
741
|
+
console.warn(`Failed to parse file ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
console.warn(`Failed to parse file ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
if (file.rawContent && file.rawContent !== file.content && tree.rootNode?.hasError) {
|
|
751
|
+
try {
|
|
752
|
+
const MAX_CHUNK = 4096;
|
|
753
|
+
const rawTree = parser.parse((index) => {
|
|
754
|
+
if (index >= file.rawContent.length)
|
|
755
|
+
return null;
|
|
756
|
+
return file.rawContent.slice(index, index + MAX_CHUNK);
|
|
757
|
+
});
|
|
758
|
+
if (!rawTree.rootNode?.hasError) {
|
|
759
|
+
tree = rawTree;
|
|
760
|
+
usedRawContentFallback = true;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
catch {
|
|
764
|
+
// Keep normalized parse result when raw fallback parsing fails
|
|
765
|
+
}
|
|
725
766
|
}
|
|
726
767
|
result.fileCount++;
|
|
768
|
+
if (usedRawContentFallback)
|
|
769
|
+
result.csharpPreprocFallbackFiles += 1;
|
|
727
770
|
onFileProcessed?.();
|
|
728
771
|
// Build per-file type environment + constructor bindings in a single AST walk.
|
|
729
772
|
// Constructor bindings are verified against the SymbolTable in processCallsFromExtracted.
|
|
@@ -1030,7 +1073,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1030
1073
|
/** Accumulated result across sub-batches */
|
|
1031
1074
|
let accumulated = {
|
|
1032
1075
|
nodes: [], relationships: [], symbols: [],
|
|
1033
|
-
imports: [], calls: [], heritage: [], routes: [], constructorBindings: [], skippedLanguages: {}, fileCount: 0,
|
|
1076
|
+
imports: [], calls: [], heritage: [], routes: [], constructorBindings: [], skippedLanguages: {}, csharpPreprocFallbackFiles: 0, fileCount: 0,
|
|
1034
1077
|
};
|
|
1035
1078
|
let cumulativeProcessed = 0;
|
|
1036
1079
|
const mergeResult = (target, src) => {
|
|
@@ -1045,6 +1088,7 @@ const mergeResult = (target, src) => {
|
|
|
1045
1088
|
for (const [lang, count] of Object.entries(src.skippedLanguages)) {
|
|
1046
1089
|
target.skippedLanguages[lang] = (target.skippedLanguages[lang] || 0) + count;
|
|
1047
1090
|
}
|
|
1091
|
+
target.csharpPreprocFallbackFiles += src.csharpPreprocFallbackFiles;
|
|
1048
1092
|
target.fileCount += src.fileCount;
|
|
1049
1093
|
};
|
|
1050
1094
|
parentPort.on('message', (msg) => {
|
|
@@ -1064,7 +1108,7 @@ parentPort.on('message', (msg) => {
|
|
|
1064
1108
|
if (msg && msg.type === 'flush') {
|
|
1065
1109
|
parentPort.postMessage({ type: 'result', data: accumulated });
|
|
1066
1110
|
// Reset for potential reuse
|
|
1067
|
-
accumulated = { nodes: [], relationships: [], symbols: [], imports: [], calls: [], heritage: [], routes: [], constructorBindings: [], skippedLanguages: {}, fileCount: 0 };
|
|
1111
|
+
accumulated = { nodes: [], relationships: [], symbols: [], imports: [], calls: [], heritage: [], routes: [], constructorBindings: [], skippedLanguages: {}, csharpPreprocFallbackFiles: 0, fileCount: 0 };
|
|
1068
1112
|
cumulativeProcessed = 0;
|
|
1069
1113
|
return;
|
|
1070
1114
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { FileContentCache, toCodeElementCsvRow } from './csv-generator.js';
|
|
4
|
-
test('FileContentCache evicts oldest entries when
|
|
5
|
-
const cache = new FileContentCache('/tmp/repo',
|
|
4
|
+
test('FileContentCache evicts oldest entries when max entry count is exceeded', async () => {
|
|
5
|
+
const cache = new FileContentCache('/tmp/repo', 1);
|
|
6
6
|
cache.setForTest('a.cs', '123456');
|
|
7
7
|
cache.setForTest('b.cs', '123456');
|
|
8
8
|
assert.equal(cache.hasForTest('a.cs'), false);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
const DEFINE_CONSTANTS_RE = /<DefineConstants>\s*([\s\S]*?)\s*<\/DefineConstants>/gi;
|
|
4
|
+
const CSHARP_SYMBOL_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
5
|
+
function parseSymbols(rawDefineConstants) {
|
|
6
|
+
const symbols = new Set();
|
|
7
|
+
for (const raw of rawDefineConstants) {
|
|
8
|
+
for (const token of raw.split(';')) {
|
|
9
|
+
const symbol = token.trim();
|
|
10
|
+
if (!symbol)
|
|
11
|
+
continue;
|
|
12
|
+
// Drop MSBuild placeholders such as $(DefineConstants)
|
|
13
|
+
if (symbol.includes('$('))
|
|
14
|
+
continue;
|
|
15
|
+
if (!CSHARP_SYMBOL_RE.test(symbol))
|
|
16
|
+
continue;
|
|
17
|
+
symbols.add(symbol);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return symbols;
|
|
21
|
+
}
|
|
22
|
+
export async function loadCSharpDefineProfileFromCsproj(csprojPath) {
|
|
23
|
+
const sourcePath = path.resolve(csprojPath);
|
|
24
|
+
let content;
|
|
25
|
+
try {
|
|
26
|
+
content = await fs.readFile(sourcePath, 'utf-8');
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
30
|
+
throw new Error(`Failed to read C# csproj: ${sourcePath} (${message})`);
|
|
31
|
+
}
|
|
32
|
+
const rawDefineConstants = [];
|
|
33
|
+
for (const match of content.matchAll(DEFINE_CONSTANTS_RE)) {
|
|
34
|
+
const raw = (match[1] || '').trim();
|
|
35
|
+
if (raw)
|
|
36
|
+
rawDefineConstants.push(raw);
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
symbols: parseSymbols(rawDefineConstants),
|
|
40
|
+
sourcePath,
|
|
41
|
+
rawDefineConstants,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface CSharpPreprocNormalizationDiagnostics {
|
|
2
|
+
directivesSeen: number;
|
|
3
|
+
inactiveLines: number;
|
|
4
|
+
undefinedSymbols: string[];
|
|
5
|
+
expressionErrors: number;
|
|
6
|
+
unmatchedEndif: number;
|
|
7
|
+
unterminatedIfBlocks: number;
|
|
8
|
+
}
|
|
9
|
+
export interface CSharpPreprocNormalizationResult {
|
|
10
|
+
normalizedText: string;
|
|
11
|
+
changed: boolean;
|
|
12
|
+
diagnostics: CSharpPreprocNormalizationDiagnostics;
|
|
13
|
+
}
|
|
14
|
+
export declare function normalizeCSharpPreprocessorBranches(source: string, defines: Set<string>): CSharpPreprocNormalizationResult;
|