@runtime-type-inspector/transpiler 5.0.1 → 5.0.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/index.cjs +1522 -296
- package/index.mjs +1419 -288
- package/package.json +2 -2
package/index.cjs
CHANGED
|
@@ -427,6 +427,7 @@
|
|
|
427
427
|
PropertySignature = _ts$SyntaxKind.PropertySignature,
|
|
428
428
|
StringKeyword = _ts$SyntaxKind.StringKeyword,
|
|
429
429
|
StringLiteral = _ts$SyntaxKind.StringLiteral,
|
|
430
|
+
SymbolKeyword = _ts$SyntaxKind.SymbolKeyword,
|
|
430
431
|
ThisType = _ts$SyntaxKind.ThisType,
|
|
431
432
|
TupleType = _ts$SyntaxKind.TupleType,
|
|
432
433
|
TypeLiteral = _ts$SyntaxKind.TypeLiteral,
|
|
@@ -453,6 +454,8 @@
|
|
|
453
454
|
var ConstructorType = _ts$SyntaxKind.ConstructorType,
|
|
454
455
|
NamedTupleMember = _ts$SyntaxKind.NamedTupleMember,
|
|
455
456
|
MappedType = _ts$SyntaxKind.MappedType,
|
|
457
|
+
MinusToken = _ts$SyntaxKind.MinusToken,
|
|
458
|
+
PlusToken = _ts$SyntaxKind.PlusToken,
|
|
456
459
|
TypeParameter = _ts$SyntaxKind.TypeParameter,
|
|
457
460
|
QualifiedName = _ts$SyntaxKind.QualifiedName,
|
|
458
461
|
TemplateLiteralType = _ts$SyntaxKind.TemplateLiteralType,
|
|
@@ -565,12 +568,26 @@
|
|
|
565
568
|
// For example: {[K in TaskType]: InstanceType etc.
|
|
566
569
|
var iterable = toSourceTS(parameter.constraint); // TaskType
|
|
567
570
|
var element = toSourceTS(parameter.name); // K
|
|
568
|
-
|
|
571
|
+
var out = {
|
|
569
572
|
type: 'mapping',
|
|
570
573
|
iterable: iterable,
|
|
571
574
|
element: element,
|
|
572
575
|
result: result
|
|
573
576
|
};
|
|
577
|
+
if (node.nameType) {
|
|
578
|
+
// `as` key remapping, e.g. {[K in keyof T as K extends string ? K : never]: ...}
|
|
579
|
+
out.nameType = toSourceTS(node.nameType);
|
|
580
|
+
}
|
|
581
|
+
// Modifiers: `-?` strips optionality, `+?`/`?` force it;
|
|
582
|
+
// `-readonly` strips readonly, `+readonly`/`readonly` force it.
|
|
583
|
+
// Absent modifiers preserve the source behavior (see createTypeFromMapping).
|
|
584
|
+
if (node.questionToken) {
|
|
585
|
+
out.question = node.questionToken.kind === MinusToken ? '-' : node.questionToken.kind === PlusToken ? '+' : '?';
|
|
586
|
+
}
|
|
587
|
+
if (node.readonlyToken) {
|
|
588
|
+
out.readonly = node.readonlyToken.kind === MinusToken ? '-' : node.readonlyToken.kind === PlusToken ? '+' : 'readonly';
|
|
589
|
+
}
|
|
590
|
+
return out;
|
|
574
591
|
}
|
|
575
592
|
console.warn("MappedType: expected TypeParameter");
|
|
576
593
|
return 'transpiler-error';
|
|
@@ -797,6 +814,15 @@
|
|
|
797
814
|
optional: true
|
|
798
815
|
};
|
|
799
816
|
}
|
|
817
|
+
if (Array.isArray(member.modifiers) && member.modifiers.some(function (modifier) {
|
|
818
|
+
return modifier.kind === ReadonlyKeyword;
|
|
819
|
+
})) {
|
|
820
|
+
// Tracked for IfEquals-style comparisons; ignored by validation.
|
|
821
|
+
if (_type && _typeof(_type) === 'object') _type.readonly = true;else _type = {
|
|
822
|
+
type: _type,
|
|
823
|
+
readonly: true
|
|
824
|
+
};
|
|
825
|
+
}
|
|
800
826
|
properties[_name2] = _type;
|
|
801
827
|
} else {
|
|
802
828
|
console.warn('TypeLiteral: unhandled member', member);
|
|
@@ -856,6 +882,7 @@
|
|
|
856
882
|
case AnyKeyword:
|
|
857
883
|
case BooleanKeyword:
|
|
858
884
|
case StringKeyword:
|
|
885
|
+
case SymbolKeyword:
|
|
859
886
|
case NeverKeyword:
|
|
860
887
|
case NullKeyword:
|
|
861
888
|
case NumberKeyword:
|
|
@@ -936,6 +963,7 @@
|
|
|
936
963
|
*/
|
|
937
964
|
/**
|
|
938
965
|
* Splits a string by a delimiter, ignoring delimiters nested inside <>, {}, [], ().
|
|
966
|
+
* Quote-aware: delimiters inside '...', "..." or `...` never split.
|
|
939
967
|
* @param {string} str - The string to split.
|
|
940
968
|
* @param {string} delimiter - Single character delimiter.
|
|
941
969
|
* @returns {string[]} Top-level split parts.
|
|
@@ -946,28 +974,148 @@
|
|
|
946
974
|
var depthCurly = 0;
|
|
947
975
|
var depthSquare = 0;
|
|
948
976
|
var depthParen = 0;
|
|
977
|
+
var inSingle = false;
|
|
978
|
+
var inDouble = false;
|
|
979
|
+
var inBacktick = false;
|
|
949
980
|
var current = '';
|
|
950
|
-
var
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
981
|
+
for (var i = 0; i < str.length; i++) {
|
|
982
|
+
var c = str[i];
|
|
983
|
+
var prev = i > 0 ? str[i - 1] : '';
|
|
984
|
+
if (c === "'" && !inDouble && !inBacktick && prev !== '\\') inSingle = !inSingle;else if (c === '"' && !inSingle && !inBacktick && prev !== '\\') inDouble = !inDouble;else if (c === '`' && !inSingle && !inDouble && prev !== '\\') inBacktick = !inBacktick;
|
|
985
|
+
var inQuote = inSingle || inDouble || inBacktick;
|
|
986
|
+
if (!inQuote) {
|
|
955
987
|
if (c === '<') depthAngle++;else if (c === '>') depthAngle--;else if (c === '{') depthCurly++;else if (c === '}') depthCurly--;else if (c === '[') depthSquare++;else if (c === ']') depthSquare--;else if (c === '(') depthParen++;else if (c === ')') depthParen--;
|
|
956
|
-
if (c === delimiter && depthAngle === 0 && depthCurly === 0 && depthSquare === 0 && depthParen === 0) {
|
|
957
|
-
parts.push(current);
|
|
958
|
-
current = '';
|
|
959
|
-
} else {
|
|
960
|
-
current += c;
|
|
961
|
-
}
|
|
962
988
|
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
989
|
+
if (c === delimiter && !inQuote && depthAngle === 0 && depthCurly === 0 && depthSquare === 0 && depthParen === 0) {
|
|
990
|
+
parts.push(current);
|
|
991
|
+
current = '';
|
|
992
|
+
} else {
|
|
993
|
+
current += c;
|
|
994
|
+
}
|
|
967
995
|
}
|
|
968
996
|
parts.push(current);
|
|
969
997
|
return parts;
|
|
970
998
|
}
|
|
999
|
+
/**
|
|
1000
|
+
* Depth state at a given index, quote-aware.
|
|
1001
|
+
* @param {string} str - The string to scan.
|
|
1002
|
+
* @param {number} upto - Exclusive end index.
|
|
1003
|
+
* @returns {{angle: number, curly: number, square: number, paren: number, quote: boolean}} Depths.
|
|
1004
|
+
*/
|
|
1005
|
+
function depthsAt(str, upto) {
|
|
1006
|
+
var angle = 0;
|
|
1007
|
+
var curly = 0;
|
|
1008
|
+
var square = 0;
|
|
1009
|
+
var paren = 0;
|
|
1010
|
+
var inSingle = false;
|
|
1011
|
+
var inDouble = false;
|
|
1012
|
+
var inBacktick = false;
|
|
1013
|
+
for (var i = 0; i < upto; i++) {
|
|
1014
|
+
var c = str[i];
|
|
1015
|
+
var prev = i > 0 ? str[i - 1] : '';
|
|
1016
|
+
if (c === "'" && !inDouble && !inBacktick && prev !== '\\') inSingle = !inSingle;else if (c === '"' && !inSingle && !inBacktick && prev !== '\\') inDouble = !inDouble;else if (c === '`' && !inSingle && !inDouble && prev !== '\\') inBacktick = !inBacktick;
|
|
1017
|
+
var inQuote = inSingle || inDouble || inBacktick;
|
|
1018
|
+
if (inQuote) continue;
|
|
1019
|
+
if (c === '<') angle++;else if (c === '>') angle--;else if (c === '{') curly++;else if (c === '}') curly--;else if (c === '[') square++;else if (c === ']') square--;else if (c === '(') paren++;else if (c === ')') paren--;
|
|
1020
|
+
}
|
|
1021
|
+
return {
|
|
1022
|
+
angle: angle,
|
|
1023
|
+
curly: curly,
|
|
1024
|
+
square: square,
|
|
1025
|
+
paren: paren,
|
|
1026
|
+
quote: inSingle || inDouble || inBacktick
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Finds a keyword (e.g. `in`, `as`, `extends`) at top level, outside brackets/quotes.
|
|
1031
|
+
* @param {string} str - The string to search.
|
|
1032
|
+
* @param {string} keyword - Keyword without surrounding spaces.
|
|
1033
|
+
* @returns {number} Start index or -1.
|
|
1034
|
+
*/
|
|
1035
|
+
function findTopLevelKeyword(str, keyword) {
|
|
1036
|
+
var isWord = function isWord(c) {
|
|
1037
|
+
return /[A-Za-z0-9_$]/.test(c);
|
|
1038
|
+
};
|
|
1039
|
+
for (var i = 0; i <= str.length - keyword.length; i++) {
|
|
1040
|
+
if (str.slice(i, i + keyword.length) !== keyword) continue;
|
|
1041
|
+
var before = i > 0 ? str[i - 1] : ' ';
|
|
1042
|
+
var after = i + keyword.length < str.length ? str[i + keyword.length] : ' ';
|
|
1043
|
+
if (isWord(before) || isWord(after)) continue;
|
|
1044
|
+
var d = depthsAt(str, i);
|
|
1045
|
+
if (d.angle === 0 && d.curly === 0 && d.square === 0 && d.paren === 0 && !d.quote) {
|
|
1046
|
+
// Ensure remainder after keyword is also top-level (keyword itself not quoted).
|
|
1047
|
+
var d2 = depthsAt(str, i + keyword.length);
|
|
1048
|
+
if (!d2.quote) return i;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return -1;
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Finds a single-char delimiter at top level, outside brackets/quotes.
|
|
1055
|
+
* @param {string} str - The string to search.
|
|
1056
|
+
* @param {string} delimiter - Single character.
|
|
1057
|
+
* @param {number} from - Start index.
|
|
1058
|
+
* @returns {number} Index or -1.
|
|
1059
|
+
*/
|
|
1060
|
+
function findTopLevelChar(str, delimiter) {
|
|
1061
|
+
var from = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
|
1062
|
+
var angle = 0;
|
|
1063
|
+
var curly = 0;
|
|
1064
|
+
var square = 0;
|
|
1065
|
+
var paren = 0;
|
|
1066
|
+
var inSingle = false;
|
|
1067
|
+
var inDouble = false;
|
|
1068
|
+
var inBacktick = false;
|
|
1069
|
+
for (var i = 0; i < str.length; i++) {
|
|
1070
|
+
var c = str[i];
|
|
1071
|
+
// Check at depths *before* the current char, so `[` itself is findable.
|
|
1072
|
+
var inQuoteBefore = inSingle || inDouble || inBacktick;
|
|
1073
|
+
if (i >= from && c === delimiter && !inQuoteBefore && angle === 0 && curly === 0 && square === 0 && paren === 0) {
|
|
1074
|
+
return i;
|
|
1075
|
+
}
|
|
1076
|
+
var prev = i > 0 ? str[i - 1] : '';
|
|
1077
|
+
if (c === "'" && !inDouble && !inBacktick && prev !== '\\') inSingle = !inSingle;else if (c === '"' && !inSingle && !inBacktick && prev !== '\\') inDouble = !inDouble;else if (c === '`' && !inSingle && !inDouble && prev !== '\\') inBacktick = !inBacktick;
|
|
1078
|
+
var inQuote = inSingle || inDouble || inBacktick;
|
|
1079
|
+
if (!inQuote) {
|
|
1080
|
+
if (c === '<') angle++;else if (c === '>') angle--;else if (c === '{') curly++;else if (c === '}') curly--;else if (c === '[') square++;else if (c === ']') square--;else if (c === '(') paren++;else if (c === ')') paren--;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return -1;
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Strips balanced outer parens, e.g. `(( T ))` -> `T`.
|
|
1087
|
+
* @param {string} type - Trimmed type string.
|
|
1088
|
+
* @returns {string} Unwrapped type.
|
|
1089
|
+
*/
|
|
1090
|
+
function stripOuterParens(type) {
|
|
1091
|
+
var changed = true;
|
|
1092
|
+
while (changed) {
|
|
1093
|
+
changed = false;
|
|
1094
|
+
if (type.length >= 2 && type[0] === '(' && type[type.length - 1] === ')') {
|
|
1095
|
+
var depth = 0;
|
|
1096
|
+
var balanced = true;
|
|
1097
|
+
var inSingle = false;
|
|
1098
|
+
var inDouble = false;
|
|
1099
|
+
for (var i = 0; i < type.length; i++) {
|
|
1100
|
+
var c = type[i];
|
|
1101
|
+
if (c === "'" && !inDouble && type[i - 1] !== '\\') inSingle = !inSingle;else if (c === '"' && !inSingle && type[i - 1] !== '\\') inDouble = !inDouble;else if (!inSingle && !inDouble) {
|
|
1102
|
+
if (c === '(') depth++;else if (c === ')') {
|
|
1103
|
+
depth--;
|
|
1104
|
+
if (depth === 0 && i !== type.length - 1) {
|
|
1105
|
+
balanced = false;
|
|
1106
|
+
break;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
if (balanced && depth === 0) {
|
|
1112
|
+
type = type.slice(1, -1).trim();
|
|
1113
|
+
changed = true;
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
return type;
|
|
1118
|
+
}
|
|
971
1119
|
/**
|
|
972
1120
|
* Parses `Name<A, B>` into name + raw arg strings, respecting nested brackets.
|
|
973
1121
|
* Returns undefined when input isn't a generic reference.
|
|
@@ -1012,6 +1160,190 @@
|
|
|
1012
1160
|
args: args
|
|
1013
1161
|
};
|
|
1014
1162
|
}
|
|
1163
|
+
/**
|
|
1164
|
+
* Parses `{[K in Iterable as NameType]?: Result}` mapped syntax.
|
|
1165
|
+
* Returns undefined when input isn't a mapped type.
|
|
1166
|
+
* @param {string} type - Trimmed type string starting with `{`.
|
|
1167
|
+
* @returns {object|undefined} Mapping struct with raw strings, or undefined.
|
|
1168
|
+
*/
|
|
1169
|
+
function parseMappedRaw(type) {
|
|
1170
|
+
if (type[0] !== '{' || type[type.length - 1] !== '}') {
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
var inner = type.slice(1, -1).trim();
|
|
1174
|
+
var readonly;
|
|
1175
|
+
if (inner.startsWith('-readonly') && (inner[9] === ' ' || inner[9] === '[')) {
|
|
1176
|
+
readonly = '-';
|
|
1177
|
+
inner = inner.slice(9).trim();
|
|
1178
|
+
} else if (inner.startsWith('+readonly') && (inner[9] === ' ' || inner[9] === '[')) {
|
|
1179
|
+
readonly = '+';
|
|
1180
|
+
inner = inner.slice(9).trim();
|
|
1181
|
+
} else if (inner.startsWith('readonly') && (inner[8] === ' ' || inner[8] === '[')) {
|
|
1182
|
+
readonly = 'readonly';
|
|
1183
|
+
inner = inner.slice(8).trim();
|
|
1184
|
+
}
|
|
1185
|
+
if (inner[0] !== '[') {
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
// Find matching `]` for the opening `[`, quote-aware.
|
|
1189
|
+
var square = 0;
|
|
1190
|
+
var inSingle = false;
|
|
1191
|
+
var inDouble = false;
|
|
1192
|
+
var inBacktick = false;
|
|
1193
|
+
var close = -1;
|
|
1194
|
+
for (var i = 0; i < inner.length; i++) {
|
|
1195
|
+
var c = inner[i];
|
|
1196
|
+
var prev = i > 0 ? inner[i - 1] : '';
|
|
1197
|
+
if (c === "'" && !inDouble && !inBacktick && prev !== '\\') inSingle = !inSingle;else if (c === '"' && !inSingle && !inBacktick && prev !== '\\') inDouble = !inDouble;else if (c === '`' && !inSingle && !inDouble && prev !== '\\') inBacktick = !inBacktick;else if (!inSingle && !inDouble && !inBacktick) {
|
|
1198
|
+
if (c === '[') square++;else if (c === ']') {
|
|
1199
|
+
square--;
|
|
1200
|
+
if (square === 0) {
|
|
1201
|
+
close = i;
|
|
1202
|
+
break;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
if (close === -1) {
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
var bracket = inner.slice(1, close);
|
|
1211
|
+
var rest = inner.slice(close + 1).trim();
|
|
1212
|
+
var question;
|
|
1213
|
+
if (rest.startsWith('-?')) {
|
|
1214
|
+
question = '-';
|
|
1215
|
+
rest = rest.slice(2).trim();
|
|
1216
|
+
} else if (rest.startsWith('+?')) {
|
|
1217
|
+
question = '+';
|
|
1218
|
+
rest = rest.slice(2).trim();
|
|
1219
|
+
} else if (rest.startsWith('?')) {
|
|
1220
|
+
question = '?';
|
|
1221
|
+
rest = rest.slice(1).trim();
|
|
1222
|
+
}
|
|
1223
|
+
if (!rest.startsWith(':')) {
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
var resultRaw = rest.slice(1).trim();
|
|
1227
|
+
if (!resultRaw) {
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
var inIdx = findTopLevelKeyword(bracket, 'in');
|
|
1231
|
+
if (inIdx === -1) {
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
var element = bracket.slice(0, inIdx).trim();
|
|
1235
|
+
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(element)) {
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
var afterIn = bracket.slice(inIdx + 2).trim();
|
|
1239
|
+
if (!afterIn) {
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
var asIdx = findTopLevelKeyword(afterIn, 'as');
|
|
1243
|
+
var iterableRaw = afterIn;
|
|
1244
|
+
var nameRaw;
|
|
1245
|
+
if (asIdx !== -1) {
|
|
1246
|
+
iterableRaw = afterIn.slice(0, asIdx).trim();
|
|
1247
|
+
nameRaw = afterIn.slice(asIdx + 2).trim();
|
|
1248
|
+
if (!iterableRaw || !nameRaw) {
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
return {
|
|
1253
|
+
element: element,
|
|
1254
|
+
iterableRaw: iterableRaw,
|
|
1255
|
+
nameRaw: nameRaw,
|
|
1256
|
+
resultRaw: resultRaw,
|
|
1257
|
+
question: question,
|
|
1258
|
+
readonly: readonly
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Splits `Check extends Extends ? True : False` at top level.
|
|
1263
|
+
* Returns undefined when input isn't a conditional type.
|
|
1264
|
+
* @param {string} type - Trimmed type string.
|
|
1265
|
+
* @returns {{check: string, extends_: string, true_: string, false_: string}|undefined} Raw parts.
|
|
1266
|
+
*/
|
|
1267
|
+
function parseConditionRaw(type) {
|
|
1268
|
+
var qIdx = findTopLevelChar(type, '?');
|
|
1269
|
+
if (qIdx === -1) {
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
var beforeQ = type.slice(0, qIdx);
|
|
1273
|
+
var afterQ = type.slice(qIdx + 1);
|
|
1274
|
+
var extIdx = findTopLevelKeyword(beforeQ, 'extends');
|
|
1275
|
+
if (extIdx === -1) {
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
var colonIdx = findTopLevelChar(afterQ, ':');
|
|
1279
|
+
if (colonIdx === -1) {
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
var check = beforeQ.slice(0, extIdx).trim();
|
|
1283
|
+
var extends_ = beforeQ.slice(extIdx + 7).trim();
|
|
1284
|
+
var true_ = afterQ.slice(0, colonIdx).trim();
|
|
1285
|
+
var false_ = afterQ.slice(colonIdx + 1).trim();
|
|
1286
|
+
if (!check || !extends_ || !true_ || !false_) {
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
// Guard against `?.` optional chaining and `??` nullish coalescing.
|
|
1290
|
+
if (type[qIdx + 1] === '.' || type[qIdx + 1] === '?') {
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
return {
|
|
1294
|
+
check: check,
|
|
1295
|
+
extends_: extends_,
|
|
1296
|
+
true_: true_,
|
|
1297
|
+
false_: false_
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Splits `Object[Index]` at top level. Excludes tuples (`[...]`) and
|
|
1302
|
+
* empty-index arrays (`T[]`, handled earlier).
|
|
1303
|
+
* @param {string} type - Trimmed type string.
|
|
1304
|
+
* @returns {{objectRaw: string, indexRaw: string}|undefined} Raw parts.
|
|
1305
|
+
*/
|
|
1306
|
+
function parseIndexedAccessRaw(type) {
|
|
1307
|
+
if (!type.endsWith(']') || type.endsWith('[]')) {
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
if (type[0] === '[') {
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
var openIdx = findTopLevelChar(type, '[');
|
|
1314
|
+
if (openIdx === -1) {
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
// Ensure the `[` at openIdx closes at the very end.
|
|
1318
|
+
var square = 0;
|
|
1319
|
+
var inSingle = false;
|
|
1320
|
+
var inDouble = false;
|
|
1321
|
+
var inBacktick = false;
|
|
1322
|
+
for (var i = openIdx; i < type.length; i++) {
|
|
1323
|
+
var c = type[i];
|
|
1324
|
+
var prev = i > 0 ? type[i - 1] : '';
|
|
1325
|
+
if (c === "'" && !inDouble && !inBacktick && prev !== '\\') inSingle = !inSingle;else if (c === '"' && !inSingle && !inBacktick && prev !== '\\') inDouble = !inDouble;else if (c === '`' && !inSingle && !inDouble && prev !== '\\') inBacktick = !inBacktick;else if (!inSingle && !inDouble && !inBacktick) {
|
|
1326
|
+
if (c === '[') square++;else if (c === ']') {
|
|
1327
|
+
square--;
|
|
1328
|
+
if (square === 0 && i !== type.length - 1) {
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
if (square !== 0) {
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
var objectRaw = type.slice(0, openIdx).trim();
|
|
1338
|
+
var indexRaw = type.slice(openIdx + 1, -1).trim();
|
|
1339
|
+
if (!objectRaw || !indexRaw) {
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
return {
|
|
1343
|
+
objectRaw: objectRaw,
|
|
1344
|
+
indexRaw: indexRaw
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1015
1347
|
/**
|
|
1016
1348
|
* 'DepFree' refers to the fact that this function has no dependencies,
|
|
1017
1349
|
* while `expandType` depends on TypeScript itself for maximum compatibility.
|
|
@@ -1027,6 +1359,33 @@
|
|
|
1027
1359
|
*/
|
|
1028
1360
|
function expandTypeDepFree(type) {
|
|
1029
1361
|
type = type.trim();
|
|
1362
|
+
// '(123)' -> '123': strip balanced outer parens first so `(cond)?`
|
|
1363
|
+
// nullable and `(A|B)` unions see through them.
|
|
1364
|
+
var stripped = stripOuterParens(type);
|
|
1365
|
+
if (stripped !== type) {
|
|
1366
|
+
return expandTypeDepFree(stripped);
|
|
1367
|
+
}
|
|
1368
|
+
// `readonly T` erased at runtime, same shape as the inner type.
|
|
1369
|
+
if (type.startsWith('readonly ') && type.length > 9) {
|
|
1370
|
+
return expandTypeDepFree(type.slice(9).trim());
|
|
1371
|
+
}
|
|
1372
|
+
if (type === 'unique symbol') {
|
|
1373
|
+
return 'any';
|
|
1374
|
+
}
|
|
1375
|
+
// Conditionals before nullable: `A extends B ? C : D?` must keep the
|
|
1376
|
+
// nullable on the false branch, not lift it over the whole condition.
|
|
1377
|
+
// (`(cond)?` has no top-level `?` due to parens, so it falls through
|
|
1378
|
+
// to nullable below.)
|
|
1379
|
+
var earlyCond = parseConditionRaw(type);
|
|
1380
|
+
if (earlyCond) {
|
|
1381
|
+
return {
|
|
1382
|
+
type: 'condition',
|
|
1383
|
+
checkType: expandTypeDepFree(earlyCond.check),
|
|
1384
|
+
extendsType: expandTypeDepFree(earlyCond.extends_),
|
|
1385
|
+
trueType: expandTypeDepFree(earlyCond.true_),
|
|
1386
|
+
falseType: expandTypeDepFree(earlyCond.false_)
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1030
1389
|
// JSDocNullableType (`T?` / `?T`): union with null, matching expandType().
|
|
1031
1390
|
if (type.endsWith('?') && type.length > 1) {
|
|
1032
1391
|
return {
|
|
@@ -1040,20 +1399,9 @@
|
|
|
1040
1399
|
members: [expandTypeDepFree(type.slice(1).trim()), 'null']
|
|
1041
1400
|
};
|
|
1042
1401
|
}
|
|
1043
|
-
// `readonly T` erased at runtime, same shape as the inner type.
|
|
1044
|
-
if (type.startsWith('readonly ') && type.length > 9) {
|
|
1045
|
-
return expandTypeDepFree(type.slice(9).trim());
|
|
1046
|
-
}
|
|
1047
|
-
if (type === 'unique symbol') {
|
|
1048
|
-
return 'any';
|
|
1049
|
-
}
|
|
1050
|
-
// '(123)' -> '123'
|
|
1051
|
-
while (!type.includes('|') && type[0] === '(' && type[type.length - 1] === ')') {
|
|
1052
|
-
type = type.slice(1, -1).trim();
|
|
1053
|
-
}
|
|
1054
1402
|
// (1) Rest parameters like ...string
|
|
1055
1403
|
if (type[0] === '.' && type[1] === '.' && type[2] === '.') {
|
|
1056
|
-
var elementType = type.slice(3);
|
|
1404
|
+
var elementType = expandTypeDepFree(type.slice(3).trim());
|
|
1057
1405
|
return {
|
|
1058
1406
|
type: 'array',
|
|
1059
1407
|
elementType: elementType
|
|
@@ -1081,17 +1429,18 @@
|
|
|
1081
1429
|
// (3) Object<...> or Record<...>
|
|
1082
1430
|
if ((type.startsWith("Object<") || type.startsWith("Record<")) && type.endsWith('>')) {
|
|
1083
1431
|
var recordSlice = type.slice(7, -1);
|
|
1084
|
-
var
|
|
1085
|
-
if (
|
|
1432
|
+
var commaIdx = findTopLevelChar(recordSlice, ',');
|
|
1433
|
+
if (commaIdx === -1) {
|
|
1086
1434
|
console.warn("expandTypeDepFree> invalid Object/Record");
|
|
1435
|
+
} else {
|
|
1436
|
+
var key = recordSlice.slice(0, commaIdx).trim();
|
|
1437
|
+
var val = recordSlice.slice(commaIdx + 1).trim();
|
|
1438
|
+
return {
|
|
1439
|
+
type: "record",
|
|
1440
|
+
key: expandTypeDepFree(key),
|
|
1441
|
+
val: expandTypeDepFree(val)
|
|
1442
|
+
};
|
|
1087
1443
|
}
|
|
1088
|
-
var key = recordSlice.slice(0, firstComma).trim();
|
|
1089
|
-
var val = recordSlice.slice(firstComma + 1).trim();
|
|
1090
|
-
return {
|
|
1091
|
-
type: "record",
|
|
1092
|
-
key: expandTypeDepFree(key),
|
|
1093
|
-
val: expandTypeDepFree(val)
|
|
1094
|
-
};
|
|
1095
1444
|
}
|
|
1096
1445
|
// (3b) Map<...> / Set<...' for dep-free parity with expandType()
|
|
1097
1446
|
if (type.startsWith("Map<") && type.endsWith('>')) {
|
|
@@ -1124,40 +1473,109 @@
|
|
|
1124
1473
|
args: args.map(expandTypeDepFree)
|
|
1125
1474
|
};
|
|
1126
1475
|
}
|
|
1127
|
-
// (4) {
|
|
1476
|
+
// (4) Mapped types `{[K in X as Y]?: Z}` before the object-literal fallback.
|
|
1128
1477
|
if (type[0] === '{' && type[type.length - 1] === '}') {
|
|
1129
|
-
var
|
|
1478
|
+
var mapped = parseMappedRaw(type);
|
|
1479
|
+
if (mapped) {
|
|
1480
|
+
var out = {
|
|
1481
|
+
type: 'mapping',
|
|
1482
|
+
iterable: expandTypeDepFree(mapped.iterableRaw),
|
|
1483
|
+
element: mapped.element,
|
|
1484
|
+
result: expandTypeDepFree(mapped.resultRaw)
|
|
1485
|
+
};
|
|
1486
|
+
if (mapped.nameRaw !== undefined) {
|
|
1487
|
+
out.nameType = expandTypeDepFree(mapped.nameRaw);
|
|
1488
|
+
}
|
|
1489
|
+
if (mapped.question !== undefined) {
|
|
1490
|
+
out.question = mapped.question;
|
|
1491
|
+
}
|
|
1492
|
+
if (mapped.readonly !== undefined) {
|
|
1493
|
+
out.readonly = mapped.readonly;
|
|
1494
|
+
}
|
|
1495
|
+
return out;
|
|
1496
|
+
}
|
|
1497
|
+
var propertiesArray = splitTopLevel(type.slice(1, -1), ','); // ['entity: Entity', ' app: AppBase']
|
|
1130
1498
|
var properties = {};
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1499
|
+
var _iterator = _createForOfIteratorHelper(propertiesArray),
|
|
1500
|
+
_step;
|
|
1501
|
+
try {
|
|
1502
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
1503
|
+
var entry = _step.value;
|
|
1504
|
+
var colonIdx = findTopLevelChar(entry, ':');
|
|
1505
|
+
if (colonIdx === -1) {
|
|
1506
|
+
// Empty `{}` yields one empty entry: the empty object type.
|
|
1507
|
+
if (entry.trim() === '') {
|
|
1508
|
+
continue;
|
|
1509
|
+
}
|
|
1510
|
+
console.warn('expandTypeDepFree> unexpected type format, fix');
|
|
1511
|
+
continue;
|
|
1512
|
+
}
|
|
1513
|
+
var propName = entry.slice(0, colonIdx).trim();
|
|
1514
|
+
var propTypeRaw = entry.slice(colonIdx + 1).trim();
|
|
1515
|
+
if (!propName || !propTypeRaw) {
|
|
1516
|
+
console.warn('expandTypeDepFree> unexpected type format, fix');
|
|
1517
|
+
continue;
|
|
1518
|
+
}
|
|
1519
|
+
properties[propName] = expandTypeDepFree(propTypeRaw);
|
|
1141
1520
|
}
|
|
1142
|
-
properties
|
|
1143
|
-
|
|
1521
|
+
// Bare `{}` carries no `properties` key, matching the TS/Babel parsers
|
|
1522
|
+
// (and distinguishing it from the `object` keyword, which does).
|
|
1523
|
+
} catch (err) {
|
|
1524
|
+
_iterator.e(err);
|
|
1525
|
+
} finally {
|
|
1526
|
+
_iterator.f();
|
|
1527
|
+
}
|
|
1528
|
+
if (!Object.keys(properties).length) {
|
|
1529
|
+
return {
|
|
1530
|
+
type: 'object'
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1144
1533
|
return {
|
|
1145
1534
|
type: 'object',
|
|
1146
1535
|
properties: properties
|
|
1147
1536
|
};
|
|
1148
1537
|
}
|
|
1149
|
-
// (5)
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
});
|
|
1538
|
+
// (5) Unions and intersections via top-level splits (`&` binds tighter, so `|` first).
|
|
1539
|
+
// Note: conditionals already handled up front (before nullable) so `D?`
|
|
1540
|
+
// false branches keep their nullability; this slot is intentionally union-only.
|
|
1541
|
+
var unionParts = splitTopLevel(type, "|");
|
|
1542
|
+
if (unionParts.length >= 2) {
|
|
1155
1543
|
return {
|
|
1156
1544
|
type: 'union',
|
|
1157
|
-
members:
|
|
1545
|
+
members: unionParts.map(function (_) {
|
|
1546
|
+
return expandTypeDepFree(_.trim());
|
|
1547
|
+
})
|
|
1548
|
+
};
|
|
1549
|
+
}
|
|
1550
|
+
var interParts = splitTopLevel(type, "&");
|
|
1551
|
+
if (interParts.length >= 2) {
|
|
1552
|
+
return {
|
|
1553
|
+
type: 'intersection',
|
|
1554
|
+
members: interParts.map(function (_) {
|
|
1555
|
+
return expandTypeDepFree(_.trim());
|
|
1556
|
+
})
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
// (7) `keyof T` before typeof so `keyof typeof X` nests correctly.
|
|
1560
|
+
if (type.startsWith('keyof ') || type.startsWith('keyof(')) {
|
|
1561
|
+
var after = type.startsWith('keyof(') ? type.slice(5).trim() : type.slice(6).trim();
|
|
1562
|
+
if (after) {
|
|
1563
|
+
return {
|
|
1564
|
+
type: 'keyof',
|
|
1565
|
+
argument: expandTypeDepFree(after)
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
// (8) Indexed access `T[K]` (arrays `T[]` handled below, tuples above start with `[`).
|
|
1570
|
+
var indexed = parseIndexedAccessRaw(type);
|
|
1571
|
+
if (indexed) {
|
|
1572
|
+
return {
|
|
1573
|
+
type: 'indexedAccess',
|
|
1574
|
+
index: expandTypeDepFree(indexed.indexRaw),
|
|
1575
|
+
object: expandTypeDepFree(indexed.objectRaw)
|
|
1158
1576
|
};
|
|
1159
1577
|
}
|
|
1160
|
-
// (
|
|
1578
|
+
// (9) expand [] Arrays
|
|
1161
1579
|
// Test arrays: new pc.Mat3().set([1, 2, 3, "asd"])
|
|
1162
1580
|
if (type.endsWith("[]")) {
|
|
1163
1581
|
var _typeSlice2 = type.slice(0, -2);
|
|
@@ -1166,17 +1584,26 @@
|
|
|
1166
1584
|
elementType: expandTypeDepFree(_typeSlice2)
|
|
1167
1585
|
};
|
|
1168
1586
|
}
|
|
1169
|
-
// (
|
|
1587
|
+
// (10) expand tuples
|
|
1170
1588
|
if (type[0] === '[' && type[type.length - 1] === ']') {
|
|
1171
|
-
var
|
|
1589
|
+
var _inner2 = type.slice(1, -1);
|
|
1590
|
+
if (_inner2.trim() === '') {
|
|
1591
|
+
return {
|
|
1592
|
+
type: 'tuple',
|
|
1593
|
+
elements: []
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
var elements = splitTopLevel(_inner2, ','); // ['null', ' Texture', ...]
|
|
1172
1597
|
return {
|
|
1173
1598
|
type: 'tuple',
|
|
1174
|
-
elements: elements.map(
|
|
1599
|
+
elements: elements.map(function (_) {
|
|
1600
|
+
return expandTypeDepFree(_.trim());
|
|
1601
|
+
})
|
|
1175
1602
|
};
|
|
1176
1603
|
}
|
|
1177
|
-
// (
|
|
1604
|
+
// (11) expand typeof expressions
|
|
1178
1605
|
if (type.startsWith('typeof ')) {
|
|
1179
|
-
var argument = expandTypeDepFree(type.substring(7));
|
|
1606
|
+
var argument = expandTypeDepFree(type.substring(7).trim());
|
|
1180
1607
|
return {
|
|
1181
1608
|
type: 'typeof',
|
|
1182
1609
|
argument: argument
|
|
@@ -1202,79 +1629,6 @@
|
|
|
1202
1629
|
return type;
|
|
1203
1630
|
}
|
|
1204
1631
|
|
|
1205
|
-
/**
|
|
1206
|
-
* Infers a parameter type from its default value AST node.
|
|
1207
|
-
* Returns widened types like TypeScript does (`= 0` means `number`, not
|
|
1208
|
-
* literal `0`; `= null` widens to `any`).
|
|
1209
|
-
* Returns `undefined` when nothing useful can be inferred — the caller then
|
|
1210
|
-
* emits no check, exactly like an undocumented parameter today.
|
|
1211
|
-
* Shapes match what `expandType` produces so they can be embedded as-is.
|
|
1212
|
-
* @param {import('@babel/types').Node} node - The default value AST node.
|
|
1213
|
-
* @returns {string | object | undefined} Inferred type or `undefined` to skip.
|
|
1214
|
-
*/
|
|
1215
|
-
function inferTypeFromDefault$1(node) {
|
|
1216
|
-
if (!node) {
|
|
1217
|
-
return;
|
|
1218
|
-
}
|
|
1219
|
-
switch (node.type) {
|
|
1220
|
-
case 'NumericLiteral':
|
|
1221
|
-
return 'number';
|
|
1222
|
-
case 'StringLiteral':
|
|
1223
|
-
return 'string';
|
|
1224
|
-
case 'BooleanLiteral':
|
|
1225
|
-
return 'boolean';
|
|
1226
|
-
case 'BigIntLiteral':
|
|
1227
|
-
return {
|
|
1228
|
-
type: 'bigint'
|
|
1229
|
-
};
|
|
1230
|
-
case 'RegExpLiteral':
|
|
1231
|
-
return 'RegExp';
|
|
1232
|
-
case 'TemplateLiteral':
|
|
1233
|
-
return 'string';
|
|
1234
|
-
case 'ArrayExpression':
|
|
1235
|
-
return {
|
|
1236
|
-
type: 'array',
|
|
1237
|
-
elementType: 'any'
|
|
1238
|
-
};
|
|
1239
|
-
case 'ObjectExpression':
|
|
1240
|
-
return {
|
|
1241
|
-
type: 'object',
|
|
1242
|
-
properties: {}
|
|
1243
|
-
};
|
|
1244
|
-
case 'ArrowFunctionExpression':
|
|
1245
|
-
case 'FunctionExpression':
|
|
1246
|
-
return 'Function';
|
|
1247
|
-
case 'NewExpression':
|
|
1248
|
-
{
|
|
1249
|
-
var callee = node.callee;
|
|
1250
|
-
if (callee.type === 'Identifier') {
|
|
1251
|
-
return callee.name;
|
|
1252
|
-
}
|
|
1253
|
-
break;
|
|
1254
|
-
}
|
|
1255
|
-
case 'UnaryExpression':
|
|
1256
|
-
{
|
|
1257
|
-
var operator = node.operator,
|
|
1258
|
-
argument = node.argument;
|
|
1259
|
-
if (operator === '!') {
|
|
1260
|
-
return 'boolean';
|
|
1261
|
-
}
|
|
1262
|
-
if (operator === 'void' || operator === 'typeof') {
|
|
1263
|
-
return operator === 'void' ? 'undefined' : 'string';
|
|
1264
|
-
}
|
|
1265
|
-
if ((operator === '-' || operator === '+') && argument.type === 'NumericLiteral') {
|
|
1266
|
-
return 'number';
|
|
1267
|
-
}
|
|
1268
|
-
if ((operator === '-' || operator === '+') && argument.type === 'BigIntLiteral') {
|
|
1269
|
-
return {
|
|
1270
|
-
type: 'bigint'
|
|
1271
|
-
};
|
|
1272
|
-
}
|
|
1273
|
-
break;
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
1632
|
/**
|
|
1279
1633
|
* Extracts the parameter name and its optionality from a JSDoc parameter string.
|
|
1280
1634
|
*
|
|
@@ -1339,73 +1693,863 @@
|
|
|
1339
1693
|
*
|
|
1340
1694
|
* It iterates through the lines of a `CommentBlock` from the Babel AST, looking for `@typedef` and `@property`
|
|
1341
1695
|
* annotations. When it finds a typedef, it stores it in the `typedefs` record. When it finds a property,
|
|
1342
|
-
* it adds it to the last found typedef if it is an object type.
|
|
1696
|
+
* it adds it to the last found typedef if it is an object type. `@template` names preceding a
|
|
1697
|
+
* typedef are recorded in `typedefTemplates` so generic references can instantiate.
|
|
1343
1698
|
* @param {Record<string, object>} typedefs - An object to store typedefs, mapping type names to their expanded definitions.
|
|
1699
|
+
* @param {Record<string, string[]>} typedefTemplates - An object to store template parameter names per generic typedef.
|
|
1344
1700
|
* @param {Console["warn"]} warn - A warn function used for emitting warnings about non-extensible types.
|
|
1345
1701
|
* @param {import("@babel/types").Comment} comment - A comment extracted from Babel's AST, expected to be a CommentBlock containing type definitions.
|
|
1346
1702
|
* @param {Function} expandType - A function that takes a type expression as a string and returns a structured representation of the type.
|
|
1347
1703
|
*/
|
|
1348
|
-
function parseJSDocTypedef(typedefs, warn, comment, expandType) {
|
|
1349
|
-
var type = comment.type,
|
|
1350
|
-
value = comment.value;
|
|
1351
|
-
if (type !== 'CommentBlock') {
|
|
1704
|
+
function parseJSDocTypedef(typedefs, typedefTemplates, warn, comment, expandType) {
|
|
1705
|
+
var type = comment.type,
|
|
1706
|
+
value = comment.value;
|
|
1707
|
+
if (type !== 'CommentBlock') {
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
var lines = value.split('\n');
|
|
1711
|
+
var lastTypedef;
|
|
1712
|
+
var pendingTemplates = [];
|
|
1713
|
+
/**
|
|
1714
|
+
* @param {string} line - The trimmed JSDoc line.
|
|
1715
|
+
* @returns {boolean} True when the line held a template tag.
|
|
1716
|
+
*/
|
|
1717
|
+
function harvestTemplate(line) {
|
|
1718
|
+
var match = line.match(/@template \{(.*?)\} ([a-zA-Z0-9_$]+)/);
|
|
1719
|
+
if (match) {
|
|
1720
|
+
pendingTemplates.push(match[2]);
|
|
1721
|
+
return true;
|
|
1722
|
+
}
|
|
1723
|
+
match = line.match(/@template (?:\{.*?\} )?\[([a-zA-Z0-9_$]+)=/);
|
|
1724
|
+
if (match) {
|
|
1725
|
+
pendingTemplates.push(match[1]);
|
|
1726
|
+
return true;
|
|
1727
|
+
}
|
|
1728
|
+
match = line.match(/@template ([a-zA-Z0-9_$]+)(?![a-zA-Z0-9_$])/);
|
|
1729
|
+
if (match) {
|
|
1730
|
+
pendingTemplates.push(match[1]);
|
|
1731
|
+
return true;
|
|
1732
|
+
}
|
|
1733
|
+
return false;
|
|
1734
|
+
}
|
|
1735
|
+
var _iterator = _createForOfIteratorHelper(lines),
|
|
1736
|
+
_step;
|
|
1737
|
+
try {
|
|
1738
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
1739
|
+
var line = _step.value;
|
|
1740
|
+
line = line.trim();
|
|
1741
|
+
if (line[0] === '*') {
|
|
1742
|
+
line = line.slice(1).trim();
|
|
1743
|
+
}
|
|
1744
|
+
if (line.startsWith('@template')) {
|
|
1745
|
+
harvestTemplate(line);
|
|
1746
|
+
} else if (line.startsWith('@typedef')) {
|
|
1747
|
+
var _extractCurlyContent = extractCurlyContent(line),
|
|
1748
|
+
def = _extractCurlyContent.content,
|
|
1749
|
+
nextIndex = _extractCurlyContent.nextIndex;
|
|
1750
|
+
var name = line.substring(nextIndex).trim();
|
|
1751
|
+
// Drop description
|
|
1752
|
+
name = name.split(' ')[0];
|
|
1753
|
+
lastTypedef = expandType(def);
|
|
1754
|
+
// Ignore @typedef's that only refer to themselves in another file (see typedef-overwrite test)
|
|
1755
|
+
if (lastTypedef !== name) {
|
|
1756
|
+
typedefs[name] = lastTypedef;
|
|
1757
|
+
if (pendingTemplates.length) {
|
|
1758
|
+
typedefTemplates[name] = _toConsumableArray(pendingTemplates);
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
pendingTemplates = [];
|
|
1762
|
+
} else if (line.startsWith('@property')) {
|
|
1763
|
+
var _lastTypedef;
|
|
1764
|
+
// class @property
|
|
1765
|
+
if (!lastTypedef) {
|
|
1766
|
+
continue;
|
|
1767
|
+
}
|
|
1768
|
+
var _extractCurlyContent2 = extractCurlyContent(line),
|
|
1769
|
+
content = _extractCurlyContent2.content,
|
|
1770
|
+
_nextIndex = _extractCurlyContent2.nextIndex;
|
|
1771
|
+
var rest = line.substring(_nextIndex);
|
|
1772
|
+
var propType = expandType(content);
|
|
1773
|
+
var _extractNameAndOption = extractNameAndOptionality(rest),
|
|
1774
|
+
_extractNameAndOption2 = _slicedToArray(_extractNameAndOption, 2),
|
|
1775
|
+
_name = _extractNameAndOption2[0],
|
|
1776
|
+
optional = _extractNameAndOption2[1];
|
|
1777
|
+
// console.log({name, optional, propType});
|
|
1778
|
+
var finalType = annotateOptional(propType, optional);
|
|
1779
|
+
if (((_lastTypedef = lastTypedef) === null || _lastTypedef === void 0 ? void 0 : _lastTypedef.type) === 'object') {
|
|
1780
|
+
lastTypedef.properties[_name] = finalType;
|
|
1781
|
+
} else {
|
|
1782
|
+
warn("not an extensible type", lastTypedef);
|
|
1783
|
+
}
|
|
1784
|
+
} else if (line.startsWith('@callback')) {
|
|
1785
|
+
var _name2 = line.substring(9).trim();
|
|
1786
|
+
typedefs[_name2] = 'Function';
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
} catch (err) {
|
|
1790
|
+
_iterator.e(err);
|
|
1791
|
+
} finally {
|
|
1792
|
+
_iterator.f();
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
/**
|
|
1797
|
+
* @typedef {ReturnType<typeof parseJSDoc>} ParseJSDocReturnType
|
|
1798
|
+
*/
|
|
1799
|
+
/**
|
|
1800
|
+
* @typedef {typeof expandTypeDepFree} ExpandType
|
|
1801
|
+
* @typedef {ReturnType<ExpandType>} ExpandTypeReturnType
|
|
1802
|
+
*/
|
|
1803
|
+
/**
|
|
1804
|
+
* Parses JSDoc comments to extract parameter type information.
|
|
1805
|
+
*
|
|
1806
|
+
* @param {string} src - The JSDoc comment string to parse.
|
|
1807
|
+
* @param {ExpandType} [expandType] - An optional function to process the types found in the JSDoc.
|
|
1808
|
+
* @returns {Record<string, ExpandTypeReturnType> | undefined} An object mapping parameter names to their parsed types, or undefined if no parameters are found.
|
|
1809
|
+
*/
|
|
1810
|
+
function parseJSDoc(src) {
|
|
1811
|
+
var expandType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : expandTypeDepFree;
|
|
1812
|
+
// Parse something like: @param {Object} [kwargs={}] Optional arguments.
|
|
1813
|
+
var regex = /@param \{(.*?)\} ([\[\]a-zA-Z0-9_$=\-\{\}\.'" ]+)/g;
|
|
1814
|
+
var matches = _toConsumableArray(src.matchAll(regex));
|
|
1815
|
+
/** @type {Record<string, ExpandTypeReturnType>} */
|
|
1816
|
+
var params = Object.create(null);
|
|
1817
|
+
matches.forEach(function (_) {
|
|
1818
|
+
var type = expandType(_[1].trim());
|
|
1819
|
+
var name = _[2].trim();
|
|
1820
|
+
var optional = false;
|
|
1821
|
+
// Examples:
|
|
1822
|
+
// name: [kwargs={}] The configuration parameters.
|
|
1823
|
+
// name: [d = 1.0] Sample spacing
|
|
1824
|
+
if (name[0] === '[') {
|
|
1825
|
+
// Counting opening/closing brackets for perfect match
|
|
1826
|
+
var openCloseCount = 1;
|
|
1827
|
+
var i = 1;
|
|
1828
|
+
for (; i < name.length; i++) {
|
|
1829
|
+
var c = name[i];
|
|
1830
|
+
if (c === '[') {
|
|
1831
|
+
openCloseCount++;
|
|
1832
|
+
} else if (c === ']') {
|
|
1833
|
+
openCloseCount--;
|
|
1834
|
+
}
|
|
1835
|
+
if (openCloseCount === 0) {
|
|
1836
|
+
break;
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
// Afterwards name will be: d = 1.0
|
|
1840
|
+
name = name.substring(1, i);
|
|
1841
|
+
// mark it for the type:
|
|
1842
|
+
optional = true;
|
|
1843
|
+
}
|
|
1844
|
+
// Strip the rest (either leftover of optional value or description)
|
|
1845
|
+
name = name.split(' ')[0].split('=')[0].trim();
|
|
1846
|
+
var annotatedType = annotateOptional(type, optional);
|
|
1847
|
+
// Turn "options.stats[].unitsName" into ['options', 'stats', 'unitsName'].
|
|
1848
|
+
var parts = name.split(/[\[\]]*\./);
|
|
1849
|
+
var properties = params;
|
|
1850
|
+
var _iterator = _createForOfIteratorHelper(parts),
|
|
1851
|
+
_step;
|
|
1852
|
+
try {
|
|
1853
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
1854
|
+
var part = _step.value;
|
|
1855
|
+
var toptype = properties[part];
|
|
1856
|
+
if (!toptype) {
|
|
1857
|
+
// No toptype means we resolved as far as possible, now we can add `annotatedType`.
|
|
1858
|
+
console.assert(part === parts.at(-1), 'Current part and last part should be the same.');
|
|
1859
|
+
properties[part] = annotatedType;
|
|
1860
|
+
} else if (toptype.type === "union") {
|
|
1861
|
+
var typeObject = toptype.members.find(function (_) {
|
|
1862
|
+
return (_ === null || _ === void 0 ? void 0 : _.type) === 'object';
|
|
1863
|
+
});
|
|
1864
|
+
properties = typeObject.properties;
|
|
1865
|
+
} else if (toptype.type === "array") {
|
|
1866
|
+
properties = toptype.elementType.properties;
|
|
1867
|
+
} else if (toptype.type === "object") {
|
|
1868
|
+
toptype.properties = toptype.properties || Object.create(null);
|
|
1869
|
+
properties = toptype.properties;
|
|
1870
|
+
} else {
|
|
1871
|
+
console.warn("parseJSDoc> Skipping @param, unseen syntax detected. Please check if your JSDoc is valid or open an issue about this!", {
|
|
1872
|
+
src: src,
|
|
1873
|
+
toptype: toptype,
|
|
1874
|
+
parts: parts,
|
|
1875
|
+
annotatedType: annotatedType
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
} catch (err) {
|
|
1880
|
+
_iterator.e(err);
|
|
1881
|
+
} finally {
|
|
1882
|
+
_iterator.f();
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1885
|
+
if (Object.keys(params).length === 0) {
|
|
1886
|
+
return;
|
|
1887
|
+
}
|
|
1888
|
+
return params;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
/**
|
|
1892
|
+
* Infers a parameter type from its default value AST node.
|
|
1893
|
+
* Returns widened types like TypeScript does (`= 0` means `number`, not
|
|
1894
|
+
* literal `0`; `= null` widens to `any`).
|
|
1895
|
+
* Returns `undefined` when nothing useful can be inferred — the caller then
|
|
1896
|
+
* emits no check, exactly like an undocumented parameter today.
|
|
1897
|
+
* Shapes match what `expandType` produces so they can be embedded as-is.
|
|
1898
|
+
* @param {import('@babel/types').Node} node - The default value AST node.
|
|
1899
|
+
* @returns {string | object | undefined} Inferred type or `undefined` to skip.
|
|
1900
|
+
*/
|
|
1901
|
+
function inferTypeFromDefault$1(node) {
|
|
1902
|
+
if (!node) {
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
switch (node.type) {
|
|
1906
|
+
case 'NumericLiteral':
|
|
1907
|
+
return 'number';
|
|
1908
|
+
case 'StringLiteral':
|
|
1909
|
+
return 'string';
|
|
1910
|
+
case 'BooleanLiteral':
|
|
1911
|
+
return 'boolean';
|
|
1912
|
+
case 'BigIntLiteral':
|
|
1913
|
+
return {
|
|
1914
|
+
type: 'bigint'
|
|
1915
|
+
};
|
|
1916
|
+
case 'RegExpLiteral':
|
|
1917
|
+
return 'RegExp';
|
|
1918
|
+
case 'TemplateLiteral':
|
|
1919
|
+
return 'string';
|
|
1920
|
+
case 'ArrayExpression':
|
|
1921
|
+
return {
|
|
1922
|
+
type: 'array',
|
|
1923
|
+
elementType: 'any'
|
|
1924
|
+
};
|
|
1925
|
+
case 'ObjectExpression':
|
|
1926
|
+
return {
|
|
1927
|
+
type: 'object',
|
|
1928
|
+
properties: {}
|
|
1929
|
+
};
|
|
1930
|
+
case 'ArrowFunctionExpression':
|
|
1931
|
+
case 'FunctionExpression':
|
|
1932
|
+
return 'Function';
|
|
1933
|
+
case 'NewExpression':
|
|
1934
|
+
{
|
|
1935
|
+
var callee = node.callee;
|
|
1936
|
+
if (callee.type === 'Identifier') {
|
|
1937
|
+
return callee.name;
|
|
1938
|
+
}
|
|
1939
|
+
break;
|
|
1940
|
+
}
|
|
1941
|
+
case 'UnaryExpression':
|
|
1942
|
+
{
|
|
1943
|
+
var operator = node.operator,
|
|
1944
|
+
argument = node.argument;
|
|
1945
|
+
if (operator === '!') {
|
|
1946
|
+
return 'boolean';
|
|
1947
|
+
}
|
|
1948
|
+
if (operator === 'void' || operator === 'typeof') {
|
|
1949
|
+
return operator === 'void' ? 'undefined' : 'string';
|
|
1950
|
+
}
|
|
1951
|
+
if ((operator === '-' || operator === '+') && argument.type === 'NumericLiteral') {
|
|
1952
|
+
return 'number';
|
|
1953
|
+
}
|
|
1954
|
+
if ((operator === '-' || operator === '+') && argument.type === 'BigIntLiteral') {
|
|
1955
|
+
return {
|
|
1956
|
+
type: 'bigint'
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
break;
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
// Declaration-site ranks: the field always beats constructor assignments,
|
|
1965
|
+
// like TSC where the declared type wins over assigned values.
|
|
1966
|
+
var FIELD_JSDOC = 3;
|
|
1967
|
+
var CTOR_JSDOC = 2;
|
|
1968
|
+
var FIELD_INFER = 1;
|
|
1969
|
+
var CTOR_INFER = 0;
|
|
1970
|
+
/**
|
|
1971
|
+
* Reads the last block comment attached to a node.
|
|
1972
|
+
* @param {*} node - Babel AST node.
|
|
1973
|
+
* @returns {string|undefined} Comment text or undefined.
|
|
1974
|
+
*/
|
|
1975
|
+
function lastBlockComment(node) {
|
|
1976
|
+
var list = node === null || node === void 0 ? void 0 : node.leadingComments;
|
|
1977
|
+
if (!list) {
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
for (var i = list.length - 1; i >= 0; i--) {
|
|
1981
|
+
if (list[i].type === 'CommentBlock') {
|
|
1982
|
+
return list[i].value;
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
/**
|
|
1987
|
+
* Extracts a `{...}`-typed JSDoc tag (`@type`, `@returns`) from a comment.
|
|
1988
|
+
* @param {string|undefined} comment - Comment text.
|
|
1989
|
+
* @param {string} tag - Tag name including `@`.
|
|
1990
|
+
* @param {Function} expandType - String type to structured type.
|
|
1991
|
+
* @returns {{type: *, raw: string}|undefined} Expanded type plus raw text.
|
|
1992
|
+
*/
|
|
1993
|
+
function tagType(comment, tag, expandType) {
|
|
1994
|
+
if (!comment) {
|
|
1995
|
+
return;
|
|
1996
|
+
}
|
|
1997
|
+
var idx = comment.search(new RegExp("".concat(tag, "(?=[\\s{])")));
|
|
1998
|
+
if (idx === -1) {
|
|
1999
|
+
return;
|
|
2000
|
+
}
|
|
2001
|
+
var slice = comment.slice(idx);
|
|
2002
|
+
if (!slice.includes('{')) {
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
try {
|
|
2006
|
+
var _extractCurlyContent = extractCurlyContent(slice),
|
|
2007
|
+
content = _extractCurlyContent.content;
|
|
2008
|
+
if (!content || !content.trim()) {
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
2011
|
+
return {
|
|
2012
|
+
type: expandType(content.trim()),
|
|
2013
|
+
raw: content.trim()
|
|
2014
|
+
};
|
|
2015
|
+
} catch (_unused) {
|
|
2016
|
+
// Unparseable annotation: fail open, harvest continues.
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* Reads a static property name: identifiers and string literals, including
|
|
2021
|
+
* computed `['name']` forms. Anything dynamic yields undefined.
|
|
2022
|
+
* @param {*} key - Babel key node.
|
|
2023
|
+
* @param {boolean} computed - Whether the key position is computed.
|
|
2024
|
+
* @returns {string|undefined} Property name or undefined.
|
|
2025
|
+
*/
|
|
2026
|
+
function keyName(key, computed) {
|
|
2027
|
+
if (!key) {
|
|
2028
|
+
return;
|
|
2029
|
+
}
|
|
2030
|
+
if (key.type === 'Identifier' && !computed) {
|
|
2031
|
+
return key.name;
|
|
2032
|
+
}
|
|
2033
|
+
if (key.type === 'StringLiteral') {
|
|
2034
|
+
return key.value;
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
/**
|
|
2038
|
+
* Unwraps parenthesized and TS `as`/`satisfies` nodes.
|
|
2039
|
+
* @param {*} node - Babel AST node.
|
|
2040
|
+
* @returns {*} Unwrapped node.
|
|
2041
|
+
*/
|
|
2042
|
+
function unwrap(node) {
|
|
2043
|
+
while (node && (node.type === 'ParenthesizedExpression' || node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression')) {
|
|
2044
|
+
node = node.expression;
|
|
2045
|
+
}
|
|
2046
|
+
return node;
|
|
2047
|
+
}
|
|
2048
|
+
/**
|
|
2049
|
+
* Merges a harvested entry: higher rank wins the type, flags accumulate
|
|
2050
|
+
* (`optional` reflects runtime reality, `@readonly` anywhere counts).
|
|
2051
|
+
* Two disagreeing humans (JSDoc vs JSDoc) warn, like a TSC error.
|
|
2052
|
+
* @param {Record<string, object>} fields - Collected entries by name.
|
|
2053
|
+
* @param {string} name - Property name.
|
|
2054
|
+
* @param {object} entry - Entry with type, rank, raw, readonly, optional.
|
|
2055
|
+
* @param {Function} warn - Transpile-time warning function.
|
|
2056
|
+
* @param {string} className - Class name for messages.
|
|
2057
|
+
*/
|
|
2058
|
+
function record(fields, name, entry, warn, className) {
|
|
2059
|
+
var existing = fields[name];
|
|
2060
|
+
if (!existing) {
|
|
2061
|
+
fields[name] = entry;
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
if (entry.jsdoc && existing.jsdoc && entry.raw !== existing.raw) {
|
|
2065
|
+
warn("harvestClassShape: ".concat(className, ".").concat(name, " has conflicting JSDoc types (").concat(existing.raw, " vs ").concat(entry.raw, ")"));
|
|
2066
|
+
}
|
|
2067
|
+
var winner = entry.rank >= existing.rank ? entry : existing;
|
|
2068
|
+
winner.readonly = existing.readonly || entry.readonly;
|
|
2069
|
+
winner.optional = existing.optional || entry.optional;
|
|
2070
|
+
fields[name] = winner;
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
2073
|
+
* Turns a collected entry into a type struct, applying flags.
|
|
2074
|
+
* @param {object} entry - Collected entry.
|
|
2075
|
+
* @returns {*} Type struct or name.
|
|
2076
|
+
*/
|
|
2077
|
+
function entryToType(entry) {
|
|
2078
|
+
var _entry$type;
|
|
2079
|
+
var t = (_entry$type = entry.type) !== null && _entry$type !== void 0 ? _entry$type : 'any';
|
|
2080
|
+
if (!entry.readonly && !entry.optional) {
|
|
2081
|
+
return t;
|
|
2082
|
+
}
|
|
2083
|
+
if (t && _typeof(t) === 'object') {
|
|
2084
|
+
return _objectSpread2(_objectSpread2(_objectSpread2({}, t), entry.readonly ? {
|
|
2085
|
+
readonly: true
|
|
2086
|
+
} : {}), entry.optional ? {
|
|
2087
|
+
optional: true
|
|
2088
|
+
} : {});
|
|
2089
|
+
}
|
|
2090
|
+
var out = {
|
|
2091
|
+
type: t
|
|
2092
|
+
};
|
|
2093
|
+
if (entry.readonly) {
|
|
2094
|
+
out.readonly = true;
|
|
2095
|
+
}
|
|
2096
|
+
if (entry.optional) {
|
|
2097
|
+
out.optional = true;
|
|
2098
|
+
}
|
|
2099
|
+
return out;
|
|
2100
|
+
}
|
|
2101
|
+
/**
|
|
2102
|
+
* Records `this.x = ...` (or `+=`, `++`) assignments.
|
|
2103
|
+
* @param {*} left - Assignment target.
|
|
2104
|
+
* @param {*} right - Assigned value or undefined for op-assign/update.
|
|
2105
|
+
* @param {string|undefined} comment - Leading comment text.
|
|
2106
|
+
* @param {boolean} conditional - True inside conditionals (counts as optional).
|
|
2107
|
+
* @param {object} ctx - Harvest context with fields, expandType, warn, className.
|
|
2108
|
+
*/
|
|
2109
|
+
function recordThisAssign(left, right, comment, conditional, ctx) {
|
|
2110
|
+
var _declared$type;
|
|
2111
|
+
var fields = ctx.fields,
|
|
2112
|
+
expandType = ctx.expandType,
|
|
2113
|
+
warn = ctx.warn,
|
|
2114
|
+
className = ctx.className;
|
|
2115
|
+
left = unwrap(left);
|
|
2116
|
+
if (!left || left.type !== 'MemberExpression' || left.computed) {
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
2119
|
+
if (!left.object || left.object.type !== 'ThisExpression') {
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2122
|
+
var name = keyName(left.property, false);
|
|
2123
|
+
if (name === undefined) {
|
|
2124
|
+
return;
|
|
2125
|
+
}
|
|
2126
|
+
var declared = tagType(comment, '@type', expandType);
|
|
2127
|
+
if (right === undefined) {
|
|
2128
|
+
// Op-assign/update (`+=`, `++`): exists, type unknown.
|
|
2129
|
+
record(fields, name, {
|
|
2130
|
+
type: 'any',
|
|
2131
|
+
rank: CTOR_INFER,
|
|
2132
|
+
jsdoc: false,
|
|
2133
|
+
optional: conditional
|
|
2134
|
+
}, warn, className);
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
var inferred = inferTypeFromDefault$1(unwrap(right));
|
|
2138
|
+
var type = (_declared$type = declared === null || declared === void 0 ? void 0 : declared.type) !== null && _declared$type !== void 0 ? _declared$type : inferred;
|
|
2139
|
+
if (type === undefined) {
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
record(fields, name, {
|
|
2143
|
+
type: type,
|
|
2144
|
+
rank: declared ? CTOR_JSDOC : CTOR_INFER,
|
|
2145
|
+
jsdoc: declared !== undefined,
|
|
2146
|
+
raw: declared === null || declared === void 0 ? void 0 : declared.raw,
|
|
2147
|
+
optional: conditional
|
|
2148
|
+
}, warn, className);
|
|
2149
|
+
}
|
|
2150
|
+
/**
|
|
2151
|
+
* Handles `Object.assign(this, {...})` with inline object literals.
|
|
2152
|
+
* @param {*} node - CallExpression node.
|
|
2153
|
+
* @param {boolean} conditional - True inside conditionals.
|
|
2154
|
+
* @param {object} ctx - Harvest context.
|
|
2155
|
+
* @returns {boolean} True when the call was `Object.assign` on `this`.
|
|
2156
|
+
*/
|
|
2157
|
+
function recordObjectAssign(node, conditional, ctx) {
|
|
2158
|
+
var _callee$object, _callee$property;
|
|
2159
|
+
var fields = ctx.fields,
|
|
2160
|
+
expandType = ctx.expandType,
|
|
2161
|
+
warn = ctx.warn,
|
|
2162
|
+
className = ctx.className;
|
|
2163
|
+
var callee = node.callee,
|
|
2164
|
+
args = node.arguments;
|
|
2165
|
+
if (!callee || callee.type !== 'MemberExpression' || callee.computed) {
|
|
2166
|
+
return false;
|
|
2167
|
+
}
|
|
2168
|
+
if (((_callee$object = callee.object) === null || _callee$object === void 0 ? void 0 : _callee$object.type) !== 'Identifier' || callee.object.name !== 'Object') {
|
|
2169
|
+
return false;
|
|
2170
|
+
}
|
|
2171
|
+
if (((_callee$property = callee.property) === null || _callee$property === void 0 ? void 0 : _callee$property.type) !== 'Identifier' || callee.property.name !== 'assign') {
|
|
2172
|
+
return false;
|
|
2173
|
+
}
|
|
2174
|
+
if (!args.length || args[0].type !== 'ThisExpression') {
|
|
2175
|
+
return false;
|
|
2176
|
+
}
|
|
2177
|
+
var _iterator = _createForOfIteratorHelper(args.slice(1)),
|
|
2178
|
+
_step;
|
|
2179
|
+
try {
|
|
2180
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
2181
|
+
var arg = _step.value;
|
|
2182
|
+
if (!arg || arg.type !== 'ObjectExpression') {
|
|
2183
|
+
continue;
|
|
2184
|
+
}
|
|
2185
|
+
var _iterator2 = _createForOfIteratorHelper(arg.properties),
|
|
2186
|
+
_step2;
|
|
2187
|
+
try {
|
|
2188
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
2189
|
+
var _declared$type2;
|
|
2190
|
+
var prop = _step2.value;
|
|
2191
|
+
if (!prop || prop.type !== 'ObjectProperty') {
|
|
2192
|
+
continue;
|
|
2193
|
+
}
|
|
2194
|
+
var name = keyName(prop.key, prop.computed);
|
|
2195
|
+
if (name === undefined) {
|
|
2196
|
+
continue;
|
|
2197
|
+
}
|
|
2198
|
+
var comment = lastBlockComment(prop);
|
|
2199
|
+
var declared = tagType(comment, '@type', expandType);
|
|
2200
|
+
var type = (_declared$type2 = declared === null || declared === void 0 ? void 0 : declared.type) !== null && _declared$type2 !== void 0 ? _declared$type2 : inferTypeFromDefault$1(unwrap(prop.value));
|
|
2201
|
+
if (type === undefined) {
|
|
2202
|
+
continue;
|
|
2203
|
+
}
|
|
2204
|
+
record(fields, name, {
|
|
2205
|
+
type: type,
|
|
2206
|
+
rank: declared ? CTOR_JSDOC : CTOR_INFER,
|
|
2207
|
+
jsdoc: declared !== undefined,
|
|
2208
|
+
raw: declared === null || declared === void 0 ? void 0 : declared.raw,
|
|
2209
|
+
optional: conditional
|
|
2210
|
+
}, warn, className);
|
|
2211
|
+
}
|
|
2212
|
+
} catch (err) {
|
|
2213
|
+
_iterator2.e(err);
|
|
2214
|
+
} finally {
|
|
2215
|
+
_iterator2.f();
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
} catch (err) {
|
|
2219
|
+
_iterator.e(err);
|
|
2220
|
+
} finally {
|
|
2221
|
+
_iterator.f();
|
|
2222
|
+
}
|
|
2223
|
+
return true;
|
|
2224
|
+
}
|
|
2225
|
+
/**
|
|
2226
|
+
* Walks an expression for `this.x` writes. Stops at function boundaries:
|
|
2227
|
+
* deferred writes (callbacks) are out of scope.
|
|
2228
|
+
* @param {*} expr - Babel expression node.
|
|
2229
|
+
* @param {boolean} conditional - True inside conditionals.
|
|
2230
|
+
* @param {object} ctx - Harvest context.
|
|
2231
|
+
* @param {*} stmt - Enclosing statement carrying leading comments.
|
|
2232
|
+
*/
|
|
2233
|
+
function walkExpression(expr, conditional, ctx, stmt) {
|
|
2234
|
+
if (!expr) {
|
|
2235
|
+
return;
|
|
2236
|
+
}
|
|
2237
|
+
switch (expr.type) {
|
|
2238
|
+
case 'AssignmentExpression':
|
|
2239
|
+
{
|
|
2240
|
+
var comment = lastBlockComment(stmt);
|
|
2241
|
+
if (expr.operator === '=') {
|
|
2242
|
+
recordThisAssign(expr.left, expr.right, comment, conditional, ctx);
|
|
2243
|
+
walkExpression(expr.right, conditional, ctx, stmt);
|
|
2244
|
+
} else {
|
|
2245
|
+
recordThisAssign(expr.left, undefined, comment, conditional, ctx);
|
|
2246
|
+
}
|
|
2247
|
+
break;
|
|
2248
|
+
}
|
|
2249
|
+
case 'UpdateExpression':
|
|
2250
|
+
recordThisAssign(expr.argument, undefined, lastBlockComment(stmt), conditional, ctx);
|
|
2251
|
+
break;
|
|
2252
|
+
case 'SequenceExpression':
|
|
2253
|
+
var _iterator3 = _createForOfIteratorHelper(expr.expressions),
|
|
2254
|
+
_step3;
|
|
2255
|
+
try {
|
|
2256
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
2257
|
+
var each = _step3.value;
|
|
2258
|
+
walkExpression(each, conditional, ctx, stmt);
|
|
2259
|
+
}
|
|
2260
|
+
} catch (err) {
|
|
2261
|
+
_iterator3.e(err);
|
|
2262
|
+
} finally {
|
|
2263
|
+
_iterator3.f();
|
|
2264
|
+
}
|
|
2265
|
+
break;
|
|
2266
|
+
case 'LogicalExpression':
|
|
2267
|
+
walkExpression(expr.right, true, ctx, stmt);
|
|
2268
|
+
break;
|
|
2269
|
+
case 'ConditionalExpression':
|
|
2270
|
+
walkExpression(expr.consequent, true, ctx, stmt);
|
|
2271
|
+
walkExpression(expr.alternate, true, ctx, stmt);
|
|
2272
|
+
break;
|
|
2273
|
+
case 'CallExpression':
|
|
2274
|
+
recordObjectAssign(expr, conditional, ctx);
|
|
2275
|
+
break;
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
/**
|
|
2279
|
+
* Walks a statement for `this.x` writes. Conditional wrappers mark entries
|
|
2280
|
+
* optional; nested functions are boundaries.
|
|
2281
|
+
* @param {*} st - Babel statement node.
|
|
2282
|
+
* @param {boolean} conditional - True inside conditionals.
|
|
2283
|
+
* @param {object} ctx - Harvest context.
|
|
2284
|
+
*/
|
|
2285
|
+
function walkStatement(st, conditional, ctx) {
|
|
2286
|
+
if (!st) {
|
|
2287
|
+
return;
|
|
2288
|
+
}
|
|
2289
|
+
switch (st.type) {
|
|
2290
|
+
case 'BlockStatement':
|
|
2291
|
+
var _iterator4 = _createForOfIteratorHelper(st.body),
|
|
2292
|
+
_step4;
|
|
2293
|
+
try {
|
|
2294
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
2295
|
+
var each = _step4.value;
|
|
2296
|
+
walkStatement(each, conditional, ctx);
|
|
2297
|
+
}
|
|
2298
|
+
} catch (err) {
|
|
2299
|
+
_iterator4.e(err);
|
|
2300
|
+
} finally {
|
|
2301
|
+
_iterator4.f();
|
|
2302
|
+
}
|
|
2303
|
+
break;
|
|
2304
|
+
case 'ExpressionStatement':
|
|
2305
|
+
walkExpression(st.expression, conditional, ctx, st);
|
|
2306
|
+
break;
|
|
2307
|
+
case 'ReturnStatement':
|
|
2308
|
+
walkExpression(st.argument, conditional, ctx, st);
|
|
2309
|
+
break;
|
|
2310
|
+
case 'IfStatement':
|
|
2311
|
+
walkStatement(st.consequent, true, ctx);
|
|
2312
|
+
walkStatement(st.alternate, true, ctx);
|
|
2313
|
+
break;
|
|
2314
|
+
case 'WhileStatement':
|
|
2315
|
+
case 'ForStatement':
|
|
2316
|
+
case 'ForInStatement':
|
|
2317
|
+
case 'ForOfStatement':
|
|
2318
|
+
walkStatement(st.body, true, ctx);
|
|
2319
|
+
break;
|
|
2320
|
+
case 'DoWhileStatement':
|
|
2321
|
+
walkStatement(st.body, conditional, ctx);
|
|
2322
|
+
break;
|
|
2323
|
+
case 'SwitchStatement':
|
|
2324
|
+
var _iterator5 = _createForOfIteratorHelper(st.cases),
|
|
2325
|
+
_step5;
|
|
2326
|
+
try {
|
|
2327
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
2328
|
+
var _each = _step5.value;
|
|
2329
|
+
var _iterator6 = _createForOfIteratorHelper(_each.consequent),
|
|
2330
|
+
_step6;
|
|
2331
|
+
try {
|
|
2332
|
+
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
|
|
2333
|
+
var consequent = _step6.value;
|
|
2334
|
+
walkStatement(consequent, true, ctx);
|
|
2335
|
+
}
|
|
2336
|
+
} catch (err) {
|
|
2337
|
+
_iterator6.e(err);
|
|
2338
|
+
} finally {
|
|
2339
|
+
_iterator6.f();
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
} catch (err) {
|
|
2343
|
+
_iterator5.e(err);
|
|
2344
|
+
} finally {
|
|
2345
|
+
_iterator5.f();
|
|
2346
|
+
}
|
|
2347
|
+
break;
|
|
2348
|
+
case 'TryStatement':
|
|
2349
|
+
walkStatement(st.block, conditional, ctx);
|
|
2350
|
+
if (st.handler) {
|
|
2351
|
+
walkStatement(st.handler.body, true, ctx);
|
|
2352
|
+
}
|
|
2353
|
+
if (st.finalizer) {
|
|
2354
|
+
walkStatement(st.finalizer, conditional, ctx);
|
|
2355
|
+
}
|
|
2356
|
+
break;
|
|
2357
|
+
case 'LabeledStatement':
|
|
2358
|
+
walkStatement(st.body, conditional, ctx);
|
|
2359
|
+
break;
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
/**
|
|
2363
|
+
* Reads the single parameter name of a setter.
|
|
2364
|
+
* @param {*} param - Babel parameter node.
|
|
2365
|
+
* @returns {string|undefined} Name or undefined.
|
|
2366
|
+
*/
|
|
2367
|
+
function setterParamName(param) {
|
|
2368
|
+
if (!param) {
|
|
2369
|
+
return;
|
|
2370
|
+
}
|
|
2371
|
+
if (param.type === 'Identifier') {
|
|
2372
|
+
return param.name;
|
|
2373
|
+
}
|
|
2374
|
+
if (param.type === 'AssignmentPattern' && param.left.type === 'Identifier') {
|
|
2375
|
+
return param.left.name;
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
/**
|
|
2379
|
+
* Reads a setter's value type: its `@param` JSDoc first, `@type` fallback.
|
|
2380
|
+
* @param {*} set - Babel ClassMethod (kind set) node.
|
|
2381
|
+
* @param {Function} expandType - String type to structured type.
|
|
2382
|
+
* @returns {{type: *, raw: string|undefined, jsdoc: boolean}} Type plus metadata.
|
|
2383
|
+
*/
|
|
2384
|
+
function setterType(set, expandType) {
|
|
2385
|
+
var setComment = lastBlockComment(set);
|
|
2386
|
+
var paramName = setterParamName(set.params[0]);
|
|
2387
|
+
var params = parseJSDoc(setComment !== null && setComment !== void 0 ? setComment : '', expandType);
|
|
2388
|
+
if (paramName && params && params[paramName] !== undefined) {
|
|
2389
|
+
return {
|
|
2390
|
+
type: params[paramName],
|
|
2391
|
+
raw: undefined,
|
|
2392
|
+
jsdoc: true
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
var tagged = tagType(setComment, '@type', expandType);
|
|
2396
|
+
if (tagged) {
|
|
2397
|
+
return {
|
|
2398
|
+
type: tagged.type,
|
|
2399
|
+
raw: tagged.raw,
|
|
2400
|
+
jsdoc: true
|
|
2401
|
+
};
|
|
2402
|
+
}
|
|
2403
|
+
return {
|
|
2404
|
+
type: 'any',
|
|
2405
|
+
raw: undefined,
|
|
2406
|
+
jsdoc: false
|
|
2407
|
+
};
|
|
2408
|
+
}
|
|
2409
|
+
/**
|
|
2410
|
+
* Harvests the instance shape of a class: field declarations (JSDoc wins,
|
|
2411
|
+
* else inferred), constructor `this.x` writes (field site wins conflicts),
|
|
2412
|
+
* methods as `Function`, getter/setter pairs as writable and getter-only as
|
|
2413
|
+
* readonly. Statics, privates and dynamic keys are skipped.
|
|
2414
|
+
* @param {*} node - Babel ClassDeclaration node.
|
|
2415
|
+
* @param {object} opts - Options with expandType and warn.
|
|
2416
|
+
* @param {Function} opts.expandType - String type to structured type.
|
|
2417
|
+
* @param {Function} opts.warn - Transpile-time warning function.
|
|
2418
|
+
* @returns {{name: string, shape: object}|undefined} Class name plus object shape.
|
|
2419
|
+
*/
|
|
2420
|
+
function harvestClassShape(node, _ref) {
|
|
2421
|
+
var expandType = _ref.expandType,
|
|
2422
|
+
warn = _ref.warn;
|
|
2423
|
+
var id = node === null || node === void 0 ? void 0 : node.id;
|
|
2424
|
+
if (!id || id.type !== 'Identifier' || !id.name) {
|
|
1352
2425
|
return;
|
|
1353
2426
|
}
|
|
1354
|
-
var
|
|
1355
|
-
var
|
|
1356
|
-
|
|
1357
|
-
|
|
2427
|
+
var className = id.name;
|
|
2428
|
+
var ctx = {
|
|
2429
|
+
fields: {},
|
|
2430
|
+
gets: {},
|
|
2431
|
+
sets: {},
|
|
2432
|
+
expandType: expandType,
|
|
2433
|
+
warn: warn,
|
|
2434
|
+
className: className
|
|
2435
|
+
};
|
|
2436
|
+
var ctor = null;
|
|
2437
|
+
var _iterator7 = _createForOfIteratorHelper(node.body.body),
|
|
2438
|
+
_step7;
|
|
1358
2439
|
try {
|
|
1359
|
-
for (
|
|
1360
|
-
var
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
line = line.slice(1).trim();
|
|
2440
|
+
for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
|
|
2441
|
+
var el = _step7.value;
|
|
2442
|
+
if (el.type === 'ClassPrivateProperty' || el.type === 'ClassPrivateMethod') {
|
|
2443
|
+
continue;
|
|
1364
2444
|
}
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
// Ignore @typedef's that only refer to themselves in another file (see typedef-overwrite test)
|
|
1374
|
-
if (lastTypedef !== name) {
|
|
1375
|
-
typedefs[name] = lastTypedef;
|
|
2445
|
+
var _name3 = keyName(el.key, el.computed);
|
|
2446
|
+
if (_name3 === undefined) {
|
|
2447
|
+
continue;
|
|
2448
|
+
}
|
|
2449
|
+
if (el.type === 'ClassProperty' || el.type === 'PropertyDefinition') {
|
|
2450
|
+
var _declared$type3;
|
|
2451
|
+
if (el.static || el.declare) {
|
|
2452
|
+
continue;
|
|
1376
2453
|
}
|
|
1377
|
-
|
|
1378
|
-
var
|
|
1379
|
-
|
|
1380
|
-
|
|
2454
|
+
var comment = lastBlockComment(el);
|
|
2455
|
+
var declared = tagType(comment, '@type', expandType);
|
|
2456
|
+
var inferred = el.value ? inferTypeFromDefault$1(unwrap(el.value)) : undefined;
|
|
2457
|
+
var _type = (_declared$type3 = declared === null || declared === void 0 ? void 0 : declared.type) !== null && _declared$type3 !== void 0 ? _declared$type3 : inferred;
|
|
2458
|
+
if (_type === undefined) {
|
|
1381
2459
|
continue;
|
|
1382
2460
|
}
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
2461
|
+
record(ctx.fields, _name3, {
|
|
2462
|
+
type: _type,
|
|
2463
|
+
rank: declared ? FIELD_JSDOC : FIELD_INFER,
|
|
2464
|
+
jsdoc: declared !== undefined,
|
|
2465
|
+
raw: declared === null || declared === void 0 ? void 0 : declared.raw,
|
|
2466
|
+
readonly: el.readonly === true || (comment ? /@readonly(?![\w])/.test(comment) : false),
|
|
2467
|
+
optional: el.optional === true
|
|
2468
|
+
}, warn, className);
|
|
2469
|
+
} else if (el.type === 'ClassMethod' || el.type === 'ClassPrivateMethod') {
|
|
2470
|
+
if (el.static) {
|
|
2471
|
+
continue;
|
|
2472
|
+
}
|
|
2473
|
+
if (el.kind === 'constructor') {
|
|
2474
|
+
ctor = el;
|
|
2475
|
+
} else if (el.kind === 'method') {
|
|
2476
|
+
var _ctx$fields$_name;
|
|
2477
|
+
ctx.fields[_name3] = (_ctx$fields$_name = ctx.fields[_name3]) !== null && _ctx$fields$_name !== void 0 ? _ctx$fields$_name : {
|
|
2478
|
+
type: 'Function',
|
|
2479
|
+
rank: FIELD_INFER,
|
|
2480
|
+
jsdoc: false
|
|
2481
|
+
};
|
|
2482
|
+
} else if (el.kind === 'get') {
|
|
2483
|
+
ctx.gets[_name3] = el;
|
|
2484
|
+
} else if (el.kind === 'set') {
|
|
2485
|
+
ctx.sets[_name3] = el;
|
|
1398
2486
|
}
|
|
1399
|
-
} else if (line.startsWith('@callback')) {
|
|
1400
|
-
var _name2 = line.substring(9).trim();
|
|
1401
|
-
typedefs[_name2] = 'Function';
|
|
1402
2487
|
}
|
|
1403
2488
|
}
|
|
1404
2489
|
} catch (err) {
|
|
1405
|
-
|
|
2490
|
+
_iterator7.e(err);
|
|
1406
2491
|
} finally {
|
|
1407
|
-
|
|
2492
|
+
_iterator7.f();
|
|
2493
|
+
}
|
|
2494
|
+
for (var _i = 0, _Object$keys = Object.keys(ctx.gets); _i < _Object$keys.length; _i++) {
|
|
2495
|
+
var _tagType, _ref2, _getType$type, _getType$raw;
|
|
2496
|
+
var name = _Object$keys[_i];
|
|
2497
|
+
var get = ctx.gets[name];
|
|
2498
|
+
var set = ctx.sets[name];
|
|
2499
|
+
var getComment = lastBlockComment(get);
|
|
2500
|
+
var getType = (_tagType = tagType(getComment, '@type', expandType)) !== null && _tagType !== void 0 ? _tagType : tagType(getComment, '@returns', expandType);
|
|
2501
|
+
var setInfo = set ? setterType(set, expandType) : undefined;
|
|
2502
|
+
var type = (_ref2 = (_getType$type = getType === null || getType === void 0 ? void 0 : getType.type) !== null && _getType$type !== void 0 ? _getType$type : setInfo === null || setInfo === void 0 ? void 0 : setInfo.type) !== null && _ref2 !== void 0 ? _ref2 : 'any';
|
|
2503
|
+
record(ctx.fields, name, {
|
|
2504
|
+
type: type,
|
|
2505
|
+
rank: getType || setInfo !== null && setInfo !== void 0 && setInfo.jsdoc ? FIELD_JSDOC : FIELD_INFER,
|
|
2506
|
+
jsdoc: Boolean(getType || (setInfo === null || setInfo === void 0 ? void 0 : setInfo.jsdoc)),
|
|
2507
|
+
raw: (_getType$raw = getType === null || getType === void 0 ? void 0 : getType.raw) !== null && _getType$raw !== void 0 ? _getType$raw : setInfo === null || setInfo === void 0 ? void 0 : setInfo.raw,
|
|
2508
|
+
readonly: !set
|
|
2509
|
+
}, warn, className);
|
|
2510
|
+
}
|
|
2511
|
+
for (var _i2 = 0, _Object$keys2 = Object.keys(ctx.sets); _i2 < _Object$keys2.length; _i2++) {
|
|
2512
|
+
var _name = _Object$keys2[_i2];
|
|
2513
|
+
if (ctx.gets[_name]) {
|
|
2514
|
+
continue;
|
|
2515
|
+
}
|
|
2516
|
+
var _setInfo = setterType(ctx.sets[_name], expandType);
|
|
2517
|
+
record(ctx.fields, _name, {
|
|
2518
|
+
type: _setInfo.type,
|
|
2519
|
+
rank: _setInfo.jsdoc ? FIELD_JSDOC : FIELD_INFER,
|
|
2520
|
+
jsdoc: _setInfo.jsdoc,
|
|
2521
|
+
raw: _setInfo.raw
|
|
2522
|
+
}, warn, className);
|
|
1408
2523
|
}
|
|
2524
|
+
if (ctor && ctor.body) {
|
|
2525
|
+
var _iterator8 = _createForOfIteratorHelper(ctor.body.body),
|
|
2526
|
+
_step8;
|
|
2527
|
+
try {
|
|
2528
|
+
for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
|
|
2529
|
+
var st = _step8.value;
|
|
2530
|
+
walkStatement(st, false, ctx);
|
|
2531
|
+
}
|
|
2532
|
+
} catch (err) {
|
|
2533
|
+
_iterator8.e(err);
|
|
2534
|
+
} finally {
|
|
2535
|
+
_iterator8.f();
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
var properties = {};
|
|
2539
|
+
for (var _i3 = 0, _Object$keys3 = Object.keys(ctx.fields); _i3 < _Object$keys3.length; _i3++) {
|
|
2540
|
+
var _name2 = _Object$keys3[_i3];
|
|
2541
|
+
properties[_name2] = entryToType(ctx.fields[_name2]);
|
|
2542
|
+
}
|
|
2543
|
+
if (!Object.keys(properties).length) {
|
|
2544
|
+
return;
|
|
2545
|
+
}
|
|
2546
|
+
return {
|
|
2547
|
+
name: className,
|
|
2548
|
+
shape: {
|
|
2549
|
+
type: 'object',
|
|
2550
|
+
properties: properties
|
|
2551
|
+
}
|
|
2552
|
+
};
|
|
1409
2553
|
}
|
|
1410
2554
|
|
|
1411
2555
|
/**
|
|
@@ -1454,101 +2598,6 @@
|
|
|
1454
2598
|
return false;
|
|
1455
2599
|
}
|
|
1456
2600
|
|
|
1457
|
-
/**
|
|
1458
|
-
* @typedef {ReturnType<typeof parseJSDoc>} ParseJSDocReturnType
|
|
1459
|
-
*/
|
|
1460
|
-
/**
|
|
1461
|
-
* @typedef {typeof expandTypeDepFree} ExpandType
|
|
1462
|
-
* @typedef {ReturnType<ExpandType>} ExpandTypeReturnType
|
|
1463
|
-
*/
|
|
1464
|
-
/**
|
|
1465
|
-
* Parses JSDoc comments to extract parameter type information.
|
|
1466
|
-
*
|
|
1467
|
-
* @param {string} src - The JSDoc comment string to parse.
|
|
1468
|
-
* @param {ExpandType} [expandType] - An optional function to process the types found in the JSDoc.
|
|
1469
|
-
* @returns {Record<string, ExpandTypeReturnType> | undefined} An object mapping parameter names to their parsed types, or undefined if no parameters are found.
|
|
1470
|
-
*/
|
|
1471
|
-
function parseJSDoc(src) {
|
|
1472
|
-
var expandType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : expandTypeDepFree;
|
|
1473
|
-
// Parse something like: @param {Object} [kwargs={}] Optional arguments.
|
|
1474
|
-
var regex = /@param \{(.*?)\} ([\[\]a-zA-Z0-9_$=\-\{\}\.'" ]+)/g;
|
|
1475
|
-
var matches = _toConsumableArray(src.matchAll(regex));
|
|
1476
|
-
/** @type {Record<string, ExpandTypeReturnType>} */
|
|
1477
|
-
var params = Object.create(null);
|
|
1478
|
-
matches.forEach(function (_) {
|
|
1479
|
-
var type = expandType(_[1].trim());
|
|
1480
|
-
var name = _[2].trim();
|
|
1481
|
-
var optional = false;
|
|
1482
|
-
// Examples:
|
|
1483
|
-
// name: [kwargs={}] The configuration parameters.
|
|
1484
|
-
// name: [d = 1.0] Sample spacing
|
|
1485
|
-
if (name[0] === '[') {
|
|
1486
|
-
// Counting opening/closing brackets for perfect match
|
|
1487
|
-
var openCloseCount = 1;
|
|
1488
|
-
var i = 1;
|
|
1489
|
-
for (; i < name.length; i++) {
|
|
1490
|
-
var c = name[i];
|
|
1491
|
-
if (c === '[') {
|
|
1492
|
-
openCloseCount++;
|
|
1493
|
-
} else if (c === ']') {
|
|
1494
|
-
openCloseCount--;
|
|
1495
|
-
}
|
|
1496
|
-
if (openCloseCount === 0) {
|
|
1497
|
-
break;
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
// Afterwards name will be: d = 1.0
|
|
1501
|
-
name = name.substring(1, i);
|
|
1502
|
-
// mark it for the type:
|
|
1503
|
-
optional = true;
|
|
1504
|
-
}
|
|
1505
|
-
// Strip the rest (either leftover of optional value or description)
|
|
1506
|
-
name = name.split(' ')[0].split('=')[0].trim();
|
|
1507
|
-
var annotatedType = annotateOptional(type, optional);
|
|
1508
|
-
// Turn "options.stats[].unitsName" into ['options', 'stats', 'unitsName'].
|
|
1509
|
-
var parts = name.split(/[\[\]]*\./);
|
|
1510
|
-
var properties = params;
|
|
1511
|
-
var _iterator = _createForOfIteratorHelper(parts),
|
|
1512
|
-
_step;
|
|
1513
|
-
try {
|
|
1514
|
-
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
1515
|
-
var part = _step.value;
|
|
1516
|
-
var toptype = properties[part];
|
|
1517
|
-
if (!toptype) {
|
|
1518
|
-
// No toptype means we resolved as far as possible, now we can add `annotatedType`.
|
|
1519
|
-
console.assert(part === parts.at(-1), 'Current part and last part should be the same.');
|
|
1520
|
-
properties[part] = annotatedType;
|
|
1521
|
-
} else if (toptype.type === "union") {
|
|
1522
|
-
var typeObject = toptype.members.find(function (_) {
|
|
1523
|
-
return (_ === null || _ === void 0 ? void 0 : _.type) === 'object';
|
|
1524
|
-
});
|
|
1525
|
-
properties = typeObject.properties;
|
|
1526
|
-
} else if (toptype.type === "array") {
|
|
1527
|
-
properties = toptype.elementType.properties;
|
|
1528
|
-
} else if (toptype.type === "object") {
|
|
1529
|
-
toptype.properties = toptype.properties || Object.create(null);
|
|
1530
|
-
properties = toptype.properties;
|
|
1531
|
-
} else {
|
|
1532
|
-
console.warn("parseJSDoc> Skipping @param, unseen syntax detected. Please check if your JSDoc is valid or open an issue about this!", {
|
|
1533
|
-
src: src,
|
|
1534
|
-
toptype: toptype,
|
|
1535
|
-
parts: parts,
|
|
1536
|
-
annotatedType: annotatedType
|
|
1537
|
-
});
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
} catch (err) {
|
|
1541
|
-
_iterator.e(err);
|
|
1542
|
-
} finally {
|
|
1543
|
-
_iterator.f();
|
|
1544
|
-
}
|
|
1545
|
-
});
|
|
1546
|
-
if (Object.keys(params).length === 0) {
|
|
1547
|
-
return;
|
|
1548
|
-
}
|
|
1549
|
-
return params;
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
2601
|
/**
|
|
1553
2602
|
* @param {string} src - JSDoc comment of the setter.
|
|
1554
2603
|
* @param {Function} expandType - The expandType function.
|
|
@@ -1583,9 +2632,6 @@
|
|
|
1583
2632
|
var expandType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : expandTypeDepFree;
|
|
1584
2633
|
var regexTemplateTyped = /@template \{(.*?)\} ([a-zA-Z0-9_$=]+)/g;
|
|
1585
2634
|
var matches = _toConsumableArray(src.matchAll(regexTemplateTyped));
|
|
1586
|
-
if (!matches.length) {
|
|
1587
|
-
return;
|
|
1588
|
-
}
|
|
1589
2635
|
/** @type {Record<string, ExpandTypeReturnType>} */
|
|
1590
2636
|
var templates = Object.create(null);
|
|
1591
2637
|
matches.forEach(function (_) {
|
|
@@ -1593,6 +2639,40 @@
|
|
|
1593
2639
|
var name = _[2].trim();
|
|
1594
2640
|
templates[name] = type;
|
|
1595
2641
|
});
|
|
2642
|
+
// `@template [A=X]` defaults: constraint X under name A.
|
|
2643
|
+
var regexTemplateDefault = /@template \[([a-zA-Z0-9_$]+)=([^\]]+)\]/g;
|
|
2644
|
+
var _iterator = _createForOfIteratorHelper(src.matchAll(regexTemplateDefault)),
|
|
2645
|
+
_step;
|
|
2646
|
+
try {
|
|
2647
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
2648
|
+
var match = _step.value;
|
|
2649
|
+
templates[match[1]] = expandType(match[2].trim());
|
|
2650
|
+
}
|
|
2651
|
+
// Bare `@template T`: unconstrained, stands in as any.
|
|
2652
|
+
} catch (err) {
|
|
2653
|
+
_iterator.e(err);
|
|
2654
|
+
} finally {
|
|
2655
|
+
_iterator.f();
|
|
2656
|
+
}
|
|
2657
|
+
var regexTemplateBare = /@template ([a-zA-Z0-9_$]+)(?![a-zA-Z0-9_$])/g;
|
|
2658
|
+
var _iterator2 = _createForOfIteratorHelper(src.matchAll(regexTemplateBare)),
|
|
2659
|
+
_step2;
|
|
2660
|
+
try {
|
|
2661
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
2662
|
+
var _match = _step2.value;
|
|
2663
|
+
var name = _match[1];
|
|
2664
|
+
if (!(name in templates)) {
|
|
2665
|
+
templates[name] = 'any';
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
} catch (err) {
|
|
2669
|
+
_iterator2.e(err);
|
|
2670
|
+
} finally {
|
|
2671
|
+
_iterator2.f();
|
|
2672
|
+
}
|
|
2673
|
+
if (!Object.keys(templates).length) {
|
|
2674
|
+
return;
|
|
2675
|
+
}
|
|
1596
2676
|
return templates;
|
|
1597
2677
|
}
|
|
1598
2678
|
|
|
@@ -1618,8 +2698,12 @@
|
|
|
1618
2698
|
*/
|
|
1619
2699
|
/**
|
|
1620
2700
|
* Recursively clones and simplifies a type for emission into source code.
|
|
1621
|
-
*
|
|
1622
|
-
*
|
|
2701
|
+
* Empty `properties` are preserved (not deleted) and empty `object` types
|
|
2702
|
+
* are kept structured: `{type: 'object'}` without a `properties` key is the
|
|
2703
|
+
* `{}` literal (accepts every non-nullish value like TS), while
|
|
2704
|
+
* `{type: 'object', properties: {}}` is the `object` keyword (rejects
|
|
2705
|
+
* primitives). Collapsing either form would erase that distinction.
|
|
2706
|
+
* Numbers/booleans (literal types) pass through.
|
|
1623
2707
|
* Non-destructive — the original type tree is never mutated.
|
|
1624
2708
|
* @param {string | DocType | number | boolean} type - The type.
|
|
1625
2709
|
* @returns {string | DocType | number | boolean} The simplified type.
|
|
@@ -1631,9 +2715,6 @@
|
|
|
1631
2715
|
var out = _objectSpread2({}, type);
|
|
1632
2716
|
if (out.properties) {
|
|
1633
2717
|
out.properties = mapValues(out.properties, simplifyType);
|
|
1634
|
-
if (out.type === 'object' && !Object.keys(out.properties).length) {
|
|
1635
|
-
delete out.properties;
|
|
1636
|
-
}
|
|
1637
2718
|
}
|
|
1638
2719
|
if (out.indexSignatures && Array.isArray(out.indexSignatures)) {
|
|
1639
2720
|
out.indexSignatures = out.indexSignatures.map(simplifyType);
|
|
@@ -1660,9 +2741,6 @@
|
|
|
1660
2741
|
if (out.type === 'typeof' && out.argument) {
|
|
1661
2742
|
out.argument = simplifyType(out.argument);
|
|
1662
2743
|
}
|
|
1663
|
-
if (out.type === 'object' && !out.properties && !out.indexSignatures && !out.optional) {
|
|
1664
|
-
return 'object';
|
|
1665
|
-
}
|
|
1666
2744
|
return out;
|
|
1667
2745
|
}
|
|
1668
2746
|
|
|
@@ -3934,6 +5012,8 @@
|
|
|
3934
5012
|
});
|
|
3935
5013
|
/** @type {Record<string, object>} */
|
|
3936
5014
|
_defineProperty(_assertThisInitialized(_this), "typedefs", {});
|
|
5015
|
+
/** @type {Record<string, string[]>} */
|
|
5016
|
+
_defineProperty(_assertThisInitialized(_this), "typedefTemplates", {});
|
|
3937
5017
|
/** @type {string[]} */
|
|
3938
5018
|
_defineProperty(_assertThisInitialized(_this), "addLaterImportNamespaceSpecifier", []);
|
|
3939
5019
|
_this.forceCurly = forceCurly;
|
|
@@ -3996,6 +5076,21 @@
|
|
|
3996
5076
|
var id_ = this.toSource(id);
|
|
3997
5077
|
var out = _get(_getPrototypeOf(Asserter.prototype), "ClassDeclaration", this).call(this, node);
|
|
3998
5078
|
out += "".concat(this.spaces, "registerClass(").concat(id_, ");");
|
|
5079
|
+
var harvested = harvestClassShape(node, {
|
|
5080
|
+
expandType: this.expandType,
|
|
5081
|
+
warn: this.warn.bind(this)
|
|
5082
|
+
});
|
|
5083
|
+
if (harvested) {
|
|
5084
|
+
// Hand-written typedefs win: emitting a second registerTypedef for the
|
|
5085
|
+
// same name would be last-wins deterministic but noisy, so the harvest
|
|
5086
|
+
// step skips names already present in this.typedefs (populated in File).
|
|
5087
|
+
if (this.typedefs[harvested.name]) {
|
|
5088
|
+
this.warn("harvestClassShape: skipping harvested shape for '".concat(harvested.name, "', hand-written typedef wins"));
|
|
5089
|
+
} else {
|
|
5090
|
+
var json = simplifyTypeToSource(harvested.shape);
|
|
5091
|
+
out += "\n".concat(this.spaces, "registerTypedef('").concat(harvested.name, "', ").concat(json, ");");
|
|
5092
|
+
}
|
|
5093
|
+
}
|
|
3999
5094
|
return out;
|
|
4000
5095
|
}
|
|
4001
5096
|
/**
|
|
@@ -4910,7 +6005,7 @@
|
|
|
4910
6005
|
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
4911
6006
|
var comment = _step5.value;
|
|
4912
6007
|
var warn = this.warn.bind(this);
|
|
4913
|
-
parseJSDocTypedef(this.typedefs, warn, comment, this.expandType);
|
|
6008
|
+
parseJSDocTypedef(this.typedefs, this.typedefTemplates, warn, comment, this.expandType);
|
|
4914
6009
|
}
|
|
4915
6010
|
} catch (err) {
|
|
4916
6011
|
_iterator5.e(err);
|
|
@@ -4923,7 +6018,8 @@
|
|
|
4923
6018
|
for (var name in this.typedefs) {
|
|
4924
6019
|
var typedef = this.typedefs[name];
|
|
4925
6020
|
var json = simplifyTypeToSource(typedef);
|
|
4926
|
-
|
|
6021
|
+
var params = this.typedefTemplates[name];
|
|
6022
|
+
out += params !== null && params !== void 0 && params.length ? "registerTypedef('".concat(name, "', ").concat(json, ", ").concat(JSON.stringify(params), ");\n") : "registerTypedef('".concat(name, "', ").concat(json, ");\n");
|
|
4927
6023
|
}
|
|
4928
6024
|
var code = this.toSource(program) + '\n';
|
|
4929
6025
|
out += code;
|
|
@@ -5214,7 +6310,13 @@
|
|
|
5214
6310
|
{
|
|
5215
6311
|
var name = toSourceBabelTS(node.typeName);
|
|
5216
6312
|
if (!node.typeParameters) {
|
|
5217
|
-
//
|
|
6313
|
+
// Bare `Object` is the empty object type, matching expandType().
|
|
6314
|
+
if (name === 'Object') {
|
|
6315
|
+
return {
|
|
6316
|
+
type: 'object',
|
|
6317
|
+
properties: {}
|
|
6318
|
+
};
|
|
6319
|
+
}
|
|
5218
6320
|
// Bare reference: Identifier gives name, TSQualifiedName gives dotted path.
|
|
5219
6321
|
return name;
|
|
5220
6322
|
}
|
|
@@ -5275,6 +6377,8 @@
|
|
|
5275
6377
|
}
|
|
5276
6378
|
case 'TSStringKeyword':
|
|
5277
6379
|
return 'string';
|
|
6380
|
+
case 'TSSymbolKeyword':
|
|
6381
|
+
return 'symbol';
|
|
5278
6382
|
case 'TSNumberKeyword':
|
|
5279
6383
|
return 'number';
|
|
5280
6384
|
case 'TSIntersectionType':
|
|
@@ -5356,6 +6460,8 @@
|
|
|
5356
6460
|
case 'ParenthesizedType':
|
|
5357
6461
|
// fall-through for parentheses
|
|
5358
6462
|
return toSourceBabelTS(node.type);
|
|
6463
|
+
case 'TSParenthesizedType':
|
|
6464
|
+
return toSourceBabelTS(node.typeAnnotation);
|
|
5359
6465
|
case 'LastTypeNode':
|
|
5360
6466
|
return toSourceBabelTS(node.qualifier);
|
|
5361
6467
|
case 'TSTypeQuery':
|
|
@@ -5364,11 +6470,129 @@
|
|
|
5364
6470
|
type: 'typeof',
|
|
5365
6471
|
argument: argument
|
|
5366
6472
|
};
|
|
6473
|
+
case 'TSConditionalType':
|
|
6474
|
+
{
|
|
6475
|
+
var checkType = toSourceBabelTS(node.checkType);
|
|
6476
|
+
var extendsType = toSourceBabelTS(node.extendsType);
|
|
6477
|
+
var trueType = toSourceBabelTS(node.trueType);
|
|
6478
|
+
var falseType = toSourceBabelTS(node.falseType);
|
|
6479
|
+
return {
|
|
6480
|
+
type: 'condition',
|
|
6481
|
+
checkType: checkType,
|
|
6482
|
+
extendsType: extendsType,
|
|
6483
|
+
trueType: trueType,
|
|
6484
|
+
falseType: falseType
|
|
6485
|
+
};
|
|
6486
|
+
}
|
|
6487
|
+
case 'TSIndexedAccessType':
|
|
6488
|
+
{
|
|
6489
|
+
var index = toSourceBabelTS(node.indexType);
|
|
6490
|
+
var object = toSourceBabelTS(node.objectType);
|
|
6491
|
+
return {
|
|
6492
|
+
type: 'indexedAccess',
|
|
6493
|
+
index: index,
|
|
6494
|
+
object: object
|
|
6495
|
+
};
|
|
6496
|
+
}
|
|
6497
|
+
case 'TSMappedType':
|
|
6498
|
+
{
|
|
6499
|
+
var param = node.typeParameter;
|
|
6500
|
+
var nameNode = param === null || param === void 0 ? void 0 : param.name;
|
|
6501
|
+
var element = typeof nameNode === 'string' ? nameNode : toSourceBabelTS(nameNode);
|
|
6502
|
+
var iterable = toSourceBabelTS(param === null || param === void 0 ? void 0 : param.constraint);
|
|
6503
|
+
var result = toSourceBabelTS(node.typeAnnotation);
|
|
6504
|
+
var out = {
|
|
6505
|
+
type: 'mapping',
|
|
6506
|
+
iterable: iterable,
|
|
6507
|
+
element: element,
|
|
6508
|
+
result: result
|
|
6509
|
+
};
|
|
6510
|
+
if (node.nameType) {
|
|
6511
|
+
out.nameType = toSourceBabelTS(node.nameType);
|
|
6512
|
+
}
|
|
6513
|
+
if (node.optional !== undefined && node.optional !== false && node.optional !== null) {
|
|
6514
|
+
out.question = node.optional === '-' ? '-' : node.optional === '+' ? '+' : '?';
|
|
6515
|
+
}
|
|
6516
|
+
if (node.readonly !== undefined && node.readonly !== false && node.readonly !== null) {
|
|
6517
|
+
out.readonly = node.readonly === '-' ? '-' : node.readonly === '+' ? '+' : 'readonly';
|
|
6518
|
+
}
|
|
6519
|
+
return out;
|
|
6520
|
+
}
|
|
6521
|
+
case 'TSTypeAnnotation':
|
|
6522
|
+
return toSourceBabelTS(node.typeAnnotation);
|
|
6523
|
+
case 'TSTypeLiteral':
|
|
6524
|
+
{
|
|
6525
|
+
var _node$members;
|
|
6526
|
+
var _properties = {};
|
|
6527
|
+
var indexSignatures;
|
|
6528
|
+
var _iterator = _createForOfIteratorHelper((_node$members = node.members) !== null && _node$members !== void 0 ? _node$members : []),
|
|
6529
|
+
_step;
|
|
6530
|
+
try {
|
|
6531
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
6532
|
+
var member = _step.value;
|
|
6533
|
+
if (member.type === 'TSIndexSignature') {
|
|
6534
|
+
var _indexSignatures;
|
|
6535
|
+
indexSignatures = (_indexSignatures = indexSignatures) !== null && _indexSignatures !== void 0 ? _indexSignatures : [];
|
|
6536
|
+
indexSignatures.push(toSourceBabelTS(member));
|
|
6537
|
+
} else if (member.type === 'TSPropertySignature') {
|
|
6538
|
+
var _name = toSourceBabelTS(member.key);
|
|
6539
|
+
var type = toSourceBabelTS(member.typeAnnotation);
|
|
6540
|
+
if (member.optional) {
|
|
6541
|
+
if (type && _typeof(type) === 'object') type.optional = true;else type = {
|
|
6542
|
+
type: type,
|
|
6543
|
+
optional: true
|
|
6544
|
+
};
|
|
6545
|
+
}
|
|
6546
|
+
if (member.readonly) {
|
|
6547
|
+
if (type && _typeof(type) === 'object') type.readonly = true;else type = {
|
|
6548
|
+
type: type,
|
|
6549
|
+
readonly: true
|
|
6550
|
+
};
|
|
6551
|
+
}
|
|
6552
|
+
_properties[_name] = type;
|
|
6553
|
+
} else {
|
|
6554
|
+
console.warn('TSTypeLiteral: unhandled member', member.type);
|
|
6555
|
+
}
|
|
6556
|
+
}
|
|
6557
|
+
} catch (err) {
|
|
6558
|
+
_iterator.e(err);
|
|
6559
|
+
} finally {
|
|
6560
|
+
_iterator.f();
|
|
6561
|
+
}
|
|
6562
|
+
var ret = {
|
|
6563
|
+
type: 'object'
|
|
6564
|
+
};
|
|
6565
|
+
if (Object.keys(_properties).length) {
|
|
6566
|
+
ret.properties = _properties;
|
|
6567
|
+
}
|
|
6568
|
+
if (indexSignatures) {
|
|
6569
|
+
ret.indexSignatures = indexSignatures;
|
|
6570
|
+
}
|
|
6571
|
+
return ret;
|
|
6572
|
+
}
|
|
6573
|
+
case 'TSIndexSignature':
|
|
6574
|
+
{
|
|
6575
|
+
var _node$parameters;
|
|
6576
|
+
var indexType = toSourceBabelTS(node.typeAnnotation);
|
|
6577
|
+
var indexParameters = ((_node$parameters = node.parameters) !== null && _node$parameters !== void 0 ? _node$parameters : []).map(toSourceBabelTS);
|
|
6578
|
+
return {
|
|
6579
|
+
type: 'indexSignature',
|
|
6580
|
+
indexType: indexType,
|
|
6581
|
+
indexParameters: indexParameters
|
|
6582
|
+
};
|
|
6583
|
+
}
|
|
5367
6584
|
case 'TSTypeOperator':
|
|
5368
6585
|
if (node.operator === 'readonly') {
|
|
5369
6586
|
// readonly erased at runtime, same shape as the inner type.
|
|
5370
6587
|
return toSourceBabelTS(node.typeAnnotation);
|
|
5371
6588
|
}
|
|
6589
|
+
if (node.operator === 'keyof') {
|
|
6590
|
+
var keyofArg = toSourceBabelTS(node.typeAnnotation);
|
|
6591
|
+
return {
|
|
6592
|
+
type: 'keyof',
|
|
6593
|
+
argument: keyofArg
|
|
6594
|
+
};
|
|
6595
|
+
}
|
|
5372
6596
|
console.warn('unimplemented TSTypeOperator', node.operator);
|
|
5373
6597
|
return 'any';
|
|
5374
6598
|
case 'TSQualifiedName':
|
|
@@ -5501,6 +6725,8 @@
|
|
|
5501
6725
|
_defineProperty(this, "parents", []);
|
|
5502
6726
|
/** @type {Record<string, object>} */
|
|
5503
6727
|
_defineProperty(this, "typedefs", {});
|
|
6728
|
+
/** @type {Record<string, string[]>} */
|
|
6729
|
+
_defineProperty(this, "typedefTemplates", {});
|
|
5504
6730
|
/** @type {import('./parseJSDoc.js').ExpandType} */
|
|
5505
6731
|
this.expandType = options.expandType || expandTypeDepFree;
|
|
5506
6732
|
}
|
|
@@ -5521,7 +6747,7 @@
|
|
|
5521
6747
|
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
5522
6748
|
var comment = _step.value;
|
|
5523
6749
|
var warn = console.warn.bind(console);
|
|
5524
|
-
parseJSDocTypedef(this.typedefs, warn, comment, this.expandType);
|
|
6750
|
+
parseJSDocTypedef(this.typedefs, this.typedefTemplates, warn, comment, this.expandType);
|
|
5525
6751
|
}
|
|
5526
6752
|
} catch (err) {
|
|
5527
6753
|
_iterator.e(err);
|