gitnexus 1.6.10 → 1.6.11-rc.2
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/group/extractors/http-patterns/kotlin.js +522 -7
- package/dist/core/group/extractors/http-patterns/php.js +279 -11
- package/dist/core/index-freshness.d.ts +2 -2
- package/dist/core/index-freshness.js +13 -0
- package/dist/core/ingestion/parsing-processor.d.ts +2 -0
- package/dist/core/ingestion/parsing-processor.js +5 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +3 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +7 -0
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +4 -0
- package/dist/core/ingestion/pipeline.js +4 -1
- package/dist/core/ingestion/route-extractors/kotlin-const-resolver.d.ts +377 -0
- package/dist/core/ingestion/route-extractors/kotlin-const-resolver.js +1203 -0
- package/dist/core/ingestion/scope-extractor-bridge.js +8 -2
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +2 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +14 -2
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +2 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +11 -1
- package/dist/core/ingestion/scope-resolution/scope-extraction-failures.d.ts +14 -0
- package/dist/core/ingestion/scope-resolution/scope-extraction-failures.js +35 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +6 -0
- package/dist/core/ingestion/workers/parse-worker.js +7 -1
- package/dist/core/ingestion/workers/result-merge.js +4 -1
- package/dist/core/run-analyze.js +6 -0
- package/dist/mcp/local/local-backend.d.ts +2 -0
- package/dist/mcp/local/local-backend.js +29 -0
- package/dist/mcp/tools.js +6 -4
- package/dist/storage/parse-cache.js +6 -6
- package/dist/storage/repo-meta.d.ts +17 -1
- package/dist/storage/repo-meta.js +1 -1
- package/dist/types/pipeline.d.ts +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kotlin binding for the language-agnostic constant resolver (#2391 core).
|
|
3
|
+
*
|
|
4
|
+
* Supplies the two Kotlin-specific pieces — {@link resolveKotlinImport} (import
|
|
5
|
+
* specifier → file, honoring JVM package rules) and
|
|
6
|
+
* {@link extractKotlinModuleConstants} (tree → {@link ModuleConstants}) — plus
|
|
7
|
+
* the folding entry points {@link resolveKotlinConstant} and
|
|
8
|
+
* {@link foldKotlinOperands}, so callers stay language-oblivious.
|
|
9
|
+
*
|
|
10
|
+
* WHAT IS ACTUALLY SHARED WITH THE AGNOSTIC CORE. One value —
|
|
11
|
+
* {@link MAX_FOLD_LENGTH} — and five types. The core's own `resolveConstant` /
|
|
12
|
+
* `resolveOperands` are NOT called: the fold state machine below (cycle guard,
|
|
13
|
+
* success memo, depth caps, operand concatenation — roughly 200 of this file's
|
|
14
|
+
* lines) is a local fork, close enough to `java-const-resolver.ts`'s already
|
|
15
|
+
* forked copy that the two read as the same code with the language name
|
|
16
|
+
* swapped.
|
|
17
|
+
*
|
|
18
|
+
* That fork is a consequence, not an oversight. The core keys its maps by
|
|
19
|
+
* SIMPLE name, and a Kotlin operand can be a QUALIFIED reference at any
|
|
20
|
+
* position (`X = ApiPaths.Y + "/tail"`); handed to the core, `ApiPaths.Y` misses
|
|
21
|
+
* every map and floors the whole chain to null — see {@link computeKotlinFold},
|
|
22
|
+
* which resolves operands through the qualified-aware walk for exactly this
|
|
23
|
+
* reason. The import chase is Kotlin-specific too: a member import is spelled
|
|
24
|
+
* identically to a type import, so {@link resolveImportedName} has to try both
|
|
25
|
+
* readings, and the core exposes no hook for that. Java forked first on the same
|
|
26
|
+
* grounds. Teaching the core qualified names, and retiring both copies against
|
|
27
|
+
* it, is the standing follow-up; until then the honest description of this file
|
|
28
|
+
* is "a second fork", not "a binding over a shared fold".
|
|
29
|
+
*
|
|
30
|
+
* Kotlin shares the JVM package/import model with Java, so this binding mirrors
|
|
31
|
+
* `java-const-resolver.ts` in structure, naming and skip-floor discipline. The
|
|
32
|
+
* four places Kotlin genuinely differs are handled explicitly, not translated:
|
|
33
|
+
*
|
|
34
|
+
* 1. **Where a constant can live.** Java has one carrier (`static final` on a
|
|
35
|
+
* type). Kotlin has three: a top-level `const val`/`val`, a member of an
|
|
36
|
+
* `object`, and a member of a `companion object` — the last is referenced
|
|
37
|
+
* through its ENCLOSING class (`Holder.NAME`), not through `Companion`.
|
|
38
|
+
* 2. **No `String` type gate.** Kotlin infers property types, so
|
|
39
|
+
* `const val ORDERS = "/orders"` carries no type node to check. The
|
|
40
|
+
* initializer decides: anything {@link parseKotlinConstOperands} cannot fold
|
|
41
|
+
* to a string (a number, a call, a template) drops the constant.
|
|
42
|
+
* 3. **File names and directories are free.** `object ApiPaths` may live in
|
|
43
|
+
* `Constants.kt`, and a file's `package` need not match the directory it
|
|
44
|
+
* sits in, so a `<package>/<Name>.kt` PATH lookup is a convention and not a
|
|
45
|
+
* rule. The authority is each file's DECLARED `package`, which
|
|
46
|
+
* {@link extractKotlinModuleConstants} records and {@link resolveKotlinImport}
|
|
47
|
+
* requires an exact match on; the path is only a tie-break among files that
|
|
48
|
+
* already declare the right package.
|
|
49
|
+
* 4. **Member imports are unmarked.** Java spells them `import static a.b.C.F`;
|
|
50
|
+
* Kotlin writes `import a.b.C.F`, which is byte-identical to a type import
|
|
51
|
+
* of a class `F` in package `a.b.C`. Nothing in the syntax says which, so
|
|
52
|
+
* the fold tries both readings (see `resolveImportedName`) instead of
|
|
53
|
+
* guessing from casing.
|
|
54
|
+
* 5. **Any identifier may be backtick-quoted.** `` package com.example.`api` ``
|
|
55
|
+
* and `package com.example.api` are the SAME package to the compiler, and a
|
|
56
|
+
* keyword segment (`` com.example.`fun` ``) can only be spelled the quoted
|
|
57
|
+
* way. The grammar keeps the backticks in the node text, so every identifier is
|
|
58
|
+
* read through {@link unquoteKotlinIdentifier} before it becomes a map key
|
|
59
|
+
* or a lookup name — see that function for what a verbatim comparison cost.
|
|
60
|
+
*
|
|
61
|
+
* THREE PLACES THIS BINDING NO LONGER MIRRORS JAVA, each because the mirrored
|
|
62
|
+
* behavior was wrong rather than merely different, and each open as a Java
|
|
63
|
+
* follow-up rather than fixed here:
|
|
64
|
+
*
|
|
65
|
+
* * `java-const-resolver.ts` flattens nested types into one file-level
|
|
66
|
+
* namespace and argues the collision away — "qualified refs carry the class
|
|
67
|
+
* name, so nesting only matters for same-name fields, which flatten
|
|
68
|
+
* last-wins". The argument does not hold: the collision is one level BELOW
|
|
69
|
+
* the qualification, in the initializer, so a fully qualified `A.ROUTE` whose
|
|
70
|
+
* initializer names a bare sibling `BASE` still resolves through whichever
|
|
71
|
+
* same-named sibling was walked last. {@link extractKotlinModuleConstants}
|
|
72
|
+
* keys by Kotlin's own visibility instead.
|
|
73
|
+
* * Java's import resolution can lean on the `<package>/<Name>.java` layout the
|
|
74
|
+
* language enforces. Kotlin's cannot, and inferring the package from the path
|
|
75
|
+
* lets a path-suffix twin outrank the real declaration — so
|
|
76
|
+
* {@link resolveKotlinImport} reads the declared `package` instead.
|
|
77
|
+
* * Java's fold entry points take a file and a name, because a Java `static
|
|
78
|
+
* final` reachable by simple name is reachable that way from anywhere in the
|
|
79
|
+
* file. A Kotlin COMPANION member is not: it is bound unqualified only inside
|
|
80
|
+
* its enclosing class body. {@link foldKotlinOperands} therefore also takes
|
|
81
|
+
* the enclosing type chain of the reference site, which is what lets the
|
|
82
|
+
* binding answer a bare reference by Kotlin's scoping rather than by "whoever
|
|
83
|
+
* was walked last" — see {@link qualifyKotlinRefInEnclosingTypes}.
|
|
84
|
+
*
|
|
85
|
+
* Constant shapes this binding harvests:
|
|
86
|
+
*
|
|
87
|
+
* const val TOP_LEVEL = "/api/v1" // file top level
|
|
88
|
+
* object ApiPaths { // object member
|
|
89
|
+
* const val BASE = "/api/v1"
|
|
90
|
+
* val ORDERS = BASE + "/orders"
|
|
91
|
+
* }
|
|
92
|
+
* class Holder { companion object { const val H = "/h" } } // → Holder.H
|
|
93
|
+
*
|
|
94
|
+
* Reference shapes at annotation sites this binding resolves:
|
|
95
|
+
* @PostMapping(ApiPaths.ORDERS) // qualified
|
|
96
|
+
* @PostMapping(com.example.app.api.ApiPaths.ORDERS) // FQN-qualified
|
|
97
|
+
* @PostMapping(ORDERS) // single-name import
|
|
98
|
+
* @PostMapping(ApiPaths.BASE + "/orders") // inline concat
|
|
99
|
+
*
|
|
100
|
+
* Which ANNOTATIONS count as routes is a separate question this module has no
|
|
101
|
+
* say in — `spring-shared.ts` owns that map. Folding and annotation recognition
|
|
102
|
+
* compose; neither implies the other.
|
|
103
|
+
*
|
|
104
|
+
* Keying (parity with the Java and Python bindings): the repo map is keyed by
|
|
105
|
+
* unique POSIX file path, and an import that cannot be pinned to exactly one
|
|
106
|
+
* file returns null (skip floor), never a wrong path. A missing route is a
|
|
107
|
+
* missing fact; a wrongly folded one is a false edge in the graph. "Exactly one
|
|
108
|
+
* file" is decided from the DECLARED package, not from the path: a path is a
|
|
109
|
+
* repository-layout accident that any decoy directory can imitate, whereas the
|
|
110
|
+
* `package` header is the declaration the compiler itself resolves against.
|
|
111
|
+
*
|
|
112
|
+
* POSIX keys are a PRECONDITION this module cannot check cheaply, so it is
|
|
113
|
+
* enforced at the one boundary that produces them: `http-patterns/kotlin.ts`
|
|
114
|
+
* normalizes separators on both the write side (the `prepareRepo` map keys) and
|
|
115
|
+
* the read side (`scan`'s `fileRel`). It has to, because the orchestrator's file
|
|
116
|
+
* list comes from glob v13, which has no `posix: true` and joins with the
|
|
117
|
+
* platform separator — so on Windows the keys arrive backslashed and every
|
|
118
|
+
* `<pkg>/<Name>.kt` test in {@link resolveKotlinImport} would miss, silently
|
|
119
|
+
* disabling cross-file folding on that platform alone. Normalizing INSIDE this
|
|
120
|
+
* module instead cannot work: the resolver returns the key it matched, and a
|
|
121
|
+
* normalized return value would then miss in a map that was never normalized.
|
|
122
|
+
*
|
|
123
|
+
* WHERE THIS IS WIRED. Java reaches its binding from BOTH layers: the group
|
|
124
|
+
* extractor (`group/extractors/http-patterns/java.ts`) and the ingestion
|
|
125
|
+
* provider (`languages/java.ts`, via `extractModuleConstants` +
|
|
126
|
+
* `foldRoutePathOperands`). Kotlin is wired into the GROUP layer only, because
|
|
127
|
+
* the ingestion fold in `pipeline-phases/parse-impl.ts` runs exclusively over
|
|
128
|
+
* `decoratorRoutes` — and `languages/kotlin.ts` declares no
|
|
129
|
+
* `extractDecoratorRoutes`, since the ingestion Spring extractor (`spring.ts`)
|
|
130
|
+
* is bound to `tree-sitter-java` and its node types. Declaring the constant
|
|
131
|
+
* hooks on the Kotlin provider today would harvest a map on every Kotlin file
|
|
132
|
+
* that nothing consumes. An ingestion-side Kotlin route extractor is the
|
|
133
|
+
* prerequisite; when it lands, this binding is what its provider hooks should
|
|
134
|
+
* point at, and no change here is needed.
|
|
135
|
+
*/
|
|
136
|
+
import type Parser from 'tree-sitter';
|
|
137
|
+
import { type ModuleConstants, type Operand, type RepoConstants } from './constant-resolver.js';
|
|
138
|
+
export type { ImportBinding, ModuleConstants, Operand, RepoConstants, } from './constant-resolver.js';
|
|
139
|
+
/**
|
|
140
|
+
* What {@link extractKotlinModuleConstants} returns: the agnostic
|
|
141
|
+
* {@link ModuleConstants} plus the one piece of per-file metadata JVM import
|
|
142
|
+
* resolution cannot be honest without — the file's declared `package`.
|
|
143
|
+
*
|
|
144
|
+
* Deliberately a KOTLIN-LOCAL widening rather than a field on the shared type.
|
|
145
|
+
* `ModuleConstants` is consumed by the Java, JS and Python bindings too, and
|
|
146
|
+
* none of them needs this: Python resolves imports from the module path, and
|
|
147
|
+
* Java's `package` is already pinned by the `<package>/<Name>.java` rule the
|
|
148
|
+
* language enforces. Adding a required field there would force three unrelated
|
|
149
|
+
* bindings to fill it in; adding an optional one would put a Kotlin-shaped hole
|
|
150
|
+
* in a type whose whole point is language neutrality.
|
|
151
|
+
*
|
|
152
|
+
* Read the metadata through {@link declaredPackageOf} and
|
|
153
|
+
* {@link unfoldableDeclarationsOf}, never by field access: a
|
|
154
|
+
* {@link RepoConstants} is typed over the agnostic shape, so an entry that some
|
|
155
|
+
* other producer put there carries no package and must be REJECTED as a
|
|
156
|
+
* candidate rather than silently treated as the default package. Missing
|
|
157
|
+
* unfoldable-declaration metadata instead means "none known", preserving the
|
|
158
|
+
* agnostic entry's existing behavior.
|
|
159
|
+
*/
|
|
160
|
+
export interface KotlinModuleConstants extends ModuleConstants {
|
|
161
|
+
/** The file's declared `package`, or `''` for the default package. */
|
|
162
|
+
readonly packageName: string;
|
|
163
|
+
/** Declaration keys whose initializer cannot be folded. */
|
|
164
|
+
readonly unfoldableDeclarations: ReadonlySet<string>;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Kotlin declaration keys known to exist but not fold, or an empty set when
|
|
168
|
+
* `mc` came from another language binding.
|
|
169
|
+
*/
|
|
170
|
+
export declare function unfoldableDeclarationsOf(mc: ModuleConstants | undefined): ReadonlySet<string>;
|
|
171
|
+
/**
|
|
172
|
+
* The name a backtick-quoted Kotlin identifier denotes: `` `api` `` → `api`.
|
|
173
|
+
*
|
|
174
|
+
* Quotes are spelling, not part of the name. tree-sitter-kotlin keeps them in
|
|
175
|
+
* node text, so every identifier that becomes a map key or lookup is read
|
|
176
|
+
* through here. Applied per dot-separated segment — a quoted identifier cannot
|
|
177
|
+
* contain `.`. Both the declaration side ({@link declaredPackage}) and the
|
|
178
|
+
* import side ({@link resolveKotlinImport}) are normalized, because either may
|
|
179
|
+
* carry the quotes while the other spells the same name plainly.
|
|
180
|
+
*/
|
|
181
|
+
export declare function unquoteKotlinIdentifier(text: string): string;
|
|
182
|
+
export declare function isKotlinConstantFile(source: string): boolean;
|
|
183
|
+
/** Files and declaration ownership for one exact Kotlin package. */
|
|
184
|
+
export interface KotlinPackageConstants {
|
|
185
|
+
readonly files: readonly string[];
|
|
186
|
+
/** Unique declaring file, or null when the package declares the name twice. */
|
|
187
|
+
readonly declarers: ReadonlyMap<string, string | null>;
|
|
188
|
+
}
|
|
189
|
+
/** One exact package-qualified declaration and its in-file lookup key. */
|
|
190
|
+
interface KotlinImportTarget {
|
|
191
|
+
readonly fileKey: string;
|
|
192
|
+
readonly localName: string;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Repo-wide projections reused by every fold in one extraction run.
|
|
196
|
+
*
|
|
197
|
+
* `repo` includes importing files overlaid by `scan`; `constantKeys` and
|
|
198
|
+
* `byPackage` include only files that define a foldable or explicitly
|
|
199
|
+
* unfoldable declaration, preserving import ambiguity semantics.
|
|
200
|
+
*/
|
|
201
|
+
export interface KotlinConstantIndex {
|
|
202
|
+
readonly repo: RepoConstants;
|
|
203
|
+
readonly constantKeys: ReadonlySet<string>;
|
|
204
|
+
readonly byPackage: ReadonlyMap<string, KotlinPackageConstants>;
|
|
205
|
+
/** Exact FQN → unique file/local key, or null when the FQN is duplicated. */
|
|
206
|
+
readonly byFqn: ReadonlyMap<string, KotlinImportTarget | null>;
|
|
207
|
+
}
|
|
208
|
+
/** Build the immutable import projections once for a repo constant map. */
|
|
209
|
+
export declare function buildKotlinConstantIndex(repo: RepoConstants): KotlinConstantIndex;
|
|
210
|
+
/**
|
|
211
|
+
* Add one scan-time file without rebuilding the base index when it only imports
|
|
212
|
+
* constants. A newly discovered declaration is rare and rebuilds once for that
|
|
213
|
+
* file's scan, never once per route.
|
|
214
|
+
*/
|
|
215
|
+
export declare function overlayKotlinConstantIndex(index: KotlinConstantIndex, fileKey: string, mc: ModuleConstants): KotlinConstantIndex;
|
|
216
|
+
/**
|
|
217
|
+
* Map a fully-qualified import specifier to the unique file key it refers to, or
|
|
218
|
+
* null when it cannot be pinned to exactly one file.
|
|
219
|
+
*
|
|
220
|
+
* A specifier is split at its last dot into the package it names and the
|
|
221
|
+
* declaration inside it (`com.example.app.api` + `ApiPaths`). Resolution then
|
|
222
|
+
* runs in three steps, all of them "unique or nothing":
|
|
223
|
+
*
|
|
224
|
+
* 0. **Declared package** — only files whose `package` header is EXACTLY the
|
|
225
|
+
* sought package can carry the declaration (compared after
|
|
226
|
+
* {@link unquoteKotlinIdentifier}, since backtick quoting is spelling and
|
|
227
|
+
* not identity). This is the authority, and it
|
|
228
|
+
* is checked first. Kotlin does not require a file's directory to match its
|
|
229
|
+
* package, so the reverse test — "does this path end with the package?" —
|
|
230
|
+
* answers a different question, one any decoy directory can satisfy: a file
|
|
231
|
+
* at `src/x/com/example/api/ApiPaths.kt` declaring `package x.com.example.api`
|
|
232
|
+
* is not `com.example.api.ApiPaths` and must never be folded as it, and a
|
|
233
|
+
* path-suffix test also lets a root-level `package data` be impersonated by
|
|
234
|
+
* `…/com/example/data/`. An entry with no recorded package is rejected, not
|
|
235
|
+
* assumed to be the default package.
|
|
236
|
+
* 1. **Declared name** — when exactly one file declares the sought name, use
|
|
237
|
+
* it. When two do, the FQN itself is duplicated in the repository and names
|
|
238
|
+
* no single declaration, so return null. This is the general form of the
|
|
239
|
+
* same-FQN check step 2 could only make for files that happen to follow the
|
|
240
|
+
* file-name convention, and it is what stops a `src/test/…` copy of a
|
|
241
|
+
* production constant from being folded into a production route.
|
|
242
|
+
* 2. **File named after the declaration** — when declaration metadata found no
|
|
243
|
+
* owner, try the package-matching file ending
|
|
244
|
+
* `com/example/app/api/ApiPaths.kt`. Kotlin does not require this (`object
|
|
245
|
+
* ApiPaths` may live in `Constants.kt`), so it is only a fallback candidate;
|
|
246
|
+
* the subsequent map lookup must still prove that it carries the value.
|
|
247
|
+
* 3. **Sole file in the package** — when declaration metadata cannot identify
|
|
248
|
+
* the name, use the unique package-matching candidate. The set passed in
|
|
249
|
+
* contains files with foldable or explicitly unfoldable declarations, so
|
|
250
|
+
* unrelated files cannot create ambiguity once step 1 identifies a unique
|
|
251
|
+
* declarer. With 2+ unidentified candidates it returns null.
|
|
252
|
+
*
|
|
253
|
+
* Steps 2 and 3 can still hand back a file that does not declare the wanted name
|
|
254
|
+
* (its package is right and it is the only candidate, but the name lives
|
|
255
|
+
* elsewhere or nowhere). That remains safe by construction: the fold looks the
|
|
256
|
+
* name up in that file's map, misses, and returns null.
|
|
257
|
+
*
|
|
258
|
+
* A "nearest shared directory" tie-break is deliberately NOT applied when a step
|
|
259
|
+
* has several candidates, for the reason the Java binding records: the JVM
|
|
260
|
+
* resolves duplicate FQNs by classpath order, not directory proximity, so a test
|
|
261
|
+
* fixture copy sitting closer in the tree can outrank the real dependency and
|
|
262
|
+
* yield a silently wrong literal. In a resolver whose whole contract is
|
|
263
|
+
* skip-or-correct, a plausible guess is the one answer that cannot be allowed.
|
|
264
|
+
*
|
|
265
|
+
* This can no longer be typed as the agnostic {@link ModuleConstants} consumer's
|
|
266
|
+
* `ImportResolver`, whose signature carries only file KEYS: deciding a candidate
|
|
267
|
+
* on its declared package needs the map those keys index. Nothing is lost — the
|
|
268
|
+
* core's own fold is not used here either (see the module header), and the
|
|
269
|
+
* alternative is a resolver that must guess from a path.
|
|
270
|
+
*/
|
|
271
|
+
export declare function resolveKotlinImport(_importingFileKey: string, rawModuleSpec: string, candidateKeys: ReadonlySet<string>, repo: RepoConstants): string | null;
|
|
272
|
+
/** Indexed equivalent of {@link resolveKotlinImport}, with identical fallbacks. */
|
|
273
|
+
export declare function resolveKotlinImportWithIndex(rawModuleSpec: string, index: KotlinConstantIndex): string | null;
|
|
274
|
+
/**
|
|
275
|
+
* Parse a Kotlin constant initializer (or an inline annotation argument) into an
|
|
276
|
+
* operand list, or null when it is not a foldable string expression. Handles a
|
|
277
|
+
* bare string literal, a bare identifier (`X = Y`), a qualified reference
|
|
278
|
+
* (`X = ApiPaths.Y` — recorded as ONE ref named `ApiPaths.Y`), and
|
|
279
|
+
* left-associative `+` chains of the three. Everything else — numbers, calls,
|
|
280
|
+
* `when`/`if` expressions, templates, `buildString` — returns null, which makes
|
|
281
|
+
* the constant unresolvable (→ skip floor), never a wrong value.
|
|
282
|
+
*
|
|
283
|
+
* A chain nests: tree-sitter-kotlin parses `A + B + C` as
|
|
284
|
+
* `additive_expression(additive_expression(A, B), C)`, so every node here has
|
|
285
|
+
* exactly two operands and arbitrary-length chains fold by recursion. The same
|
|
286
|
+
* node type also carries `-`, which is not a string operation, so a `+` token
|
|
287
|
+
* must be present.
|
|
288
|
+
*
|
|
289
|
+
* A PARENTHESIZED operand (`(A + B) + "/c"`) is deliberately NOT unwrapped,
|
|
290
|
+
* matching `parseJavaConstOperands`, which has no parenthesis arm either. The
|
|
291
|
+
* shape is vanishingly rare in a route annotation and the cost of omitting it is
|
|
292
|
+
* a skipped route, not a wrong one; adding it to both bindings at once is the
|
|
293
|
+
* only way to keep them in parity, so it is left to a follow-up.
|
|
294
|
+
*/
|
|
295
|
+
export declare function parseKotlinConstOperands(node: Parser.SyntaxNode | null | undefined, depth?: number): Operand[] | null;
|
|
296
|
+
/**
|
|
297
|
+
* Extract the declared package, file-level string constants and import bindings
|
|
298
|
+
* of one parsed Kotlin file into the {@link KotlinModuleConstants} shape the
|
|
299
|
+
* resolver consumes.
|
|
300
|
+
*
|
|
301
|
+
* Constants come from the three carriers Kotlin allows a caller to reach without
|
|
302
|
+
* an instance: file top level, `object` members, and `companion object` members.
|
|
303
|
+
* A `val` in a plain class or interface body is per-instance or abstract and is
|
|
304
|
+
* NOT collected — the Kotlin analogue of Java's `static final` requirement. `var`
|
|
305
|
+
* is rejected outright.
|
|
306
|
+
*
|
|
307
|
+
* KEYS FOLLOW KOTLIN'S OWN VISIBILITY, not a flattened namespace. Every constant
|
|
308
|
+
* is recorded under `<DeclaringType>.<NAME>`, the spelling a qualified reference
|
|
309
|
+
* uses, with a companion member keyed under its ENCLOSING CLASS (`Holder.NAME`)
|
|
310
|
+
* because that is how Kotlin source refers to it — `Companion` never appears in
|
|
311
|
+
* a reference. The SIMPLE name is recorded only for a TOP-LEVEL `val`, the one
|
|
312
|
+
* carrier whose bare binding really does span the file. A member of a named
|
|
313
|
+
* `object` gets no bare key, because `BASE` alone does not name `A.BASE` from
|
|
314
|
+
* anywhere outside `object A`'s own body. Writing one anyway (as this binding
|
|
315
|
+
* and the Java one both used to) fabricates a binding the language does not
|
|
316
|
+
* have, and a fabricated key outranks the genuine `import com.example.api.Paths.ORDERS`
|
|
317
|
+
* that {@link computeKotlinFold} consults only after literals and expressions.
|
|
318
|
+
*
|
|
319
|
+
* A COMPANION member gets no bare key either: it is bound unqualified inside
|
|
320
|
+
* its enclosing class body and nowhere else. {@link qualifyKotlinRefInEnclosingTypes}
|
|
321
|
+
* rewrites a bare name to `<EnclosingType>.<NAME>` when an enclosing type
|
|
322
|
+
* declares it, so the companion wins inside its own class and loses everywhere
|
|
323
|
+
* else.
|
|
324
|
+
*
|
|
325
|
+
* An initializer that names a SIBLING is resolved the same way, against its own
|
|
326
|
+
* scope chain, innermost first, before the file level: inside
|
|
327
|
+
* `object A { const val BASE = "/right"; const val ROUTE = BASE + "/m" }` the
|
|
328
|
+
* operand `BASE` is rewritten to `A.BASE`. Collecting every declaration before
|
|
329
|
+
* recording any keeps that independent of declaration order.
|
|
330
|
+
*
|
|
331
|
+
* A TOP-LEVEL initializer has an EMPTY scope chain, so its bare operands stay
|
|
332
|
+
* bare and resolve at file level — they must not pick up a companion key.
|
|
333
|
+
*
|
|
334
|
+
* A non-foldable rebind (`X = compute()`) DROPS X to unresolvable rather than
|
|
335
|
+
* leaving a stale literal — and drops a same-named import with it whenever the
|
|
336
|
+
* declaration shadows that import ANYWHERE (top level, or a companion inside its
|
|
337
|
+
* class). The import map has no scopes, so a companion's shadow is applied
|
|
338
|
+
* file-wide: the conservative direction, costing a route rather than publishing
|
|
339
|
+
* the imported value at a reference the compiler binds to the unfoldable member.
|
|
340
|
+
* An `object` member shadows nothing and must leave the import alone.
|
|
341
|
+
*/
|
|
342
|
+
export declare function extractKotlinModuleConstants(tree: Parser.Tree): KotlinModuleConstants;
|
|
343
|
+
/**
|
|
344
|
+
* Resolve a single Kotlin constant referenced in `fileKey` to its literal string
|
|
345
|
+
* value, folding `+` concatenation and following import chains via
|
|
346
|
+
* {@link resolveKotlinImport}, or null when it cannot be fully folded.
|
|
347
|
+
*
|
|
348
|
+
* `name` may be simple (`ORDERS`, resolved via a single-name import or a
|
|
349
|
+
* same-file constant) or qualified (`ApiPaths.ORDERS`, resolved via the type
|
|
350
|
+
* import plus the target file's qualified alias).
|
|
351
|
+
*/
|
|
352
|
+
export declare function resolveKotlinConstant(fileKey: string, name: string, repo: RepoConstants, depth?: number, index?: KotlinConstantIndex): string | null;
|
|
353
|
+
/**
|
|
354
|
+
* Fold an inline operand list (e.g. `ApiPaths.BASE + "/orders"`) against
|
|
355
|
+
* `fileKey`, or null when any piece is unresolvable (skip floor).
|
|
356
|
+
*
|
|
357
|
+
* `enclosingTypes` is the chain of type declarations the REFERENCE sits inside
|
|
358
|
+
* (innermost first), and it is applied to the entry operands only — everything
|
|
359
|
+
* deeper is either already qualified by
|
|
360
|
+
* {@link extractKotlinModuleConstants} against its own declaring scope, or lives
|
|
361
|
+
* in another file where this chain means nothing. Passing it empty answers
|
|
362
|
+
* "what does this name mean at file level", which is the right question for a
|
|
363
|
+
* reference outside any type and the only one a caller without position
|
|
364
|
+
* information can honestly ask.
|
|
365
|
+
*
|
|
366
|
+
* An empty result is a SUCCESS, not a skip. `const val ROOT = ""` folds to `""`,
|
|
367
|
+
* which `joinPath` then resolves against the class-level prefix exactly as it
|
|
368
|
+
* resolves the literal `@GetMapping("")` — both mean "the prefix itself", the
|
|
369
|
+
* Spring idiom for a collection root. Collapsing it into `null` would make a
|
|
370
|
+
* resolved-empty path indistinguishable from an unresolvable one — the skip
|
|
371
|
+
* floor is reserved for "could not fold", and nothing else in the resolver
|
|
372
|
+
* conflates the two: {@link resolveKotlinConstant} returns `''` for an empty
|
|
373
|
+
* constant, and `resolveOperands` in the shared core returns its fold
|
|
374
|
+
* unfiltered. Matches `foldJavaOperands`, so the two JVM bindings do not
|
|
375
|
+
* diverge on the same input.
|
|
376
|
+
*/
|
|
377
|
+
export declare function foldKotlinOperands(fileKey: string, operands: readonly Operand[], repo: RepoConstants, enclosingTypes?: readonly string[], index?: KotlinConstantIndex): string | null;
|