ucn 5.5.1 → 5.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/ucn/SKILL.md +35 -9
- package/.claude/skills/ucn/references/commands.md +40 -18
- package/.claude/skills/ucn/references/resolution-rules.md +126 -20
- package/.claude/skills/ucn/references/trust-contract.md +5 -3
- package/README.md +32 -21
- package/cli/index.js +2 -1
- package/core/accessors.js +341 -54
- package/core/account.js +74 -20
- package/core/analysis.js +257 -125
- package/core/ast-analysis.js +12 -3
- package/core/bridge.js +15 -65
- package/core/brief.js +8 -4
- package/core/build-worker.js +2 -1
- package/core/cache.js +137 -6
- package/core/call-name-cache.js +134 -0
- package/core/callable-scope.js +21 -0
- package/core/callers.js +4726 -940
- package/core/check.js +22 -5
- package/core/class-identity.js +399 -12
- package/core/client-receivers.js +189 -0
- package/core/constructor-calls.js +831 -0
- package/core/constructor-hierarchy.js +147 -0
- package/core/constructor-protocol.js +196 -0
- package/core/contract-membership.js +346 -140
- package/core/cpp-conversion-constraints.js +123 -0
- package/core/cpp-conversion-syntax.js +216 -0
- package/core/cpp-conversions.js +414 -0
- package/core/cpp-scope.js +245 -44
- package/core/deadcode.js +61 -16
- package/core/decorator-binding.js +137 -3
- package/core/enum-reference-syntax.js +134 -0
- package/core/execute.js +17 -1
- package/core/external-macros.js +42 -10
- package/core/go-packages.js +171 -0
- package/core/graph-build.js +2 -0
- package/core/graph.js +75 -59
- package/core/ignore-rules.js +2 -1
- package/core/imports.js +145 -19
- package/core/index-ir.js +41 -4
- package/core/inprocess-clients.js +41 -15
- package/core/ir.js +27 -6
- package/core/macro-expansion.js +38 -2
- package/core/out-of-class-members.js +182 -0
- package/core/output/analysis-ext.js +4 -2
- package/core/output/analysis.js +89 -20
- package/core/output/graph.js +12 -3
- package/core/output/lines.js +4 -3
- package/core/output/public.js +2 -0
- package/core/output/reporting.js +12 -1
- package/core/output/shared.js +2 -0
- package/core/output/tracing.js +28 -22
- package/core/output-budget.js +2 -2
- package/core/parallel-build.js +52 -1
- package/core/project.js +217 -112
- package/core/provenance-constructor-base.js +161 -0
- package/core/provenance-facts.js +257 -27
- package/core/provenance-overload.js +458 -54
- package/core/provenance-type.js +487 -0
- package/core/provenance.js +234 -15
- package/core/public-command.js +17 -2
- package/core/query-bodies.js +159 -0
- package/core/reflection.js +270 -2
- package/core/rename-token-syntax.js +111 -0
- package/core/reporting.js +13 -7
- package/core/request-helpers.js +63 -7
- package/core/rust-macro-expansion.js +151 -19
- package/core/rust-modules.js +1 -1
- package/core/rust-names.js +607 -0
- package/core/search.js +70 -17
- package/core/shared.js +98 -1
- package/core/source-memo.js +201 -0
- package/core/text-usage-syntax.js +38 -0
- package/core/tracing.js +7 -1
- package/core/type-denotation.js +124 -37
- package/core/type-references.js +615 -246
- package/core/value-references.js +2777 -0
- package/core/verify.js +1097 -281
- package/languages/adapter.js +28 -4
- package/languages/c-family.js +1118 -164
- package/languages/constructor-bases.js +105 -0
- package/languages/cpp-platform.js +26 -0
- package/languages/cpp-site-syntax.js +83 -0
- package/languages/csharp-scope.js +439 -0
- package/languages/csharp.js +428 -68
- package/languages/go.js +385 -207
- package/languages/html.js +4 -2
- package/languages/index.js +397 -2
- package/languages/java-platform.js +226 -0
- package/languages/java-unicode.js +269 -0
- package/languages/java.js +461 -117
- package/languages/javascript.js +418 -66
- package/languages/lexical-scope.js +190 -23
- package/languages/python.js +404 -54
- package/languages/rust.js +510 -82
- package/languages/utils.js +67 -8
- package/mcp/server.js +8 -2
- package/package.json +3 -3
package/languages/javascript.js
CHANGED
|
@@ -131,6 +131,9 @@ const FUNCTION_NODE_TYPES = new Set([
|
|
|
131
131
|
'method_definition', 'generator_function', 'generator_function_declaration',
|
|
132
132
|
]);
|
|
133
133
|
|
|
134
|
+
const JS_VALUE_WRAPPERS = new Set(['parenthesized_expression', 'as_expression', 'non_null_expression',
|
|
135
|
+
'satisfies_expression', 'type_assertion']);
|
|
136
|
+
|
|
134
137
|
function unwrapValueExpression(node) {
|
|
135
138
|
while (node && (node.type === 'parenthesized_expression' || node.type === 'as_expression' ||
|
|
136
139
|
node.type === 'non_null_expression' || node.type === 'satisfies_expression' ||
|
|
@@ -142,26 +145,26 @@ function unwrapValueExpression(node) {
|
|
|
142
145
|
}
|
|
143
146
|
|
|
144
147
|
/** Names a binding pattern declares (identifiers, shorthand, rest, defaults' targets). */
|
|
145
|
-
function collectPatternNames(pattern, out) {
|
|
148
|
+
function collectPatternNames(pattern, out, withNodes = false) {
|
|
146
149
|
if (!pattern) return;
|
|
147
150
|
if (pattern.type === 'required_parameter' || pattern.type === 'optional_parameter') {
|
|
148
|
-
collectPatternNames(pattern.childForFieldName('pattern') || pattern.childForFieldName('name'), out);
|
|
151
|
+
collectPatternNames(pattern.childForFieldName('pattern') || pattern.childForFieldName('name'), out, withNodes);
|
|
149
152
|
return;
|
|
150
153
|
}
|
|
151
154
|
if (pattern.type === 'identifier' || pattern.type === 'shorthand_property_identifier_pattern') {
|
|
152
|
-
out.push(pattern.text);
|
|
155
|
+
out.push(withNodes ? pattern : pattern.text);
|
|
153
156
|
return;
|
|
154
157
|
}
|
|
155
158
|
if (pattern.type === 'pair_pattern' || pattern.type === 'pair') {
|
|
156
|
-
collectPatternNames(pattern.childForFieldName('value'), out);
|
|
159
|
+
collectPatternNames(pattern.childForFieldName('value'), out, withNodes);
|
|
157
160
|
return;
|
|
158
161
|
}
|
|
159
162
|
if (pattern.type === 'assignment_pattern') {
|
|
160
|
-
collectPatternNames(pattern.childForFieldName('left') || pattern.childForFieldName('pattern'), out);
|
|
163
|
+
collectPatternNames(pattern.childForFieldName('left') || pattern.childForFieldName('pattern'), out, withNodes);
|
|
161
164
|
return;
|
|
162
165
|
}
|
|
163
166
|
if (pattern.type === 'type_annotation') return;
|
|
164
|
-
for (let i = 0; i < pattern.namedChildCount; i++) collectPatternNames(pattern.namedChild(i), out);
|
|
167
|
+
for (let i = 0; i < pattern.namedChildCount; i++) collectPatternNames(pattern.namedChild(i), out, withNodes);
|
|
165
168
|
}
|
|
166
169
|
|
|
167
170
|
// Module-level alias: findCallsInCode declares its own one-argument
|
|
@@ -449,6 +452,47 @@ function objectPropertyName(nameNode) {
|
|
|
449
452
|
return null;
|
|
450
453
|
}
|
|
451
454
|
|
|
455
|
+
// A non-computed method's key has the value of its identifier or string
|
|
456
|
+
// literal. Class constructor identity uses that value, including escapes.
|
|
457
|
+
function classMethodName(node) {
|
|
458
|
+
const raw = objectPropertyName(node);
|
|
459
|
+
if (raw == null || !raw.includes('\\')) return raw;
|
|
460
|
+
const string = node.type === 'string';
|
|
461
|
+
const escapes = { b: '\b', f: '\f', n: '\n', r: '\r', t: '\t', v: '\v', '0': '\0' };
|
|
462
|
+
let value = '';
|
|
463
|
+
for (let at = 0; at < raw.length; at++) {
|
|
464
|
+
if (raw[at] !== '\\') { value += raw[at]; continue; }
|
|
465
|
+
const escape = raw[++at];
|
|
466
|
+
if (escape === undefined) return null;
|
|
467
|
+
if (escape === 'u' || string && escape === 'x') {
|
|
468
|
+
const brace = escape === 'u' && raw[at + 1] === '{';
|
|
469
|
+
const start = at + (brace ? 2 : 1);
|
|
470
|
+
const end = brace ? raw.indexOf('}', start) : start + (escape === 'u' ? 4 : 2);
|
|
471
|
+
if (end <= start || end > raw.length) return null;
|
|
472
|
+
let code = 0;
|
|
473
|
+
for (let digit = start; digit < end; digit++) {
|
|
474
|
+
const point = raw.charCodeAt(digit);
|
|
475
|
+
const hex = point >= 48 && point <= 57 ? point - 48
|
|
476
|
+
: point >= 65 && point <= 70 ? point - 55
|
|
477
|
+
: point >= 97 && point <= 102 ? point - 87 : -1;
|
|
478
|
+
if (hex < 0) return null;
|
|
479
|
+
code = code * 16 + hex;
|
|
480
|
+
if (code > 0x10ffff) return null;
|
|
481
|
+
}
|
|
482
|
+
value += String.fromCodePoint(code);
|
|
483
|
+
at = brace ? end : end - 1;
|
|
484
|
+
} else if (!string) return null;
|
|
485
|
+
else if (escape === '\r' || escape === '\n' || escape === '\u2028' || escape === '\u2029') {
|
|
486
|
+
if (escape === '\r' && raw[at + 1] === '\n') at++;
|
|
487
|
+
} else {
|
|
488
|
+
if (escape >= '0' && escape <= '9' &&
|
|
489
|
+
(escape !== '0' || raw[at + 1] >= '0' && raw[at + 1] <= '9')) return null;
|
|
490
|
+
value += Object.hasOwn(escapes, escape) ? escapes[escape] : escape;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return value;
|
|
494
|
+
}
|
|
495
|
+
|
|
452
496
|
/**
|
|
453
497
|
* Index function-valued object properties. Module-scope objects are dynamic
|
|
454
498
|
* dispatch surfaces (`HANDLERS[command](...)`); without symbols for their
|
|
@@ -486,7 +530,7 @@ function appendObjectFunctionMembers(objectNode, functions, lines, extraFields =
|
|
|
486
530
|
}
|
|
487
531
|
|
|
488
532
|
const paramsNode = fnNode.childForFieldName('parameters');
|
|
489
|
-
const { startLine, endLine, indent } = nodeToLocation(prop, lines);
|
|
533
|
+
const { startLine, endLine, indent, sourceSpan } = nodeToLocation(prop, lines);
|
|
490
534
|
const returnType = extractReturnType(fnNode);
|
|
491
535
|
const generics = extractGenerics(fnNode);
|
|
492
536
|
const docstring = extractJSDocstring(lines, startLine);
|
|
@@ -499,6 +543,7 @@ function appendObjectFunctionMembers(objectNode, functions, lines, extraFields =
|
|
|
499
543
|
paramsStructured,
|
|
500
544
|
startLine,
|
|
501
545
|
endLine,
|
|
546
|
+
sourceSpan,
|
|
502
547
|
indent,
|
|
503
548
|
isArrow: fnNode.type === 'arrow_function',
|
|
504
549
|
isGenerator: isGenerator(fnNode),
|
|
@@ -852,7 +897,7 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
852
897
|
const paramsNode = node.childForFieldName('parameters');
|
|
853
898
|
|
|
854
899
|
if (nameNode) {
|
|
855
|
-
const { startLine, endLine, indent } = nodeToLocation(node, lines);
|
|
900
|
+
const { startLine, endLine, indent, sourceSpan } = nodeToLocation(node, lines);
|
|
856
901
|
const returnType = extractReturnType(node);
|
|
857
902
|
const returnedConcreteType = extractReturnedConcreteType(node);
|
|
858
903
|
const generics = extractGenerics(node);
|
|
@@ -874,6 +919,7 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
874
919
|
paramsStructured,
|
|
875
920
|
startLine,
|
|
876
921
|
endLine,
|
|
922
|
+
sourceSpan,
|
|
877
923
|
indent,
|
|
878
924
|
isArrow: false,
|
|
879
925
|
isGenerator: isGen,
|
|
@@ -905,7 +951,7 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
905
951
|
if (!isBoundValue && nameNode && !processedRanges.has(rangeKey)) {
|
|
906
952
|
processedRanges.add(rangeKey);
|
|
907
953
|
const paramsNode = node.childForFieldName('parameters');
|
|
908
|
-
const { startLine, endLine, indent } = nodeToLocation(node, lines);
|
|
954
|
+
const { startLine, endLine, indent, sourceSpan } = nodeToLocation(node, lines);
|
|
909
955
|
const returnType = extractReturnType(node);
|
|
910
956
|
const generics = extractGenerics(node);
|
|
911
957
|
const paramsStructured = parseStructuredParams(paramsNode, 'javascript');
|
|
@@ -917,6 +963,7 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
917
963
|
paramsStructured,
|
|
918
964
|
startLine,
|
|
919
965
|
endLine,
|
|
966
|
+
sourceSpan,
|
|
920
967
|
indent,
|
|
921
968
|
isArrow: false,
|
|
922
969
|
isGenerator: isGenerator(node),
|
|
@@ -945,7 +992,7 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
945
992
|
const paramsNode = node.childForFieldName('parameters');
|
|
946
993
|
|
|
947
994
|
if (nameNode) {
|
|
948
|
-
const { startLine, endLine, indent } = nodeToLocation(node, lines);
|
|
995
|
+
const { startLine, endLine, indent, sourceSpan } = nodeToLocation(node, lines);
|
|
949
996
|
const returnType = extractReturnType(node);
|
|
950
997
|
const generics = extractGenerics(node);
|
|
951
998
|
const docstring = extractJSDocstring(lines, startLine);
|
|
@@ -958,6 +1005,7 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
958
1005
|
paramsStructured,
|
|
959
1006
|
startLine,
|
|
960
1007
|
endLine,
|
|
1008
|
+
sourceSpan,
|
|
961
1009
|
indent,
|
|
962
1010
|
isArrow: false,
|
|
963
1011
|
isGenerator: false,
|
|
@@ -974,12 +1022,16 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
974
1022
|
// Variable declarations with arrow functions or function expressions
|
|
975
1023
|
if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {
|
|
976
1024
|
if (processedRanges.has(rangeKey)) return false;
|
|
1025
|
+
const declarators = node.namedChildren.filter(child => child.type === 'variable_declarator');
|
|
977
1026
|
|
|
978
1027
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
979
1028
|
const declarator = node.namedChild(i);
|
|
980
1029
|
if (declarator.type === 'variable_declarator') {
|
|
981
1030
|
const nameNode = declarator.childForFieldName('name');
|
|
982
|
-
|
|
1031
|
+
// A function wrapped in parentheses, a cast or a satisfies
|
|
1032
|
+
// clause (`((next) => ..) as Middleware`, fix #434) is the
|
|
1033
|
+
// value the declaration binds.
|
|
1034
|
+
const valueNode = unwrapValueExpression(declarator.childForFieldName('value'));
|
|
983
1035
|
|
|
984
1036
|
if (nameNode && valueNode) {
|
|
985
1037
|
const isArrow = valueNode.type === 'arrow_function';
|
|
@@ -990,7 +1042,10 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
990
1042
|
processedRanges.add(rangeKey);
|
|
991
1043
|
const paramsNode = valueNode.childForFieldName('parameters') ||
|
|
992
1044
|
valueNode.childForFieldName('parameter');
|
|
993
|
-
|
|
1045
|
+
// A comma-separated declaration binds distinct functions.
|
|
1046
|
+
// Its other declarators are outside this callable's extent.
|
|
1047
|
+
const { startLine, endLine, indent } = nodeToLocation(
|
|
1048
|
+
declarators.length === 1 ? node : declarator, lines);
|
|
994
1049
|
const returnType = extractReturnType(valueNode);
|
|
995
1050
|
const generics = extractGenerics(valueNode);
|
|
996
1051
|
const docstring = extractJSDocstring(lines, startLine);
|
|
@@ -1014,7 +1069,8 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
1014
1069
|
params: extractParams(paramsNode),
|
|
1015
1070
|
paramsStructured,
|
|
1016
1071
|
startLine,
|
|
1017
|
-
|
|
1072
|
+
nameLine: nameNode.startPosition.row + 1,
|
|
1073
|
+
endLine, sourceSpan: nodeToLocation(valueNode, lines).sourceSpan,
|
|
1018
1074
|
indent,
|
|
1019
1075
|
isArrow,
|
|
1020
1076
|
isGenerator: isGen,
|
|
@@ -1051,7 +1107,8 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
1051
1107
|
if (innerFn && (innerFn.type === 'arrow_function' || innerFn.type === 'function_expression')) {
|
|
1052
1108
|
processedRanges.add(rangeKey);
|
|
1053
1109
|
const paramsNode = innerFn.childForFieldName('parameters');
|
|
1054
|
-
const { startLine, endLine, indent } = nodeToLocation(
|
|
1110
|
+
const { startLine, endLine, indent } = nodeToLocation(
|
|
1111
|
+
declarators.length === 1 ? node : declarator, lines);
|
|
1055
1112
|
const returnType = extractReturnType(innerFn);
|
|
1056
1113
|
const generics = extractGenerics(innerFn);
|
|
1057
1114
|
const docstring = extractJSDocstring(lines, startLine);
|
|
@@ -1066,11 +1123,14 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
1066
1123
|
params: extractParams(paramsNode),
|
|
1067
1124
|
paramsStructured,
|
|
1068
1125
|
startLine,
|
|
1126
|
+
nameLine: nameNode.startPosition.row + 1,
|
|
1069
1127
|
endLine,
|
|
1128
|
+
sourceSpan: nodeToLocation(innerFn, lines).sourceSpan,
|
|
1070
1129
|
indent,
|
|
1071
1130
|
isArrow: innerFn.type === 'arrow_function',
|
|
1072
1131
|
isGenerator: false,
|
|
1073
1132
|
modifiers,
|
|
1133
|
+
...lexicalOwnerRange(node),
|
|
1074
1134
|
...typeAnno,
|
|
1075
1135
|
...(generics && { generics }),
|
|
1076
1136
|
...(docstring && { docstring })
|
|
@@ -1184,6 +1244,7 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
1184
1244
|
paramsStructured,
|
|
1185
1245
|
startLine,
|
|
1186
1246
|
endLine,
|
|
1247
|
+
sourceSpan: nodeToLocation(rightNode, lines).sourceSpan,
|
|
1187
1248
|
indent,
|
|
1188
1249
|
isArrow,
|
|
1189
1250
|
isGenerator: isGen,
|
|
@@ -1269,6 +1330,7 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
1269
1330
|
paramsStructured,
|
|
1270
1331
|
startLine,
|
|
1271
1332
|
endLine,
|
|
1333
|
+
sourceSpan: nodeToLocation(child, lines).sourceSpan,
|
|
1272
1334
|
indent,
|
|
1273
1335
|
isArrow: child.type === 'arrow_function',
|
|
1274
1336
|
isGenerator: isGen,
|
|
@@ -1311,12 +1373,38 @@ function findFunctions(code, parser) {
|
|
|
1311
1373
|
* Process a node for class/interface/type/enum extraction (single-pass helper)
|
|
1312
1374
|
* Returns true if node was matched, false otherwise
|
|
1313
1375
|
*/
|
|
1376
|
+
/**
|
|
1377
|
+
* The location of a class declaration from its first decorator (fix #423):
|
|
1378
|
+
* `@Component({..}) export class C` places the decorators on the export
|
|
1379
|
+
* statement, before `export`; the declaration starts there, as an
|
|
1380
|
+
* undecorated-export or non-exported decorated class starts at its first
|
|
1381
|
+
* token.
|
|
1382
|
+
*/
|
|
1383
|
+
function _decoratedDeclarationLocation(node, lines) {
|
|
1384
|
+
const located = nodeToLocation(node, lines);
|
|
1385
|
+
const wrapper = node.parent?.type === 'export_statement' ? node.parent : null;
|
|
1386
|
+
let first = null;
|
|
1387
|
+
for (let i = 0; wrapper && i < wrapper.namedChildCount; i++) {
|
|
1388
|
+
const child = wrapper.namedChild(i);
|
|
1389
|
+
if (child.type !== 'decorator') continue;
|
|
1390
|
+
if (child.startIndex < node.startIndex) first = first || child;
|
|
1391
|
+
}
|
|
1392
|
+
if (!first) return located;
|
|
1393
|
+
const startLine = first.startPosition.row + 1;
|
|
1394
|
+
const indent = ((lines[first.startPosition.row] || '').match(/^(\s*)/) || ['', ''])[1].length;
|
|
1395
|
+
return {
|
|
1396
|
+
startLine, endLine: located.endLine, indent,
|
|
1397
|
+
sourceSpan: { ...located.sourceSpan, start: first.startIndex, startLine,
|
|
1398
|
+
startColumn: first.startPosition.column },
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1314
1402
|
function _processClass(node, classes, processedRanges, lines) {
|
|
1315
1403
|
// Class declarations (including abstract classes)
|
|
1316
1404
|
if (node.type === 'class_declaration' || node.type === 'class' || node.type === 'abstract_class_declaration') {
|
|
1317
1405
|
const nameNode = node.childForFieldName('name');
|
|
1318
1406
|
if (nameNode) {
|
|
1319
|
-
const { startLine, endLine } =
|
|
1407
|
+
const { startLine, endLine, sourceSpan } = _decoratedDeclarationLocation(node, lines);
|
|
1320
1408
|
const members = extractClassMembers(node, lines);
|
|
1321
1409
|
const docstring = extractJSDocstring(lines, startLine);
|
|
1322
1410
|
const generics = extractGenerics(node);
|
|
@@ -1326,10 +1414,14 @@ function _processClass(node, classes, processedRanges, lines) {
|
|
|
1326
1414
|
const decoratorsWithArgs = extractDecoratorsWithArgs(node);
|
|
1327
1415
|
|
|
1328
1416
|
const isAbstract = node.type === 'abstract_class_declaration';
|
|
1417
|
+
const nameLine = nameNode.startPosition.row + 1;
|
|
1329
1418
|
classes.push({
|
|
1330
1419
|
name: nameNode.text,
|
|
1331
1420
|
startLine,
|
|
1332
1421
|
endLine,
|
|
1422
|
+
sourceSpan,
|
|
1423
|
+
// A decorated class names itself below its first decorator.
|
|
1424
|
+
...(nameLine !== startLine && { nameLine }),
|
|
1333
1425
|
type: 'class',
|
|
1334
1426
|
members,
|
|
1335
1427
|
...(isAbstract && { modifiers: ['abstract'] }),
|
|
@@ -1351,7 +1443,7 @@ function _processClass(node, classes, processedRanges, lines) {
|
|
|
1351
1443
|
if (node.type === 'interface_declaration') {
|
|
1352
1444
|
const nameNode = node.childForFieldName('name');
|
|
1353
1445
|
if (nameNode) {
|
|
1354
|
-
const { startLine, endLine } = nodeToLocation(node, lines);
|
|
1446
|
+
const { startLine, endLine, sourceSpan } = nodeToLocation(node, lines);
|
|
1355
1447
|
const docstring = extractJSDocstring(lines, startLine);
|
|
1356
1448
|
const generics = extractGenerics(node);
|
|
1357
1449
|
const extendsInfo = extractInterfaceExtends(node);
|
|
@@ -1361,6 +1453,7 @@ function _processClass(node, classes, processedRanges, lines) {
|
|
|
1361
1453
|
name: nameNode.text,
|
|
1362
1454
|
startLine,
|
|
1363
1455
|
endLine,
|
|
1456
|
+
sourceSpan,
|
|
1364
1457
|
type: 'interface',
|
|
1365
1458
|
members,
|
|
1366
1459
|
...(docstring && { docstring }),
|
|
@@ -1375,7 +1468,7 @@ function _processClass(node, classes, processedRanges, lines) {
|
|
|
1375
1468
|
if (node.type === 'type_alias_declaration') {
|
|
1376
1469
|
const nameNode = node.childForFieldName('name');
|
|
1377
1470
|
if (nameNode) {
|
|
1378
|
-
const { startLine, endLine } = nodeToLocation(node, lines);
|
|
1471
|
+
const { startLine, endLine, sourceSpan } = nodeToLocation(node, lines);
|
|
1379
1472
|
const docstring = extractJSDocstring(lines, startLine);
|
|
1380
1473
|
const valueNode = node.childForFieldName('value');
|
|
1381
1474
|
// `type ZodTypeAny = ZodType<any, any, any>;` — the alias IS the
|
|
@@ -1395,6 +1488,7 @@ function _processClass(node, classes, processedRanges, lines) {
|
|
|
1395
1488
|
name: nameNode.text,
|
|
1396
1489
|
startLine,
|
|
1397
1490
|
endLine,
|
|
1491
|
+
sourceSpan,
|
|
1398
1492
|
type: 'type',
|
|
1399
1493
|
members,
|
|
1400
1494
|
...(aliasOf && { aliasOf }),
|
|
@@ -1408,7 +1502,7 @@ function _processClass(node, classes, processedRanges, lines) {
|
|
|
1408
1502
|
if (node.type === 'enum_declaration') {
|
|
1409
1503
|
const nameNode = node.childForFieldName('name');
|
|
1410
1504
|
if (nameNode) {
|
|
1411
|
-
const { startLine, endLine } = nodeToLocation(node, lines);
|
|
1505
|
+
const { startLine, endLine, sourceSpan } = nodeToLocation(node, lines);
|
|
1412
1506
|
const docstring = extractJSDocstring(lines, startLine);
|
|
1413
1507
|
const members = extractEnumMembers(node, lines);
|
|
1414
1508
|
|
|
@@ -1416,6 +1510,7 @@ function _processClass(node, classes, processedRanges, lines) {
|
|
|
1416
1510
|
name: nameNode.text,
|
|
1417
1511
|
startLine,
|
|
1418
1512
|
endLine,
|
|
1513
|
+
sourceSpan,
|
|
1419
1514
|
type: 'enum',
|
|
1420
1515
|
members,
|
|
1421
1516
|
...(docstring && { docstring })
|
|
@@ -1435,13 +1530,14 @@ function _processClass(node, classes, processedRanges, lines) {
|
|
|
1435
1530
|
// StoreMutators augmentations). The compiler merges it into the
|
|
1436
1531
|
// TARGET module — never claimable, never importable by this "name".
|
|
1437
1532
|
if (nameNode && nameNode.type !== 'string') {
|
|
1438
|
-
const { startLine, endLine } = nodeToLocation(node, lines);
|
|
1533
|
+
const { startLine, endLine, sourceSpan } = nodeToLocation(node, lines);
|
|
1439
1534
|
const docstring = extractJSDocstring(lines, startLine);
|
|
1440
1535
|
|
|
1441
1536
|
classes.push({
|
|
1442
1537
|
name: nameNode.text,
|
|
1443
1538
|
startLine,
|
|
1444
1539
|
endLine,
|
|
1540
|
+
sourceSpan,
|
|
1445
1541
|
type: 'namespace',
|
|
1446
1542
|
members: [],
|
|
1447
1543
|
...(docstring && { docstring })
|
|
@@ -1578,7 +1674,7 @@ function extractTypeMembers(bodyNode, code) {
|
|
|
1578
1674
|
const nameNode = child.childForFieldName('name');
|
|
1579
1675
|
const paramsNode = child.childForFieldName('parameters');
|
|
1580
1676
|
if (nameNode) {
|
|
1581
|
-
const { startLine, endLine } = nodeToLocation(child, code);
|
|
1677
|
+
const { startLine, endLine, sourceSpan } = nodeToLocation(child, code);
|
|
1582
1678
|
const returnType = extractReturnType(child);
|
|
1583
1679
|
const paramsStructured = parseStructuredParams(paramsNode, 'javascript');
|
|
1584
1680
|
const typeAnno = buildTypeAnnotations(paramsStructured, returnType, code, startLine, true);
|
|
@@ -1588,6 +1684,7 @@ function extractTypeMembers(bodyNode, code) {
|
|
|
1588
1684
|
paramsStructured,
|
|
1589
1685
|
startLine,
|
|
1590
1686
|
endLine,
|
|
1687
|
+
sourceSpan,
|
|
1591
1688
|
memberType: 'method',
|
|
1592
1689
|
isMethod: true,
|
|
1593
1690
|
...typeAnno
|
|
@@ -1596,7 +1693,7 @@ function extractTypeMembers(bodyNode, code) {
|
|
|
1596
1693
|
} else if (child.type === 'property_signature') {
|
|
1597
1694
|
const nameNode = child.childForFieldName('name');
|
|
1598
1695
|
if (nameNode) {
|
|
1599
|
-
const { startLine, endLine } = nodeToLocation(child, code);
|
|
1696
|
+
const { startLine, endLine, sourceSpan } = nodeToLocation(child, code);
|
|
1600
1697
|
// Declared property type (fix #219): raw annotation text —
|
|
1601
1698
|
// findCallers hops field receivers to it (this._map.has()),
|
|
1602
1699
|
// and function-typed properties ((arg) => T) count as
|
|
@@ -1607,6 +1704,7 @@ function extractTypeMembers(bodyNode, code) {
|
|
|
1607
1704
|
name: nameNode.text,
|
|
1608
1705
|
startLine,
|
|
1609
1706
|
endLine,
|
|
1707
|
+
sourceSpan,
|
|
1610
1708
|
memberType: 'field',
|
|
1611
1709
|
...(fieldType && { fieldType })
|
|
1612
1710
|
});
|
|
@@ -1629,12 +1727,12 @@ function extractEnumMembers(enumNode, code) {
|
|
|
1629
1727
|
if (child.type === 'enum_assignment') {
|
|
1630
1728
|
const nameNode = child.childForFieldName('name');
|
|
1631
1729
|
if (nameNode) {
|
|
1632
|
-
const { startLine, endLine } = nodeToLocation(child, code);
|
|
1633
|
-
members.push({ name: nameNode.text, startLine, endLine, memberType: 'field' });
|
|
1730
|
+
const { startLine, endLine, sourceSpan } = nodeToLocation(child, code);
|
|
1731
|
+
members.push({ name: nameNode.text, startLine, endLine, sourceSpan, memberType: 'field' });
|
|
1634
1732
|
}
|
|
1635
1733
|
} else if (child.type === 'property_identifier') {
|
|
1636
|
-
const { startLine, endLine } = nodeToLocation(child, code);
|
|
1637
|
-
members.push({ name: child.text, startLine, endLine, memberType: 'field' });
|
|
1734
|
+
const { startLine, endLine, sourceSpan } = nodeToLocation(child, code);
|
|
1735
|
+
members.push({ name: child.text, startLine, endLine, sourceSpan, memberType: 'field' });
|
|
1638
1736
|
}
|
|
1639
1737
|
}
|
|
1640
1738
|
return members;
|
|
@@ -1657,9 +1755,10 @@ function extractClassMembers(classNode, codeOrLines) {
|
|
|
1657
1755
|
const nameNode = child.childForFieldName('name');
|
|
1658
1756
|
const paramsNode = child.childForFieldName('parameters');
|
|
1659
1757
|
if (nameNode) {
|
|
1660
|
-
const { startLine, endLine } = nodeToLocation(child, code);
|
|
1661
|
-
let name = nameNode.text;
|
|
1758
|
+
const { startLine, endLine, sourceSpan } = nodeToLocation(child, code);
|
|
1759
|
+
let name = classMethodName(nameNode) === 'constructor' ? 'constructor' : nameNode.text;
|
|
1662
1760
|
const text = child.text;
|
|
1761
|
+
const isStatic = child.children.some(part => part.type === 'static');
|
|
1663
1762
|
|
|
1664
1763
|
// Collect decorators from preceding siblings in the class body
|
|
1665
1764
|
const decorators = [];
|
|
@@ -1685,7 +1784,7 @@ function extractClassMembers(classNode, codeOrLines) {
|
|
|
1685
1784
|
if (staticMatch) name = staticMatch[1];
|
|
1686
1785
|
}
|
|
1687
1786
|
|
|
1688
|
-
if (name === 'constructor') {
|
|
1787
|
+
if (name === 'constructor' && !isStatic && nameNode.type !== 'computed_property_name') {
|
|
1689
1788
|
memberType = 'constructor';
|
|
1690
1789
|
} else if (text.match(/^\s*(?:public\s+|private\s+|protected\s+)?(?:override\s+)?static\s+(?:override\s+)?get\s/)) {
|
|
1691
1790
|
memberType = hasOverride ? 'static override get' : 'static get';
|
|
@@ -1726,6 +1825,7 @@ function extractClassMembers(classNode, codeOrLines) {
|
|
|
1726
1825
|
paramsStructured,
|
|
1727
1826
|
startLine,
|
|
1728
1827
|
endLine,
|
|
1828
|
+
sourceSpan,
|
|
1729
1829
|
memberType,
|
|
1730
1830
|
...(accessMatch && { modifiers: [accessMatch[1]] }),
|
|
1731
1831
|
isAsync,
|
|
@@ -1783,7 +1883,7 @@ function extractClassMembers(classNode, codeOrLines) {
|
|
|
1783
1883
|
const nameNode = child.childForFieldName('name');
|
|
1784
1884
|
const paramsNode = child.childForFieldName('parameters');
|
|
1785
1885
|
if (nameNode) {
|
|
1786
|
-
const { startLine, endLine } = nodeToLocation(child, code);
|
|
1886
|
+
const { startLine, endLine, sourceSpan } = nodeToLocation(child, code);
|
|
1787
1887
|
const returnType = extractReturnType(child);
|
|
1788
1888
|
const docstring = extractJSDocstring(code, startLine);
|
|
1789
1889
|
const paramsStructured = parseStructuredParams(paramsNode, 'javascript');
|
|
@@ -1805,6 +1905,7 @@ function extractClassMembers(classNode, codeOrLines) {
|
|
|
1805
1905
|
paramsStructured,
|
|
1806
1906
|
startLine,
|
|
1807
1907
|
endLine,
|
|
1908
|
+
sourceSpan,
|
|
1808
1909
|
memberType: 'abstract',
|
|
1809
1910
|
isMethod: true,
|
|
1810
1911
|
...typeAnno,
|
|
@@ -1818,7 +1919,7 @@ function extractClassMembers(classNode, codeOrLines) {
|
|
|
1818
1919
|
if (child.type === 'field_definition' || child.type === 'public_field_definition') {
|
|
1819
1920
|
const nameNode = child.childForFieldName('name') || child.childForFieldName('property');
|
|
1820
1921
|
if (nameNode) {
|
|
1821
|
-
const { startLine, endLine } = nodeToLocation(child, code);
|
|
1922
|
+
const { startLine, endLine, sourceSpan } = nodeToLocation(child, code);
|
|
1822
1923
|
const name = nameNode.text;
|
|
1823
1924
|
// fix #390: a TS decorator is part of the field node; the
|
|
1824
1925
|
// name's line is where definition edits go.
|
|
@@ -1865,6 +1966,7 @@ function extractClassMembers(classNode, codeOrLines) {
|
|
|
1865
1966
|
paramsStructured,
|
|
1866
1967
|
startLine,
|
|
1867
1968
|
endLine,
|
|
1969
|
+
sourceSpan,
|
|
1868
1970
|
...fieldNameLine,
|
|
1869
1971
|
memberType: name.startsWith('#') ? 'private' : 'field',
|
|
1870
1972
|
...(isStatic && { modifiers: ['static'] }),
|
|
@@ -1892,6 +1994,7 @@ function extractClassMembers(classNode, codeOrLines) {
|
|
|
1892
1994
|
name,
|
|
1893
1995
|
startLine,
|
|
1894
1996
|
endLine,
|
|
1997
|
+
sourceSpan,
|
|
1895
1998
|
...fieldNameLine,
|
|
1896
1999
|
memberType: name.startsWith('#') ? 'private field' : 'field',
|
|
1897
2000
|
...(isStatic && { modifiers: ['static'] }),
|
|
@@ -1905,9 +2008,48 @@ function extractClassMembers(classNode, codeOrLines) {
|
|
|
1905
2008
|
}
|
|
1906
2009
|
}
|
|
1907
2010
|
|
|
2011
|
+
members.push(...jsInstanceProperties(bodyNode, members.map(member => member.name)));
|
|
1908
2012
|
return members;
|
|
1909
2013
|
}
|
|
1910
2014
|
|
|
2015
|
+
const JS_OWN_THIS_SCOPES = new Set(['function_expression', 'function_declaration', 'function',
|
|
2016
|
+
'generator_function', 'generator_function_declaration', 'class', 'class_declaration',
|
|
2017
|
+
'abstract_class_declaration', 'method_definition']);
|
|
2018
|
+
|
|
2019
|
+
/**
|
|
2020
|
+
* Instance properties a class's methods create by assigning them on `this`
|
|
2021
|
+
* (fix #431, as Python instance attributes, fix #430): one per name, the
|
|
2022
|
+
* first assignment in source order, for names the class body does not
|
|
2023
|
+
* declare. A static method's `this` is the class; a nested function or
|
|
2024
|
+
* class has its own `this`; an arrow function shares it.
|
|
2025
|
+
*/
|
|
2026
|
+
function jsInstanceProperties(bodyNode, declared) {
|
|
2027
|
+
const out = [];
|
|
2028
|
+
const seen = new Set(declared);
|
|
2029
|
+
for (let i = 0; i < bodyNode.namedChildCount; i++) {
|
|
2030
|
+
const member = bodyNode.namedChild(i);
|
|
2031
|
+
if (member.type !== 'method_definition' || member.children.some(part => part.type === 'static')) continue;
|
|
2032
|
+
const body = member.childForFieldName('body');
|
|
2033
|
+
if (!body) continue;
|
|
2034
|
+
traverseTree(body, node => {
|
|
2035
|
+
if (JS_OWN_THIS_SCOPES.has(node.type)) return false;
|
|
2036
|
+
if (node.type !== 'assignment_expression' && node.type !== 'augmented_assignment_expression') return true;
|
|
2037
|
+
const left = node.childForFieldName('left');
|
|
2038
|
+
const property = left?.type === 'member_expression' && left.childForFieldName('object')?.type === 'this'
|
|
2039
|
+
? left.childForFieldName('property') : null;
|
|
2040
|
+
// Reading an inherited member on the RHS still writes an own
|
|
2041
|
+
// instance property, as in `this.m = this.m.bind(this)`.
|
|
2042
|
+
if (property?.type === 'property_identifier' && !seen.has(property.text)) {
|
|
2043
|
+
seen.add(property.text);
|
|
2044
|
+
out.push({ name: property.text, startLine: property.startPosition.row + 1,
|
|
2045
|
+
endLine: node.endPosition.row + 1, memberType: 'field', instanceAttribute: true });
|
|
2046
|
+
}
|
|
2047
|
+
return true;
|
|
2048
|
+
});
|
|
2049
|
+
}
|
|
2050
|
+
return out;
|
|
2051
|
+
}
|
|
2052
|
+
|
|
1911
2053
|
// Module-level state detection helpers
|
|
1912
2054
|
const _STATE_PATTERN = /^(CONFIG|[A-Z][a-zA-Z]*(?:State|Store|Context|Options|Settings)|[A-Z][A-Z_]+|Entities|Input)$/;
|
|
1913
2055
|
const _ACTION_PATTERN = /^(action\w*|[a-z]+Action|[a-z]+State)$/;
|
|
@@ -1925,25 +2067,63 @@ function _isFactoryCall(node) {
|
|
|
1925
2067
|
* Process a node for state object extraction (single-pass helper)
|
|
1926
2068
|
* Returns true if node was matched, false otherwise
|
|
1927
2069
|
*/
|
|
2070
|
+
function moduleStateScope(declaration) {
|
|
2071
|
+
const namespaces = [];
|
|
2072
|
+
for (let scope = declaration.parent; scope; scope = scope.parent) {
|
|
2073
|
+
if (scope.type === 'program') return { namespace: namespaces.reverse().join('.') };
|
|
2074
|
+
if (scope.type === 'export_statement' || scope.type === 'ambient_declaration') continue;
|
|
2075
|
+
if (scope.type === 'internal_module' || scope.type === 'module') {
|
|
2076
|
+
const name = scope.childForFieldName('name');
|
|
2077
|
+
if (!name || name.type === 'string') return null;
|
|
2078
|
+
namespaces.push(name.text);
|
|
2079
|
+
continue;
|
|
2080
|
+
}
|
|
2081
|
+
if (scope.type === 'statement_block' &&
|
|
2082
|
+
['internal_module', 'module'].includes(scope.parent?.type)) continue;
|
|
2083
|
+
if (FUNCTION_NODE_TYPES.has(scope.type) ||
|
|
2084
|
+
['class_body', 'class_static_block'].includes(scope.type)) return null;
|
|
2085
|
+
if (declaration.type !== 'variable_declaration') return null;
|
|
2086
|
+
}
|
|
2087
|
+
return null;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
1928
2090
|
function _processState(node, objects, lines) {
|
|
1929
2091
|
if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {
|
|
2092
|
+
const moduleScope = moduleStateScope(node);
|
|
1930
2093
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
1931
2094
|
const declarator = node.namedChild(i);
|
|
1932
2095
|
if (declarator.type === 'variable_declarator') {
|
|
1933
2096
|
const nameNode = declarator.childForFieldName('name');
|
|
1934
2097
|
const valueNode = declarator.childForFieldName('value');
|
|
1935
2098
|
|
|
2099
|
+
const value = unwrapValueExpression(valueNode);
|
|
2100
|
+
if (moduleScope && nameNode && !isImportInitializerNode(valueNode) &&
|
|
2101
|
+
!['arrow_function', 'function', 'function_expression', 'generator_function',
|
|
2102
|
+
'class', 'class_declaration'].includes(value?.type)) {
|
|
2103
|
+
const names = [];
|
|
2104
|
+
collectPatternNames(nameNode, names, true);
|
|
2105
|
+
for (const binding of names) {
|
|
2106
|
+
objects.push({ name: binding.text,
|
|
2107
|
+
startLine: binding.startPosition.row + 1,
|
|
2108
|
+
endLine: declarator.endPosition.row + 1,
|
|
2109
|
+
sourceSpan: nodeToLocation(declarator, lines).sourceSpan,
|
|
2110
|
+
...(moduleScope.namespace && { namespace: moduleScope.namespace }),
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
continue;
|
|
2114
|
+
}
|
|
2115
|
+
|
|
1936
2116
|
if (nameNode && valueNode) {
|
|
1937
2117
|
const name = nameNode.text;
|
|
1938
2118
|
const isObject = valueNode.type === 'object';
|
|
1939
2119
|
const isArray = valueNode.type === 'array';
|
|
1940
2120
|
|
|
1941
2121
|
if ((isObject || isArray) && _STATE_PATTERN.test(name)) {
|
|
1942
|
-
const { startLine, endLine } = nodeToLocation(node, lines);
|
|
1943
|
-
objects.push({ name, startLine, endLine });
|
|
2122
|
+
const { startLine, endLine, sourceSpan } = nodeToLocation(node, lines);
|
|
2123
|
+
objects.push({ name, startLine, endLine, sourceSpan });
|
|
1944
2124
|
} else if (_isFactoryCall(valueNode) && (_ACTION_PATTERN.test(name) || _STATE_PATTERN.test(name))) {
|
|
1945
|
-
const { startLine, endLine } = nodeToLocation(node, lines);
|
|
1946
|
-
objects.push({ name, startLine, endLine });
|
|
2125
|
+
const { startLine, endLine, sourceSpan } = nodeToLocation(node, lines);
|
|
2126
|
+
objects.push({ name, startLine, endLine, sourceSpan });
|
|
1947
2127
|
}
|
|
1948
2128
|
}
|
|
1949
2129
|
}
|
|
@@ -2051,33 +2231,63 @@ function jsModuleValueAliases(root) {
|
|
|
2051
2231
|
}
|
|
2052
2232
|
}
|
|
2053
2233
|
}
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
for (const child of node.namedChildren) writeTargets(child);
|
|
2066
|
-
}
|
|
2067
|
-
};
|
|
2068
|
-
for (const node of root.descendantsOfType(['assignment_expression',
|
|
2069
|
-
'augmented_assignment_expression', 'update_expression', 'for_in_statement'])) {
|
|
2070
|
-
if (node.type === 'update_expression') writeTargets(node.namedChild(0));
|
|
2071
|
-
else if (node.type === 'for_in_statement') {
|
|
2072
|
-
if (!node.childForFieldName('kind')) writeTargets(node.childForFieldName('left'));
|
|
2073
|
-
} else writeTargets(node.childForFieldName('left'));
|
|
2234
|
+
// A `let`/`var` alias is the class only while nothing assigns the
|
|
2235
|
+
// name again (fix #392): any write to that identifier anywhere in the
|
|
2236
|
+
// module, or a second declaration, leaves it unbound.
|
|
2237
|
+
const written = new Set();
|
|
2238
|
+
const writeTargets = (node) => {
|
|
2239
|
+
if (!node) return;
|
|
2240
|
+
if (node.type === 'identifier') { written.add(node.text); return; }
|
|
2241
|
+
if (/pattern$/.test(node.type) || node.type === 'pair_pattern' ||
|
|
2242
|
+
node.type === 'shorthand_property_identifier_pattern') {
|
|
2243
|
+
if (node.type === 'shorthand_property_identifier_pattern') written.add(node.text);
|
|
2244
|
+
for (const child of node.namedChildren) writeTargets(child);
|
|
2074
2245
|
}
|
|
2075
|
-
|
|
2076
|
-
|
|
2246
|
+
};
|
|
2247
|
+
for (const node of root.descendantsOfType(['assignment_expression',
|
|
2248
|
+
'augmented_assignment_expression', 'update_expression', 'for_in_statement'])) {
|
|
2249
|
+
if (node.type === 'update_expression') writeTargets(node.namedChild(0));
|
|
2250
|
+
else if (node.type === 'for_in_statement') {
|
|
2251
|
+
if (!node.childForFieldName('kind')) writeTargets(node.childForFieldName('left'));
|
|
2252
|
+
} else writeTargets(node.childForFieldName('left'));
|
|
2253
|
+
}
|
|
2254
|
+
for (const alias of mutable) {
|
|
2255
|
+
if (!written.has(alias.name) && declared.get(alias.name) === 1) aliases.push(alias);
|
|
2256
|
+
}
|
|
2257
|
+
aliases.sort((a, b) => a.line - b.line);
|
|
2258
|
+
return { aliases, assignedNames: [...written].sort() };
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
/**
|
|
2262
|
+
* Declarations inside `declare global { .. }` and `declare module 'name' {
|
|
2263
|
+
* .. }` merge with declarations outside the project (fix #423: `interface
|
|
2264
|
+
* Window`, a router's `Register`): the checker reads them through the merged
|
|
2265
|
+
* declaration, so no use names them in project code. Marks every symbol
|
|
2266
|
+
* (and member) whose span lies in such a block `ambientAugmentation`.
|
|
2267
|
+
*/
|
|
2268
|
+
function markAmbientAugmentations(root, groups) {
|
|
2269
|
+
const ranges = [];
|
|
2270
|
+
for (let i = 0; i < root.namedChildCount; i++) {
|
|
2271
|
+
let node = root.namedChild(i);
|
|
2272
|
+
if (node.type === 'export_statement') node = node.namedChild(0);
|
|
2273
|
+
if (node?.type !== 'ambient_declaration') continue;
|
|
2274
|
+
const inner = node.namedChild(0);
|
|
2275
|
+
const augments = inner?.type === 'statement_block' ||
|
|
2276
|
+
(inner?.type === 'module' && inner.childForFieldName('name')?.type === 'string');
|
|
2277
|
+
if (augments) ranges.push([node.startIndex, node.endIndex]);
|
|
2278
|
+
}
|
|
2279
|
+
if (ranges.length === 0) return;
|
|
2280
|
+
const inside = item => {
|
|
2281
|
+
const start = item?.sourceSpan?.start;
|
|
2282
|
+
return Number.isInteger(start) && ranges.some(([from, to]) => start >= from && start < to);
|
|
2283
|
+
};
|
|
2284
|
+
for (const group of groups) {
|
|
2285
|
+
for (const item of group) {
|
|
2286
|
+
if (!inside(item)) continue;
|
|
2287
|
+
item.ambientAugmentation = true;
|
|
2288
|
+
for (const member of item.members || []) member.ambientAugmentation = true;
|
|
2077
2289
|
}
|
|
2078
|
-
aliases.sort((a, b) => a.line - b.line);
|
|
2079
2290
|
}
|
|
2080
|
-
return aliases;
|
|
2081
2291
|
}
|
|
2082
2292
|
|
|
2083
2293
|
function parse(code, parser) {
|
|
@@ -2191,7 +2401,8 @@ function parse(code, parser) {
|
|
|
2191
2401
|
classes.sort((a, b) => a.startLine - b.startLine);
|
|
2192
2402
|
stateObjects.sort((a, b) => a.startLine - b.startLine);
|
|
2193
2403
|
callableAliases.sort((a, b) => a.startLine - b.startLine);
|
|
2194
|
-
|
|
2404
|
+
markAmbientAugmentations(tree.rootNode, [functions, classes, stateObjects]);
|
|
2405
|
+
const { aliases: moduleValueAliases, assignedNames: moduleAssignedNames } = jsModuleValueAliases(tree.rootNode);
|
|
2195
2406
|
|
|
2196
2407
|
return {
|
|
2197
2408
|
language: 'javascript',
|
|
@@ -2201,6 +2412,7 @@ function parse(code, parser) {
|
|
|
2201
2412
|
stateObjects,
|
|
2202
2413
|
callableAliases,
|
|
2203
2414
|
...(moduleValueAliases.length > 0 && { moduleValueAliases }),
|
|
2415
|
+
...(moduleAssignedNames.length > 0 && { moduleAssignedNames }),
|
|
2204
2416
|
...(tree.rootNode.hasError && { parseRecovery: true, parseErrorRegions: parseErrorRegions(tree.rootNode) }),
|
|
2205
2417
|
imports: [], // Handled by core/imports.js
|
|
2206
2418
|
exports: [] // Handled by core/imports.js
|
|
@@ -2447,6 +2659,40 @@ function jsLiteralRequireModule(node) {
|
|
|
2447
2659
|
return first?.type === 'string' ? first.text.slice(1, -1) : undefined;
|
|
2448
2660
|
}
|
|
2449
2661
|
|
|
2662
|
+
// Functions with their own `this`; an arrow function reads its enclosing one.
|
|
2663
|
+
const JS_THIS_FUNCTIONS = new Set(['function_declaration', 'function_expression', 'function',
|
|
2664
|
+
'generator_function', 'generator_function_declaration']);
|
|
2665
|
+
|
|
2666
|
+
/**
|
|
2667
|
+
* A construction through a method's own receiver (fix #434): `new this(..)`
|
|
2668
|
+
* in a static method and `new this.constructor(..)` in an instance method
|
|
2669
|
+
* build the method's class or a subclass of it. Returns the class (by its
|
|
2670
|
+
* name line), the method, the receiver binding and the callee spelling.
|
|
2671
|
+
*/
|
|
2672
|
+
function jsOwnClassConstruction(newNode, ctorNode) {
|
|
2673
|
+
let binding;
|
|
2674
|
+
if (ctorNode.type === 'this') binding = 'class';
|
|
2675
|
+
else if (ctorNode.type === 'member_expression' && ctorNode.childForFieldName('object')?.type === 'this' &&
|
|
2676
|
+
ctorNode.childForFieldName('property')?.text === 'constructor') binding = 'instance';
|
|
2677
|
+
else return null;
|
|
2678
|
+
let method = null;
|
|
2679
|
+
for (let parent = newNode.parent; parent && !method; parent = parent.parent) {
|
|
2680
|
+
if (JS_THIS_FUNCTIONS.has(parent.type) || parent.type === 'class_body' || parent.type === 'program' ||
|
|
2681
|
+
parent.type === 'class_static_block') return null;
|
|
2682
|
+
if (parent.type === 'method_definition') method = parent;
|
|
2683
|
+
}
|
|
2684
|
+
const classNode = method?.parent?.type === 'class_body' ? method.parent.parent : null;
|
|
2685
|
+
const className = classNode && ['class_declaration', 'abstract_class_declaration'].includes(classNode.type)
|
|
2686
|
+
? classNode.childForFieldName('name') : null;
|
|
2687
|
+
const methodName = method?.childForFieldName('name');
|
|
2688
|
+
if (!className || !methodName) return null;
|
|
2689
|
+
const isStatic = method.children.some(child => child.type === 'static');
|
|
2690
|
+
if ((binding === 'class') !== isStatic) return null;
|
|
2691
|
+
return { name: className.text, line: className.startPosition.row + 1, method: methodName.text,
|
|
2692
|
+
methodLine: methodName.startPosition.row + 1, receiver: 'this', binding,
|
|
2693
|
+
spelling: binding === 'class' ? 'this' : 'constructor' };
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2450
2696
|
function findCallsInCode(code, parser) {
|
|
2451
2697
|
const tree = parseTree(parser, code);
|
|
2452
2698
|
const calls = [];
|
|
@@ -2997,8 +3243,10 @@ function findCallsInCode(code, parser) {
|
|
|
2997
3243
|
return nameNode?.text || '<anonymous>';
|
|
2998
3244
|
}
|
|
2999
3245
|
if (node.type === 'arrow_function') {
|
|
3000
|
-
// Arrow functions don't have names, but check parent for variable
|
|
3001
|
-
|
|
3246
|
+
// Arrow functions don't have names, but check parent for variable
|
|
3247
|
+
// assignment, through parentheses and casts (fix #434).
|
|
3248
|
+
let parent = node.parent;
|
|
3249
|
+
while (parent && JS_VALUE_WRAPPERS.has(parent.type)) parent = parent.parent;
|
|
3002
3250
|
if (parent?.type === 'variable_declarator') {
|
|
3003
3251
|
const nameNode = parent.childForFieldName('name');
|
|
3004
3252
|
return nameNode?.text || '<anonymous>';
|
|
@@ -3676,6 +3924,38 @@ function findCallsInCode(code, parser) {
|
|
|
3676
3924
|
enclosingFunction: getCurrentEnclosingFunction(),
|
|
3677
3925
|
});
|
|
3678
3926
|
};
|
|
3927
|
+
// A method overwritten on a typed receiver (fix #423, commander tests:
|
|
3928
|
+
// `cmd.copyInheritedSettings = mock` with `cmd = new Command()`): the
|
|
3929
|
+
// written member names the method exactly like a stored read, and a
|
|
3930
|
+
// rename that leaves it assigns another property. Only values that can
|
|
3931
|
+
// hold a callable record a reference; a data write (`x.count = 0`)
|
|
3932
|
+
// records nothing, and `this` members are read by their own pass.
|
|
3933
|
+
const CALLABLE_VALUE_TYPES = new Set(['identifier', 'member_expression', 'arrow_function',
|
|
3934
|
+
'function_expression', 'function', 'call_expression', 'conditional_expression']);
|
|
3935
|
+
const memberWriteReference = (leftNode, rightNode) => {
|
|
3936
|
+
if (leftNode?.type !== 'member_expression') return;
|
|
3937
|
+
const prop = leftNode.childForFieldName('property');
|
|
3938
|
+
const obj = leftNode.childForFieldName('object');
|
|
3939
|
+
if (prop?.type !== 'property_identifier' || obj?.type !== 'identifier' || SKIP_IDENTS.has(prop.text)) return;
|
|
3940
|
+
let value = rightNode;
|
|
3941
|
+
while (value && (value.type === 'parenthesized_expression' || value.type === 'as_expression' ||
|
|
3942
|
+
value.type === 'satisfies_expression' || value.type === 'non_null_expression')) {
|
|
3943
|
+
value = value.namedChild(0);
|
|
3944
|
+
}
|
|
3945
|
+
if (!value || !CALLABLE_VALUE_TYPES.has(value.type)) return;
|
|
3946
|
+
const facts = memberReceiverFacts(obj, prop.text);
|
|
3947
|
+
if (!facts.receiverType) return;
|
|
3948
|
+
calls.push({
|
|
3949
|
+
name: prop.text,
|
|
3950
|
+
line: prop.startPosition.row + 1,
|
|
3951
|
+
column: prop.startPosition.column,
|
|
3952
|
+
isMethod: true,
|
|
3953
|
+
...facts,
|
|
3954
|
+
isFunctionReference: true,
|
|
3955
|
+
memberValue: true,
|
|
3956
|
+
enclosingFunction: getCurrentEnclosingFunction(),
|
|
3957
|
+
});
|
|
3958
|
+
};
|
|
3679
3959
|
const destructuredFields = (refNode) => {
|
|
3680
3960
|
const destructured = destructuredBindingOf(refNode, refNode.text);
|
|
3681
3961
|
return destructured ? { destructured } : null;
|
|
@@ -3867,6 +4147,7 @@ function findCallsInCode(code, parser) {
|
|
|
3867
4147
|
const left = node.childForFieldName('left');
|
|
3868
4148
|
const right = node.childForFieldName('right');
|
|
3869
4149
|
memberValueReference(right);
|
|
4150
|
+
memberWriteReference(left, right);
|
|
3870
4151
|
if (left?.type === 'identifier') {
|
|
3871
4152
|
if (right?.type === 'new_expression') {
|
|
3872
4153
|
nonCallableNames.add(left.text);
|
|
@@ -4020,6 +4301,7 @@ function findCallsInCode(code, parser) {
|
|
|
4020
4301
|
// the parent class's constructor definition.
|
|
4021
4302
|
calls.push({
|
|
4022
4303
|
name: 'constructor',
|
|
4304
|
+
constructorSyntax: 'delegation',
|
|
4023
4305
|
line: node.startPosition.row + 1,
|
|
4024
4306
|
callStart: node.startIndex,
|
|
4025
4307
|
callEnd: node.endIndex,
|
|
@@ -4288,12 +4570,34 @@ function findCallsInCode(code, parser) {
|
|
|
4288
4570
|
const ctorNode = node.childForFieldName('constructor');
|
|
4289
4571
|
if (ctorNode) {
|
|
4290
4572
|
const enclosingFunction = getCurrentEnclosingFunction();
|
|
4573
|
+
const args = (node.childForFieldName('arguments')?.namedChildren || [])
|
|
4574
|
+
.filter(arg => !arg.type.endsWith('comment'));
|
|
4575
|
+
const argumentsShape = {
|
|
4576
|
+
argCount: args.length,
|
|
4577
|
+
...(args.some(arg => arg.type === 'spread_element') && { argSpread: true }),
|
|
4578
|
+
};
|
|
4291
4579
|
|
|
4292
4580
|
// fix #366: `new Router({ prefix: '/api' })` (koa-router)
|
|
4293
4581
|
// carries a constructor mount prefix.
|
|
4294
4582
|
const ctorPrefix = hasObjectKeyArg(node, 'prefix');
|
|
4295
|
-
|
|
4583
|
+
const ownClass = jsOwnClassConstruction(node, ctorNode);
|
|
4584
|
+
if (ctorNode.type === 'this' && ownClass) {
|
|
4585
|
+
// `new this(..)` in a static method builds the method's
|
|
4586
|
+
// class or a subclass of it (fix #434).
|
|
4587
|
+
calls.push({
|
|
4588
|
+
callSite: typeOrigin('call', ctorNode),
|
|
4589
|
+
...argumentsShape,
|
|
4590
|
+
name: ownClass.name,
|
|
4591
|
+
line: node.startPosition.row + 1,
|
|
4592
|
+
isMethod: false,
|
|
4593
|
+
isConstructor: true,
|
|
4594
|
+
ownClass,
|
|
4595
|
+
enclosingFunction
|
|
4596
|
+
});
|
|
4597
|
+
} else if (ctorNode.type === 'identifier') {
|
|
4296
4598
|
calls.push({
|
|
4599
|
+
callSite: typeOrigin('call', ctorNode),
|
|
4600
|
+
...argumentsShape,
|
|
4297
4601
|
name: ctorNode.text,
|
|
4298
4602
|
line: node.startPosition.row + 1,
|
|
4299
4603
|
isMethod: false,
|
|
@@ -4307,10 +4611,14 @@ function findCallsInCode(code, parser) {
|
|
|
4307
4611
|
const propNode = ctorNode.childForFieldName('property');
|
|
4308
4612
|
if (propNode) {
|
|
4309
4613
|
calls.push({
|
|
4614
|
+
callSite: typeOrigin('call', propNode),
|
|
4615
|
+
...argumentsShape,
|
|
4310
4616
|
name: propNode.text,
|
|
4311
4617
|
line: node.startPosition.row + 1,
|
|
4312
4618
|
isMethod: true,
|
|
4313
4619
|
isConstructor: true,
|
|
4620
|
+
...memberReceiverFacts(ctorNode.childForFieldName('object'), propNode.text),
|
|
4621
|
+
...(ownClass && { ownClass }),
|
|
4314
4622
|
...(ctorPrefix && { prefixOption: true }),
|
|
4315
4623
|
enclosingFunction
|
|
4316
4624
|
});
|
|
@@ -4982,8 +5290,39 @@ function findImportsInCode(code, parser) {
|
|
|
4982
5290
|
const deferredFields = deferral ? { deferred: true, deferredReason: deferral } : {};
|
|
4983
5291
|
if (firstArg && firstArg.type === 'string') {
|
|
4984
5292
|
const modulePath = firstArg.text.slice(1, -1);
|
|
4985
|
-
|
|
4986
|
-
|
|
5293
|
+
// `const { a, b: c } = await import('m')` binds the
|
|
5294
|
+
// module's named exports like a static import does
|
|
5295
|
+
// (fix #423, swr `const { preload } = await
|
|
5296
|
+
// import('swr')` in a test).
|
|
5297
|
+
const names = [], renames = [];
|
|
5298
|
+
// `{ default: x }` reads the module's default value,
|
|
5299
|
+
// as `import x from 'm'` (and `require('m')`) does.
|
|
5300
|
+
let defaultLocal = null;
|
|
5301
|
+
let holder = node.parent;
|
|
5302
|
+
while (holder && (holder.type === 'parenthesized_expression' ||
|
|
5303
|
+
holder.type === 'await_expression')) holder = holder.parent;
|
|
5304
|
+
const pattern = holder?.type === 'variable_declarator' ? holder.childForFieldName('name') : null;
|
|
5305
|
+
if (pattern?.type === 'object_pattern' && holder.childForFieldName('value') &&
|
|
5306
|
+
node.startIndex >= holder.childForFieldName('value').startIndex) {
|
|
5307
|
+
for (let i = 0; i < pattern.namedChildCount; i++) {
|
|
5308
|
+
const prop = pattern.namedChild(i);
|
|
5309
|
+
if (prop.type === 'shorthand_property_identifier_pattern') names.push(prop.text);
|
|
5310
|
+
else if (prop.type === 'pair_pattern') {
|
|
5311
|
+
const key = prop.childForFieldName('key');
|
|
5312
|
+
const val = prop.childForFieldName('value');
|
|
5313
|
+
if (key?.type !== 'property_identifier' || val?.type !== 'identifier') continue;
|
|
5314
|
+
if (key.text === 'default') { defaultLocal = val.text; continue; }
|
|
5315
|
+
names.push(key.text);
|
|
5316
|
+
if (val.text !== key.text) renames.push({ original: key.text, local: val.text });
|
|
5317
|
+
}
|
|
5318
|
+
}
|
|
5319
|
+
}
|
|
5320
|
+
imports.push({ module: modulePath, names, type: 'dynamic', line, dynamic: false,
|
|
5321
|
+
...deferredFields, ...(renames.length > 0 && { renames }) });
|
|
5322
|
+
if (defaultLocal) {
|
|
5323
|
+
imports.push({ module: modulePath, names: [defaultLocal], type: 'dynamic', line,
|
|
5324
|
+
dynamic: false, defaultLike: true, ...deferredFields });
|
|
5325
|
+
}
|
|
4987
5326
|
} else if (firstArg) {
|
|
4988
5327
|
imports.push({ module: firstArg.text, names: [], type: 'dynamic', line, dynamic: true,
|
|
4989
5328
|
...deferredFields });
|
|
@@ -5376,6 +5715,16 @@ function findUsagesInCode(code, name, parser, tree, options = {}) {
|
|
|
5376
5715
|
importAliasLocal = true;
|
|
5377
5716
|
}
|
|
5378
5717
|
}
|
|
5718
|
+
// `const { default: name } = await import('m')` / `require('m')`:
|
|
5719
|
+
// the local of the module's default value is an import (fix #423).
|
|
5720
|
+
else if (parent.type === 'pair_pattern' && sameNode(parent.childForFieldName('value'), node) &&
|
|
5721
|
+
parent.childForFieldName('key')?.text === 'default' &&
|
|
5722
|
+
parent.parent?.type === 'object_pattern' &&
|
|
5723
|
+
parent.parent.parent?.type === 'variable_declarator' &&
|
|
5724
|
+
sameNode(parent.parent.parent.childForFieldName('name'), parent.parent) &&
|
|
5725
|
+
isImportInitializerNode(parent.parent.parent.childForFieldName('value'))) {
|
|
5726
|
+
usageType = 'import';
|
|
5727
|
+
}
|
|
5379
5728
|
// Call: identifier is function in call_expression
|
|
5380
5729
|
else if (parent.type === 'call_expression' &&
|
|
5381
5730
|
sameNode(parent.childForFieldName('function'), node)) {
|
|
@@ -5449,10 +5798,13 @@ function findUsagesInCode(code, name, parser, tree, options = {}) {
|
|
|
5449
5798
|
// Check if the object_pattern is part of a variable_declarator with require()
|
|
5450
5799
|
const declarator = parent.parent;
|
|
5451
5800
|
if (declarator && declarator.type === 'variable_declarator') {
|
|
5452
|
-
|
|
5801
|
+
let value = declarator.childForFieldName('value');
|
|
5802
|
+
// `const { name } = await import('m')` (fix #423).
|
|
5803
|
+
while (value && (value.type === 'await_expression' ||
|
|
5804
|
+
value.type === 'parenthesized_expression')) value = value.namedChild(0);
|
|
5453
5805
|
if (value && value.type === 'call_expression') {
|
|
5454
5806
|
const func = value.childForFieldName('function');
|
|
5455
|
-
if (func && func.text === 'require') {
|
|
5807
|
+
if (func && (func.text === 'require' || func.type === 'import')) {
|
|
5456
5808
|
usageType = 'import';
|
|
5457
5809
|
}
|
|
5458
5810
|
}
|