luaut-parser 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -4
- package/dist/index.cjs +549 -111
- package/dist/index.d.cts +56 -11
- package/dist/index.d.ts +56 -11
- package/dist/index.js +548 -111
- package/package.json +1 -3
package/dist/index.js
CHANGED
|
@@ -796,23 +796,14 @@ var Parser = class {
|
|
|
796
796
|
if (t.type === "Punctuator" && t.value === "@") {
|
|
797
797
|
const { attributes, start } = this.parseAttributes();
|
|
798
798
|
const next = this.current();
|
|
799
|
-
if (next.type === "Keyword" && (next.value === "const" || next.value === "let")) {
|
|
800
|
-
const stmt = this.parseVariableDeclaration();
|
|
801
|
-
if (stmt.type === "FunctionDeclaration") {
|
|
802
|
-
stmt.attributes = attributes;
|
|
803
|
-
stmt.line.start = start.line.start;
|
|
804
|
-
stmt.column.start = start.column.start;
|
|
805
|
-
}
|
|
806
|
-
return stmt;
|
|
807
|
-
}
|
|
808
799
|
if (next.type === "Keyword" && next.value === "function") {
|
|
809
|
-
const stmt = this.
|
|
800
|
+
const stmt = this.parseFunctionStatement();
|
|
810
801
|
stmt.attributes = attributes;
|
|
811
802
|
stmt.line.start = start.line.start;
|
|
812
803
|
stmt.column.start = start.column.start;
|
|
813
804
|
return stmt;
|
|
814
805
|
}
|
|
815
|
-
throw new ParseError("Expected 'function'
|
|
806
|
+
throw new ParseError("Expected 'function' after an attribute", next.line.start, next.column.start);
|
|
816
807
|
}
|
|
817
808
|
if (t.type === "Keyword") {
|
|
818
809
|
switch (t.value) {
|
|
@@ -830,7 +821,7 @@ var Parser = class {
|
|
|
830
821
|
case "for":
|
|
831
822
|
return this.parseForStatement();
|
|
832
823
|
case "function":
|
|
833
|
-
return this.
|
|
824
|
+
return this.parseFunctionStatement();
|
|
834
825
|
case "return":
|
|
835
826
|
return this.parseReturnStatement();
|
|
836
827
|
case "import":
|
|
@@ -852,6 +843,9 @@ var Parser = class {
|
|
|
852
843
|
}
|
|
853
844
|
if (t.type === "Identifier" && t.value === "declare") {
|
|
854
845
|
const p1 = this.peek(1);
|
|
846
|
+
if (p1.type === "Identifier" && p1.value === "class" && this.peek(2).type === "Identifier") {
|
|
847
|
+
return this.parseDeclareClassStatement();
|
|
848
|
+
}
|
|
855
849
|
if (p1.type === "Identifier" || p1.type === "Keyword" && p1.value === "function") {
|
|
856
850
|
return this.parseDeclareStatement();
|
|
857
851
|
}
|
|
@@ -893,26 +887,66 @@ var Parser = class {
|
|
|
893
887
|
const valueType = this.parseType();
|
|
894
888
|
return { type: "DeclareStatement", name: nameTok.value, id: tokenIdentifier(nameTok), valueType, ...spanFrom(start, this.previous()) };
|
|
895
889
|
}
|
|
890
|
+
/** A declared type's name. It may be qualified once — `Enum.Material` —
|
|
891
|
+
* which is how a definitions file names types under a namespace, and how
|
|
892
|
+
* they are then written (`const m: Enum.Material`). */
|
|
893
|
+
parseTypeName() {
|
|
894
|
+
const first = this.expectIdentifier();
|
|
895
|
+
if (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
|
|
896
|
+
this.advance();
|
|
897
|
+
const second = this.expectIdentifier();
|
|
898
|
+
return { type: "Identifier", name: `${first.value}.${second.value}`, ...spanFrom(first, second) };
|
|
899
|
+
}
|
|
900
|
+
return tokenIdentifier(first);
|
|
901
|
+
}
|
|
902
|
+
// `declare class Name extends Base { member: T, ... }`
|
|
903
|
+
parseDeclareClassStatement() {
|
|
904
|
+
const start = this.current();
|
|
905
|
+
this.advance();
|
|
906
|
+
this.advance();
|
|
907
|
+
const name = this.parseTypeName();
|
|
908
|
+
let superclass;
|
|
909
|
+
if (this.checkIdentifierValue("extends")) {
|
|
910
|
+
this.advance();
|
|
911
|
+
const base = this.parseType();
|
|
912
|
+
if (base.type !== "TypeReference") this.error("A class can only extend another class, written by name");
|
|
913
|
+
superclass = base;
|
|
914
|
+
}
|
|
915
|
+
if (!this.checkPunctuator("{")) this.error("Expected '{' to start the class body");
|
|
916
|
+
const body = this.parseTableType();
|
|
917
|
+
if (body.type !== "TableTypeNode") this.error("A class body lists members ('name: T'), not a mapped type");
|
|
918
|
+
return { type: "DeclareClassStatement", name, superclass, body, ...spanFrom(start, this.previous()) };
|
|
919
|
+
}
|
|
896
920
|
// `import { a, b as c } from '...'` / `import Default from '...'` /
|
|
897
921
|
// `import Default, { a } from '...'`. Compiled away entirely by the
|
|
898
922
|
// bundler — never survives into emitted Luau.
|
|
899
923
|
parseImportStatement() {
|
|
900
924
|
const start = this.current();
|
|
901
925
|
this.advance();
|
|
926
|
+
const next = this.peek(1);
|
|
927
|
+
const isTypeOnly = this.checkIdentifierValue("type") && (next.type === "Punctuator" && next.value === "{" || next.type === "Operator" && next.value === "*" || next.type === "Identifier");
|
|
928
|
+
if (isTypeOnly) this.advance();
|
|
902
929
|
let defaultImport;
|
|
903
930
|
const specifiers = [];
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
this.
|
|
909
|
-
this.
|
|
910
|
-
this.
|
|
931
|
+
let namespaceImport;
|
|
932
|
+
const parseBindings = () => {
|
|
933
|
+
if (this.checkOperator("*")) {
|
|
934
|
+
this.advance();
|
|
935
|
+
if (!this.checkKeyword("as")) this.error("Expected 'as' after 'import *'");
|
|
936
|
+
this.advance();
|
|
937
|
+
namespaceImport = this.parseIdentifier();
|
|
938
|
+
return;
|
|
911
939
|
}
|
|
912
|
-
} else {
|
|
913
940
|
this.expectPunctuator("{");
|
|
914
941
|
this.parseImportSpecifierList(specifiers);
|
|
915
942
|
this.expectPunctuator("}");
|
|
943
|
+
};
|
|
944
|
+
if (this.checkType("Identifier")) {
|
|
945
|
+
const nameTok = this.expectIdentifier();
|
|
946
|
+
defaultImport = { type: "Identifier", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
|
|
947
|
+
if (this.matchPunctuator(",")) parseBindings();
|
|
948
|
+
} else {
|
|
949
|
+
parseBindings();
|
|
916
950
|
}
|
|
917
951
|
if (!this.checkKeyword("from")) {
|
|
918
952
|
this.error("Expected 'from' in import statement");
|
|
@@ -929,7 +963,15 @@ var Parser = class {
|
|
|
929
963
|
raw: sourceTok.raw,
|
|
930
964
|
...spanFrom(sourceTok, sourceTok)
|
|
931
965
|
};
|
|
932
|
-
return {
|
|
966
|
+
return {
|
|
967
|
+
type: "ImportStatement",
|
|
968
|
+
defaultImport,
|
|
969
|
+
namespaceImport,
|
|
970
|
+
specifiers,
|
|
971
|
+
source,
|
|
972
|
+
isTypeOnly: isTypeOnly || void 0,
|
|
973
|
+
...spanFrom(start, this.previous())
|
|
974
|
+
};
|
|
933
975
|
}
|
|
934
976
|
parseImportSpecifierList(out) {
|
|
935
977
|
if (this.checkPunctuator("}")) return;
|
|
@@ -965,7 +1007,7 @@ var Parser = class {
|
|
|
965
1007
|
...spanFrom(sourceTok, sourceTok)
|
|
966
1008
|
};
|
|
967
1009
|
}
|
|
968
|
-
// `export const ...` / `export let ...` / `export
|
|
1010
|
+
// `export const ...` / `export let ...` / `export function ...` /
|
|
969
1011
|
// `export type ...` / `export default <expr>`
|
|
970
1012
|
parseExportStatement() {
|
|
971
1013
|
const start = this.current();
|
|
@@ -983,6 +1025,11 @@ var Parser = class {
|
|
|
983
1025
|
const declaration = this.parseVariableDeclaration();
|
|
984
1026
|
return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
|
|
985
1027
|
}
|
|
1028
|
+
if (this.checkKeyword("function")) {
|
|
1029
|
+
const declaration = this.parseFunctionStatement();
|
|
1030
|
+
if (declaration.type !== "FunctionDeclaration") this.error("An exported function needs a plain name: 'export function name()'");
|
|
1031
|
+
return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
|
|
1032
|
+
}
|
|
986
1033
|
if (this.checkPunctuator("{")) {
|
|
987
1034
|
this.advance();
|
|
988
1035
|
const specifiers = [];
|
|
@@ -1006,15 +1053,15 @@ var Parser = class {
|
|
|
1006
1053
|
const source = this.parseModuleSource();
|
|
1007
1054
|
return { type: "ExportAllStatement", source, ...spanFrom(start, this.previous()) };
|
|
1008
1055
|
}
|
|
1009
|
-
this.error("Expected 'const', 'let', 'type', 'default', '{' or '*' after 'export'");
|
|
1056
|
+
this.error("Expected 'const', 'let', 'function', 'type', 'default', '{' or '*' after 'export'");
|
|
1010
1057
|
}
|
|
1011
|
-
// `const x = ...` / `let x, y =
|
|
1058
|
+
// `const x = ...` / `let x, y = ...`.
|
|
1012
1059
|
// luaut has no `local` — `const` bindings are immutable, `let` mutable.
|
|
1013
1060
|
parseVariableDeclaration() {
|
|
1014
1061
|
const start = this.current();
|
|
1015
1062
|
const kind = this.advance().value;
|
|
1016
|
-
if (this.
|
|
1017
|
-
|
|
1063
|
+
if (this.checkKeyword("function")) {
|
|
1064
|
+
this.error(`A function is declared as 'function name()'; '${kind}' does not apply to functions`);
|
|
1018
1065
|
}
|
|
1019
1066
|
const names = [this.parseBindingTarget(true)];
|
|
1020
1067
|
while (this.matchPunctuator(",")) {
|
|
@@ -1028,31 +1075,6 @@ var Parser = class {
|
|
|
1028
1075
|
}
|
|
1029
1076
|
return { type: "VariableDeclaration", kind, names, init, ...spanFrom(start, this.previous()) };
|
|
1030
1077
|
}
|
|
1031
|
-
/** `const/let function` — `function` already consumed. Collects TS-style
|
|
1032
|
-
* overload signatures. */
|
|
1033
|
-
parseFunctionDeclarationRest(start, kind) {
|
|
1034
|
-
const name = this.parseIdentifier();
|
|
1035
|
-
const signatures = [];
|
|
1036
|
-
while (true) {
|
|
1037
|
-
const head = this.parseFunctionHead();
|
|
1038
|
-
if (this.isOverloadContinuation(name.name, kind)) {
|
|
1039
|
-
signatures.push(this.headToSignature(head));
|
|
1040
|
-
this.advance();
|
|
1041
|
-
this.expectKeyword("function");
|
|
1042
|
-
this.parseIdentifier();
|
|
1043
|
-
continue;
|
|
1044
|
-
}
|
|
1045
|
-
const func = this.headToBody(head);
|
|
1046
|
-
return {
|
|
1047
|
-
type: "FunctionDeclaration",
|
|
1048
|
-
kind,
|
|
1049
|
-
name,
|
|
1050
|
-
func,
|
|
1051
|
-
signatures: signatures.length ? signatures : void 0,
|
|
1052
|
-
...spanFrom(start, this.previous())
|
|
1053
|
-
};
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
1078
|
parseIfStatement() {
|
|
1057
1079
|
const start = this.current();
|
|
1058
1080
|
this.expectKeyword("if");
|
|
@@ -1142,7 +1164,9 @@ var Parser = class {
|
|
|
1142
1164
|
...spanFrom(start, this.previous())
|
|
1143
1165
|
};
|
|
1144
1166
|
}
|
|
1145
|
-
|
|
1167
|
+
/** `function name() end` declares `name`; `function a.b() end` and
|
|
1168
|
+
* `function T:m() end` define a member. */
|
|
1169
|
+
parseFunctionStatement() {
|
|
1146
1170
|
const start = this.current();
|
|
1147
1171
|
this.expectKeyword("function");
|
|
1148
1172
|
const target = this.parseFunctionName();
|
|
@@ -1158,6 +1182,15 @@ var Parser = class {
|
|
|
1158
1182
|
continue;
|
|
1159
1183
|
}
|
|
1160
1184
|
const func = this.headToBody(head);
|
|
1185
|
+
if (simpleName !== void 0) {
|
|
1186
|
+
return {
|
|
1187
|
+
type: "FunctionDeclaration",
|
|
1188
|
+
name: target.base,
|
|
1189
|
+
func,
|
|
1190
|
+
signatures: signatures.length ? signatures : void 0,
|
|
1191
|
+
...spanFrom(start, this.previous())
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1161
1194
|
if (isMethod) {
|
|
1162
1195
|
func.params.unshift({ type: "FunctionParameter", name: "self", ...spanFrom(target, target) });
|
|
1163
1196
|
func.isMethod = true;
|
|
@@ -1174,12 +1207,8 @@ var Parser = class {
|
|
|
1174
1207
|
}
|
|
1175
1208
|
/** After a bodyless function head, is the next token the start of another
|
|
1176
1209
|
* declaration for the same simple `name` (making the head an overload
|
|
1177
|
-
* signature rather than an implementation)?
|
|
1178
|
-
|
|
1179
|
-
isOverloadContinuation(name, kind) {
|
|
1180
|
-
if (kind) {
|
|
1181
|
-
return this.checkKeyword(kind) && this.peek(1).type === "Keyword" && this.peek(1).value === "function" && this.peek(2).type === "Identifier" && this.peek(2).value === name;
|
|
1182
|
-
}
|
|
1210
|
+
* signature rather than an implementation)? */
|
|
1211
|
+
isOverloadContinuation(name) {
|
|
1183
1212
|
return this.checkKeyword("function") && this.peek(1).type === "Identifier" && this.peek(1).value === name;
|
|
1184
1213
|
}
|
|
1185
1214
|
parseFunctionName() {
|
|
@@ -1223,8 +1252,7 @@ var Parser = class {
|
|
|
1223
1252
|
parseTypeAliasStatement() {
|
|
1224
1253
|
const start = this.current();
|
|
1225
1254
|
this.advance();
|
|
1226
|
-
const
|
|
1227
|
-
const name = { type: "Identifier", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
|
|
1255
|
+
const name = this.parseTypeName();
|
|
1228
1256
|
let generics = [];
|
|
1229
1257
|
if (this.checkOperator("<")) {
|
|
1230
1258
|
generics = this.parseGenericTypeParameterList();
|
|
@@ -2487,7 +2515,7 @@ var Analyzer = class {
|
|
|
2487
2515
|
};
|
|
2488
2516
|
}
|
|
2489
2517
|
// ---------------- declaration / resolution primitives ----------------
|
|
2490
|
-
declare(scope, name, kind, node, isConst = false) {
|
|
2518
|
+
declare(scope, name, kind, node, isConst = false, declaredBy) {
|
|
2491
2519
|
if (scope.declarations.has(name) && scope !== this.globalScope) {
|
|
2492
2520
|
this.diagnostics.push({
|
|
2493
2521
|
node,
|
|
@@ -2496,7 +2524,7 @@ var Analyzer = class {
|
|
|
2496
2524
|
});
|
|
2497
2525
|
}
|
|
2498
2526
|
const id = this.nextId++;
|
|
2499
|
-
this.bindings.set(id, { id, name, kind, declarationNode: node, references: [], isConst });
|
|
2527
|
+
this.bindings.set(id, { id, name, kind, declarationNode: node, references: [], isConst, declaredBy });
|
|
2500
2528
|
scope.declarations.set(name, id);
|
|
2501
2529
|
return id;
|
|
2502
2530
|
}
|
|
@@ -2530,6 +2558,19 @@ var Analyzer = class {
|
|
|
2530
2558
|
const id = this.resolve(scope, identifier.name);
|
|
2531
2559
|
this.bindingOf.set(identifier, id);
|
|
2532
2560
|
this.bindings.get(id).references.push(identifier);
|
|
2561
|
+
if (this.typeQueryDepth === 0) this.checkTypeOnly(id, identifier);
|
|
2562
|
+
}
|
|
2563
|
+
/** Inside `typeof x` in a type, where a type-only import may be named. */
|
|
2564
|
+
typeQueryDepth = 0;
|
|
2565
|
+
/** A name from `import type` used as a value. */
|
|
2566
|
+
checkTypeOnly(id, node) {
|
|
2567
|
+
const b = this.bindings.get(id);
|
|
2568
|
+
if (b.declaredBy !== "type") return;
|
|
2569
|
+
this.diagnostics.push({
|
|
2570
|
+
node,
|
|
2571
|
+
message: `'${b.name}' is imported with 'import type' and can only be used as a type`,
|
|
2572
|
+
kind: "type-only"
|
|
2573
|
+
});
|
|
2533
2574
|
}
|
|
2534
2575
|
/** For assignment-like targets (`x = ...`, `function foo() end`): if
|
|
2535
2576
|
* this resolved to a global with no declaration site yet, treat this
|
|
@@ -2546,14 +2587,34 @@ var Analyzer = class {
|
|
|
2546
2587
|
this.bindingOf.set(identifier, id);
|
|
2547
2588
|
this.bindings.get(id).references.push(identifier);
|
|
2548
2589
|
this.recordPossibleGlobalDefinition(id, identifier);
|
|
2590
|
+
this.checkTypeOnly(id, identifier);
|
|
2549
2591
|
this.checkConstAssign(id, identifier);
|
|
2550
2592
|
}
|
|
2593
|
+
/** `Module.x = 1` through `import * as Module`: a module's exports belong
|
|
2594
|
+
* to it and are read-only, as in ES modules. Deeper writes (`Module.x.y`)
|
|
2595
|
+
* change the value, not the module, and are fine. */
|
|
2596
|
+
checkModuleWrite(target) {
|
|
2597
|
+
if (target.type !== "MemberExpression" && target.type !== "IndexExpression") return;
|
|
2598
|
+
if (target.object.type !== "Identifier") return;
|
|
2599
|
+
const id = this.bindingOf.get(target.object);
|
|
2600
|
+
if (id !== void 0 && this.bindings.get(id).declaredBy === "namespace") {
|
|
2601
|
+
this.moduleWriteError(target.object.name, target);
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
moduleWriteError(name, node) {
|
|
2605
|
+
this.diagnostics.push({
|
|
2606
|
+
node,
|
|
2607
|
+
message: `Cannot assign to a member of '${name}' \u2014 a module's exports are read-only`,
|
|
2608
|
+
kind: "const-assign"
|
|
2609
|
+
});
|
|
2610
|
+
}
|
|
2551
2611
|
checkConstAssign(id, node) {
|
|
2552
2612
|
const b = this.bindings.get(id);
|
|
2613
|
+
if (b.declaredBy === "type") return;
|
|
2553
2614
|
if (b.isConst) {
|
|
2554
2615
|
this.diagnostics.push({
|
|
2555
2616
|
node,
|
|
2556
|
-
message: `Cannot assign to '${b.name}' \u2014 it is a const`,
|
|
2617
|
+
message: `Cannot assign to '${b.name}' \u2014 it is ${b.declaredBy === "import" || b.declaredBy === "namespace" ? "an import" : b.declaredBy === "function" ? "a function" : "a const"}`,
|
|
2557
2618
|
kind: "const-assign"
|
|
2558
2619
|
});
|
|
2559
2620
|
}
|
|
@@ -2594,6 +2655,7 @@ var Analyzer = class {
|
|
|
2594
2655
|
const id = this.resolve(scope, t.name);
|
|
2595
2656
|
this.bindingOf.set(t, id);
|
|
2596
2657
|
this.recordPossibleGlobalDefinition(id, t);
|
|
2658
|
+
this.checkTypeOnly(id, t);
|
|
2597
2659
|
this.checkConstAssign(id, t);
|
|
2598
2660
|
return;
|
|
2599
2661
|
}
|
|
@@ -2637,7 +2699,7 @@ var Analyzer = class {
|
|
|
2637
2699
|
return;
|
|
2638
2700
|
}
|
|
2639
2701
|
case "FunctionDeclaration": {
|
|
2640
|
-
this.declare(scope, stmt.name.name, "local", stmt.name,
|
|
2702
|
+
this.declare(scope, stmt.name.name, "local", stmt.name, true, "function");
|
|
2641
2703
|
for (const signature of stmt.signatures ?? []) this.visitSignature(signature, scope);
|
|
2642
2704
|
this.visitFunctionBody(stmt.func, scope);
|
|
2643
2705
|
return;
|
|
@@ -2647,6 +2709,11 @@ var Analyzer = class {
|
|
|
2647
2709
|
this.referenceAsAssignmentTarget(scope, stmt.target.base);
|
|
2648
2710
|
} else {
|
|
2649
2711
|
this.reference(scope, stmt.target.base);
|
|
2712
|
+
const id = this.bindingOf.get(stmt.target.base);
|
|
2713
|
+
const depth = stmt.target.path.length + (stmt.target.method ? 1 : 0);
|
|
2714
|
+
if (id !== void 0 && depth === 1 && this.bindings.get(id).declaredBy === "namespace") {
|
|
2715
|
+
this.moduleWriteError(stmt.target.base.name, stmt.target);
|
|
2716
|
+
}
|
|
2650
2717
|
}
|
|
2651
2718
|
for (const signature of stmt.signatures ?? []) this.visitSignature(signature, scope);
|
|
2652
2719
|
this.visitFunctionBody(stmt.func, scope, stmt.isMethod);
|
|
@@ -2661,6 +2728,7 @@ var Analyzer = class {
|
|
|
2661
2728
|
this.assignPattern(scope, target);
|
|
2662
2729
|
} else {
|
|
2663
2730
|
this.visitExpression(target, scope);
|
|
2731
|
+
this.checkModuleWrite(target);
|
|
2664
2732
|
}
|
|
2665
2733
|
}
|
|
2666
2734
|
return;
|
|
@@ -2673,6 +2741,7 @@ var Analyzer = class {
|
|
|
2673
2741
|
if (id !== void 0) this.checkConstAssign(id, stmt.target);
|
|
2674
2742
|
} else {
|
|
2675
2743
|
this.visitExpression(stmt.target, scope);
|
|
2744
|
+
this.checkModuleWrite(stmt.target);
|
|
2676
2745
|
}
|
|
2677
2746
|
return;
|
|
2678
2747
|
}
|
|
@@ -2726,16 +2795,23 @@ var Analyzer = class {
|
|
|
2726
2795
|
case "DeclareStatement":
|
|
2727
2796
|
this.visitType(stmt.valueType, scope);
|
|
2728
2797
|
return;
|
|
2798
|
+
case "DeclareClassStatement":
|
|
2799
|
+
this.visitType(stmt.body, scope);
|
|
2800
|
+
return;
|
|
2729
2801
|
case "TypeAliasStatement":
|
|
2730
2802
|
case "ExportTypeAliasStatement":
|
|
2731
2803
|
this.visitType(stmt.definition, scope);
|
|
2732
2804
|
return;
|
|
2733
2805
|
case "ImportStatement": {
|
|
2806
|
+
const typeOnly = stmt.isTypeOnly ? "type" : void 0;
|
|
2734
2807
|
if (stmt.defaultImport) {
|
|
2735
|
-
this.declare(scope, stmt.defaultImport.name, "local", stmt.defaultImport);
|
|
2808
|
+
this.declare(scope, stmt.defaultImport.name, "local", stmt.defaultImport, true, typeOnly ?? "import");
|
|
2809
|
+
}
|
|
2810
|
+
if (stmt.namespaceImport) {
|
|
2811
|
+
this.declare(scope, stmt.namespaceImport.name, "local", stmt.namespaceImport, true, typeOnly ?? "namespace");
|
|
2736
2812
|
}
|
|
2737
2813
|
for (const spec of stmt.specifiers) {
|
|
2738
|
-
this.declare(scope, spec.local.name, "local", spec.local);
|
|
2814
|
+
this.declare(scope, spec.local.name, "local", spec.local, true, typeOnly ?? "import");
|
|
2739
2815
|
}
|
|
2740
2816
|
return;
|
|
2741
2817
|
}
|
|
@@ -2794,7 +2870,12 @@ var Analyzer = class {
|
|
|
2794
2870
|
return;
|
|
2795
2871
|
}
|
|
2796
2872
|
if (value.type === "TypeofTypeNode") {
|
|
2797
|
-
this.
|
|
2873
|
+
this.typeQueryDepth++;
|
|
2874
|
+
try {
|
|
2875
|
+
this.visitExpression(value.expression, scope);
|
|
2876
|
+
} finally {
|
|
2877
|
+
this.typeQueryDepth--;
|
|
2878
|
+
}
|
|
2798
2879
|
return;
|
|
2799
2880
|
}
|
|
2800
2881
|
for (const key of Object.keys(value)) {
|
|
@@ -2899,6 +2980,9 @@ function analyzeScopes(program, options = {}) {
|
|
|
2899
2980
|
}
|
|
2900
2981
|
|
|
2901
2982
|
// src/ast/typeModel.ts
|
|
2983
|
+
function isClassType(t) {
|
|
2984
|
+
return t.kind === "object" && t.class !== void 0;
|
|
2985
|
+
}
|
|
2902
2986
|
function typeParam(name, constraint, isConst) {
|
|
2903
2987
|
return { kind: "typeParam", name, constraint, isConst };
|
|
2904
2988
|
}
|
|
@@ -2958,10 +3042,16 @@ function substitute(t, subst) {
|
|
|
2958
3042
|
}
|
|
2959
3043
|
case "function": {
|
|
2960
3044
|
const inner = t.typeParams ? new Map([...subst].filter(([k]) => !t.typeParams.includes(k))) : subst;
|
|
3045
|
+
let params = t.params.map((p) => ({ ...p, type: substitute(p.type, inner) }));
|
|
3046
|
+
let varargs = t.varargs && substitute(t.varargs, inner);
|
|
3047
|
+
if (varargs?.kind === "tuple" && varargs.isPack) {
|
|
3048
|
+
params = [...params, ...varargs.elements.map((type) => ({ type }))];
|
|
3049
|
+
varargs = void 0;
|
|
3050
|
+
}
|
|
2961
3051
|
return {
|
|
2962
3052
|
kind: "function",
|
|
2963
|
-
params
|
|
2964
|
-
varargs
|
|
3053
|
+
params,
|
|
3054
|
+
varargs,
|
|
2965
3055
|
returns: substitute(t.returns, inner),
|
|
2966
3056
|
typeParams: t.typeParams,
|
|
2967
3057
|
predicate: t.predicate && {
|
|
@@ -3041,7 +3131,8 @@ function unify(param, arg, vars, out) {
|
|
|
3041
3131
|
}
|
|
3042
3132
|
return;
|
|
3043
3133
|
case "object":
|
|
3044
|
-
if (
|
|
3134
|
+
if (param.class) return;
|
|
3135
|
+
if (arg.kind === "object" && !arg.class) {
|
|
3045
3136
|
for (const [k, pv] of param.properties) {
|
|
3046
3137
|
const av = arg.properties.get(k);
|
|
3047
3138
|
if (av) unify(pv.type, av.type, vars, out);
|
|
@@ -3124,7 +3215,7 @@ function widen(t) {
|
|
|
3124
3215
|
case "tuple":
|
|
3125
3216
|
return tuple(t.elements.map(widen), t.isPack);
|
|
3126
3217
|
case "object": {
|
|
3127
|
-
if (t.frozen) return t;
|
|
3218
|
+
if (t.frozen || t.class) return t;
|
|
3128
3219
|
const entries = [];
|
|
3129
3220
|
for (const [k, v] of t.properties) entries.push([k, { ...v, type: widen(v.type) }]);
|
|
3130
3221
|
const w = objectType(entries, t.indexer && { key: t.indexer.key, value: widen(t.indexer.value) });
|
|
@@ -3151,6 +3242,10 @@ function isAssignable(rawA, rawB) {
|
|
|
3151
3242
|
if (expandAlias) {
|
|
3152
3243
|
if (a.kind === "genericRef" && b.kind !== "genericRef") a = expandAlias(a);
|
|
3153
3244
|
else if (b.kind === "genericRef" && a.kind !== "genericRef") b = expandAlias(b);
|
|
3245
|
+
else if (a.kind === "genericRef" && b.kind === "genericRef" && a.name !== b.name) {
|
|
3246
|
+
a = expandAlias(a);
|
|
3247
|
+
b = expandAlias(b);
|
|
3248
|
+
}
|
|
3154
3249
|
if (a === b) return true;
|
|
3155
3250
|
}
|
|
3156
3251
|
for (let i = 0; i < comparing.length; i += 2) {
|
|
@@ -3206,6 +3301,8 @@ function isAssignableInner(a, b) {
|
|
|
3206
3301
|
}
|
|
3207
3302
|
if (a.kind === "object") {
|
|
3208
3303
|
if (b.kind !== "object") return false;
|
|
3304
|
+
if (b.class) return a.class !== void 0 && a.class.ancestors.includes(b.class.name);
|
|
3305
|
+
if (a.class && (b.indexer || b.properties.size === 0)) return false;
|
|
3209
3306
|
for (const [name, bp] of b.properties) {
|
|
3210
3307
|
const ap = a.properties.get(name);
|
|
3211
3308
|
if (!ap) {
|
|
@@ -3348,6 +3445,7 @@ function containsFreeTypeParam(t, seen, bound) {
|
|
|
3348
3445
|
case "intersection":
|
|
3349
3446
|
return t.types.some((m) => containsTypeParam(m, seen, bound));
|
|
3350
3447
|
case "object":
|
|
3448
|
+
if (t.class) return false;
|
|
3351
3449
|
return [...t.properties.values()].some((v) => containsTypeParam(v.type, seen, bound)) || !!t.indexer && (containsTypeParam(t.indexer.key, seen, bound) || containsTypeParam(t.indexer.value, seen, bound));
|
|
3352
3450
|
case "function": {
|
|
3353
3451
|
const inner = t.typeParams?.length ? /* @__PURE__ */ new Set([...bound, ...t.typeParams]) : bound;
|
|
@@ -3546,7 +3644,7 @@ function mergeObjectMembers(types) {
|
|
|
3546
3644
|
return expanded !== void 0 && expanded !== t && collect(expanded);
|
|
3547
3645
|
}
|
|
3548
3646
|
if (t.kind === "intersection") return t.types.every(collect);
|
|
3549
|
-
if (t.kind === "object") {
|
|
3647
|
+
if (t.kind === "object" && !t.class) {
|
|
3550
3648
|
objects.push(t);
|
|
3551
3649
|
return true;
|
|
3552
3650
|
}
|
|
@@ -3758,6 +3856,62 @@ function keepsLiterals(paramType) {
|
|
|
3758
3856
|
const members = paramType.constraint.kind === "union" ? paramType.constraint.types : [paramType.constraint];
|
|
3759
3857
|
return members.some((m) => m.kind === "literal");
|
|
3760
3858
|
}
|
|
3859
|
+
var AliasMap = class extends Map {
|
|
3860
|
+
pending = /* @__PURE__ */ new Map();
|
|
3861
|
+
defer(name, resolve5) {
|
|
3862
|
+
super.delete(name);
|
|
3863
|
+
this.pending.set(name, resolve5);
|
|
3864
|
+
}
|
|
3865
|
+
get(name) {
|
|
3866
|
+
const resolved = super.get(name);
|
|
3867
|
+
if (resolved !== void 0) return resolved;
|
|
3868
|
+
const resolve5 = this.pending.get(name);
|
|
3869
|
+
if (!resolve5) return void 0;
|
|
3870
|
+
this.pending.delete(name);
|
|
3871
|
+
const type = resolve5();
|
|
3872
|
+
super.set(name, type);
|
|
3873
|
+
return type;
|
|
3874
|
+
}
|
|
3875
|
+
has(name) {
|
|
3876
|
+
return super.has(name) || (this.pending?.has(name) ?? false);
|
|
3877
|
+
}
|
|
3878
|
+
set(name, type) {
|
|
3879
|
+
this.pending?.delete(name);
|
|
3880
|
+
return super.set(name, type);
|
|
3881
|
+
}
|
|
3882
|
+
delete(name) {
|
|
3883
|
+
const deferred = this.pending?.delete(name) ?? false;
|
|
3884
|
+
return super.delete(name) || deferred;
|
|
3885
|
+
}
|
|
3886
|
+
get size() {
|
|
3887
|
+
return super.size + (this.pending?.size ?? 0);
|
|
3888
|
+
}
|
|
3889
|
+
keys() {
|
|
3890
|
+
return [...super.keys(), ...this.pending?.keys() ?? []][Symbol.iterator]();
|
|
3891
|
+
}
|
|
3892
|
+
entries() {
|
|
3893
|
+
return [...this.keys()].map((name) => [name, this.get(name)])[Symbol.iterator]();
|
|
3894
|
+
}
|
|
3895
|
+
values() {
|
|
3896
|
+
return [...this.keys()].map((name) => this.get(name))[Symbol.iterator]();
|
|
3897
|
+
}
|
|
3898
|
+
forEach(callback, thisArg) {
|
|
3899
|
+
for (const [name, type] of this.entries()) callback.call(thisArg, type, name, this);
|
|
3900
|
+
}
|
|
3901
|
+
[Symbol.iterator]() {
|
|
3902
|
+
return this.entries();
|
|
3903
|
+
}
|
|
3904
|
+
};
|
|
3905
|
+
var METAMETHODS = {
|
|
3906
|
+
"+": "__add",
|
|
3907
|
+
"-": "__sub",
|
|
3908
|
+
"*": "__mul",
|
|
3909
|
+
"/": "__div",
|
|
3910
|
+
"//": "__idiv",
|
|
3911
|
+
"%": "__mod",
|
|
3912
|
+
"^": "__pow",
|
|
3913
|
+
"..": "__concat"
|
|
3914
|
+
};
|
|
3761
3915
|
function posKey(name, line, column) {
|
|
3762
3916
|
return `${name}@${line}:${column}`;
|
|
3763
3917
|
}
|
|
@@ -3778,9 +3932,12 @@ var TypeAnalyzer = class {
|
|
|
3778
3932
|
expectedTypeOf = /* @__PURE__ */ new Map();
|
|
3779
3933
|
/** Public: each alias resolved once (generic aliases keep their params as
|
|
3780
3934
|
* `typeParam` nodes in the body). */
|
|
3781
|
-
aliases =
|
|
3935
|
+
aliases = new AliasMap();
|
|
3782
3936
|
/** Uninstantiated alias definitions, for `Name<Args>` instantiation. */
|
|
3783
3937
|
aliasDefs = /* @__PURE__ */ new Map();
|
|
3938
|
+
/** See `resolveClass`. */
|
|
3939
|
+
classTypes = /* @__PURE__ */ new WeakMap();
|
|
3940
|
+
classMembers = /* @__PURE__ */ new WeakMap();
|
|
3784
3941
|
/** Generic parameters currently in lexical scope (alias body / generic fn),
|
|
3785
3942
|
* with their `extends` constraints resolved. */
|
|
3786
3943
|
typeParamScope = [];
|
|
@@ -3877,6 +4034,13 @@ var TypeAnalyzer = class {
|
|
|
3877
4034
|
if (stmt.type !== "ImportStatement") continue;
|
|
3878
4035
|
const exports = this.moduleFor(stmt.source.value);
|
|
3879
4036
|
if (!exports) continue;
|
|
4037
|
+
if (stmt.namespaceImport) {
|
|
4038
|
+
for (const [name, exported] of exports.types) {
|
|
4039
|
+
const qualified = `${stmt.namespaceImport.name}.${name}`;
|
|
4040
|
+
this.importedTypes.set(qualified, exported);
|
|
4041
|
+
this.aliases.set(qualified, exported.type);
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
3880
4044
|
for (const s of stmt.specifiers) {
|
|
3881
4045
|
const exported = exports.types.get(s.imported.name);
|
|
3882
4046
|
if (exported) {
|
|
@@ -3890,24 +4054,123 @@ var TypeAnalyzer = class {
|
|
|
3890
4054
|
for (const stmt of block.statements) {
|
|
3891
4055
|
const alias = stmt.type === "TypeAliasStatement" ? stmt : stmt.type === "ExportTypeAliasStatement" ? stmt.alias : void 0;
|
|
3892
4056
|
if (alias) this.aliasDefs.set(alias.name.name, { params: alias.generics, node: alias.definition });
|
|
4057
|
+
if (stmt.type === "DeclareClassStatement") {
|
|
4058
|
+
this.aliasDefs.set(stmt.name.name, { params: [], node: stmt.body, class: stmt });
|
|
4059
|
+
}
|
|
3893
4060
|
}
|
|
3894
4061
|
}
|
|
3895
|
-
/**
|
|
3896
|
-
|
|
3897
|
-
|
|
4062
|
+
/** A non-generic definition's type. */
|
|
4063
|
+
resolveDef(def) {
|
|
4064
|
+
return def.class ? this.classType(def.class) : this.resolveType(def.node);
|
|
4065
|
+
}
|
|
4066
|
+
/** One type per class declaration, so every mention of a class is the same
|
|
4067
|
+
* object — its own members included, which refer back to it. */
|
|
4068
|
+
classType(stmt) {
|
|
4069
|
+
return this.classTypes.get(stmt) ?? this.resolveClass(stmt);
|
|
4070
|
+
}
|
|
4071
|
+
/** A class's members are resolved the first time anyone asks for
|
|
4072
|
+
* `properties` — its own from its body, the inherited ones from its
|
|
4073
|
+
* superclass.
|
|
4074
|
+
*
|
|
4075
|
+
* Both have to wait. A definitions file for a whole engine declares
|
|
4076
|
+
* thousands of classes that all refer to one another; resolving each body
|
|
4077
|
+
* as soon as the class is named would resolve every class on every
|
|
4078
|
+
* analysis, when a script touches a handful. And classes refer to one
|
|
4079
|
+
* another constantly — `Object.IsA` mentions a map of every class, each
|
|
4080
|
+
* of which extends `Object` — so while one class resolves, one it extends
|
|
4081
|
+
* may itself be half-resolved; copying its members then would miss some
|
|
4082
|
+
* for good. */
|
|
4083
|
+
resolveClass(stmt) {
|
|
4084
|
+
const name = stmt.name.name;
|
|
4085
|
+
const { ancestors, cyclic } = this.classChain(stmt);
|
|
4086
|
+
const superclass = !cyclic && ancestors.length > 1 ? this.aliasDefs.get(ancestors[1])?.class : void 0;
|
|
4087
|
+
let own;
|
|
4088
|
+
let resolvingOwn = false;
|
|
4089
|
+
const ownMembers = () => {
|
|
4090
|
+
if (own || resolvingOwn) return own;
|
|
4091
|
+
resolvingOwn = true;
|
|
4092
|
+
try {
|
|
4093
|
+
own = this.resolveType(stmt.body);
|
|
4094
|
+
} finally {
|
|
4095
|
+
resolvingOwn = false;
|
|
4096
|
+
}
|
|
4097
|
+
return own;
|
|
4098
|
+
};
|
|
4099
|
+
let complete;
|
|
4100
|
+
const members = () => {
|
|
4101
|
+
if (complete) return complete;
|
|
4102
|
+
const mine = ownMembers();
|
|
4103
|
+
if (!mine) return void 0;
|
|
4104
|
+
const base = superclass ? this.classMembers.get(this.classType(superclass))?.() : void 0;
|
|
4105
|
+
if (superclass && !base) return void 0;
|
|
4106
|
+
return complete = {
|
|
4107
|
+
properties: new Map([...base?.properties ?? [], ...mine.properties]),
|
|
4108
|
+
indexer: mine.indexer ?? base?.indexer
|
|
4109
|
+
};
|
|
4110
|
+
};
|
|
4111
|
+
const type = { kind: "object", name, class: { name, superclass: superclass?.name.name, ancestors } };
|
|
4112
|
+
Object.defineProperties(type, {
|
|
4113
|
+
properties: { enumerable: true, get: () => members()?.properties ?? own?.properties ?? /* @__PURE__ */ new Map() },
|
|
4114
|
+
indexer: { enumerable: true, get: () => members()?.indexer ?? own?.indexer }
|
|
4115
|
+
});
|
|
4116
|
+
this.classTypes.set(stmt, type);
|
|
4117
|
+
this.classMembers.set(type, members);
|
|
4118
|
+
if (this.program.body.statements.includes(stmt)) ownMembers();
|
|
4119
|
+
return type;
|
|
4120
|
+
}
|
|
4121
|
+
/** `extends` must name a class, and the chain must end. */
|
|
4122
|
+
checkClass(stmt) {
|
|
4123
|
+
if (!stmt.superclass || !this.emitDiagnostics) return;
|
|
4124
|
+
const base = stmt.superclass.base;
|
|
4125
|
+
if (!this.aliasDefs.get(base)?.class) {
|
|
4126
|
+
const known = this.aliasDefs.has(base) || this.importedTypes.has(base);
|
|
4127
|
+
this.diagnostics.push({
|
|
4128
|
+
node: stmt.superclass,
|
|
4129
|
+
message: known ? `'${base}' is not a class; a class can only extend another class` : `Cannot find class '${base}'`
|
|
4130
|
+
});
|
|
4131
|
+
} else if (this.classChain(stmt).cyclic) {
|
|
4132
|
+
this.diagnostics.push({ node: stmt.superclass, message: `'${stmt.name.name}' cannot extend itself` });
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
/** The class and the classes it extends, nearest first, read from the
|
|
4136
|
+
* declarations — no type has to be resolved to know them. The walk stops
|
|
4137
|
+
* at a superclass that is not a class. */
|
|
4138
|
+
classChain(stmt) {
|
|
4139
|
+
const ancestors = [stmt.name.name];
|
|
4140
|
+
for (let cls = stmt; cls?.superclass; ) {
|
|
4141
|
+
const base = cls.superclass.base;
|
|
4142
|
+
if (ancestors.includes(base)) return { ancestors, cyclic: true };
|
|
4143
|
+
cls = this.aliasDefs.get(base)?.class;
|
|
4144
|
+
if (!cls) break;
|
|
4145
|
+
ancestors.push(base);
|
|
4146
|
+
}
|
|
4147
|
+
return { ancestors, cyclic: false };
|
|
4148
|
+
}
|
|
4149
|
+
/** Seed global types from `declare` statements. Repeating a function name
|
|
4150
|
+
* builds an *overload set* (an intersection, in declaration order) rather
|
|
4151
|
+
* than replacing — which is how `typeof` gets one signature per result
|
|
4152
|
+
* string. Any other value is simply redeclared: a sourcemap's
|
|
4153
|
+
* `declare script: <this file's instance>` replaces the library's
|
|
4154
|
+
* `declare script: LuaSourceContainer`. */
|
|
3898
4155
|
harvestDeclares(block) {
|
|
3899
4156
|
for (const stmt of block.statements) {
|
|
3900
4157
|
if (stmt.type !== "DeclareStatement") continue;
|
|
3901
4158
|
const t = this.resolveType(stmt.valueType);
|
|
3902
4159
|
const prev = this.libGlobalTypes.get(stmt.name);
|
|
3903
|
-
|
|
4160
|
+
const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
|
|
4161
|
+
this.libGlobalTypes.set(stmt.name, overload ? intersection([prev, t]) : t);
|
|
3904
4162
|
}
|
|
3905
4163
|
}
|
|
3906
4164
|
resolveAllAliases() {
|
|
3907
4165
|
for (const [name, def] of this.aliasDefs) {
|
|
4166
|
+
if (def.class && !this.program.body.statements.includes(def.class)) {
|
|
4167
|
+
const cls = def.class;
|
|
4168
|
+
this.aliases.defer(name, () => this.classType(cls));
|
|
4169
|
+
continue;
|
|
4170
|
+
}
|
|
3908
4171
|
if (containsTypeQuery(def.node)) continue;
|
|
3909
4172
|
this.withTypeParams(def.params, () => {
|
|
3910
|
-
this.aliases.set(name, this.
|
|
4173
|
+
this.aliases.set(name, this.resolveDef(def));
|
|
3911
4174
|
});
|
|
3912
4175
|
}
|
|
3913
4176
|
}
|
|
@@ -3917,7 +4180,7 @@ var TypeAnalyzer = class {
|
|
|
3917
4180
|
for (const [name, def] of this.aliasDefs) {
|
|
3918
4181
|
if (this.aliases.has(name)) continue;
|
|
3919
4182
|
this.withTypeParams(def.params, () => {
|
|
3920
|
-
this.aliases.set(name, this.
|
|
4183
|
+
this.aliases.set(name, this.resolveDef(def));
|
|
3921
4184
|
});
|
|
3922
4185
|
}
|
|
3923
4186
|
return this.aliases;
|
|
@@ -3945,10 +4208,7 @@ var TypeAnalyzer = class {
|
|
|
3945
4208
|
/** Instantiate a generic alias: `Box<number>` -> `{ value: number }`. */
|
|
3946
4209
|
instantiateAlias(def, args) {
|
|
3947
4210
|
if (this.instantiationDepth > 20) return unknownType;
|
|
3948
|
-
const subst =
|
|
3949
|
-
def.params.forEach((p, i) => {
|
|
3950
|
-
subst.set(p.name, args[i] ?? (p.default ? this.resolveType(p.default) : unknownType));
|
|
3951
|
-
});
|
|
4211
|
+
const subst = this.bindTypeArguments(def.params, args);
|
|
3952
4212
|
this.instantiationDepth++;
|
|
3953
4213
|
try {
|
|
3954
4214
|
const body = this.withTypeParams(def.params, () => this.resolveType(def.node));
|
|
@@ -3957,6 +4217,34 @@ var TypeAnalyzer = class {
|
|
|
3957
4217
|
this.instantiationDepth--;
|
|
3958
4218
|
}
|
|
3959
4219
|
}
|
|
4220
|
+
/** Pair written type arguments with the parameters they instantiate. A
|
|
4221
|
+
* pack parameter (`T...`) takes every argument from its position on, as
|
|
4222
|
+
* one pack: `Signal<Instance, string>` binds `T` to `(Instance, string)`,
|
|
4223
|
+
* and `Signal<()>` to the empty pack. Left out, a parameter takes its
|
|
4224
|
+
* default (`T... = ...any` is `any`), or `unknown`. */
|
|
4225
|
+
bindTypeArguments(params, args) {
|
|
4226
|
+
const subst = /* @__PURE__ */ new Map();
|
|
4227
|
+
params.forEach((p, i) => {
|
|
4228
|
+
let arg = args[i];
|
|
4229
|
+
if (p.isPack && i < args.length) {
|
|
4230
|
+
const rest = args.slice(i);
|
|
4231
|
+
const single = rest.length === 1 ? rest[0] : void 0;
|
|
4232
|
+
arg = single && (single.kind === "tuple" && single.isPack || single.kind === "typeParam" || single.kind === "any") ? single : tuple([...rest], true);
|
|
4233
|
+
}
|
|
4234
|
+
subst.set(p.name, arg ?? (p.default ? this.resolveType(p.default) : unknownType));
|
|
4235
|
+
});
|
|
4236
|
+
return subst;
|
|
4237
|
+
}
|
|
4238
|
+
/** An imported type, with its type arguments applied. */
|
|
4239
|
+
importedType(imported, typeArguments) {
|
|
4240
|
+
if (!imported.params.length) return imported.type;
|
|
4241
|
+
const subst = /* @__PURE__ */ new Map();
|
|
4242
|
+
imported.params.forEach((name, i) => {
|
|
4243
|
+
const arg = typeArguments[i];
|
|
4244
|
+
subst.set(name, arg ? this.resolveType(arg) : unknownType);
|
|
4245
|
+
});
|
|
4246
|
+
return this.reduceType(substitute(imported.type, subst));
|
|
4247
|
+
}
|
|
3960
4248
|
// --------------------------------------------------------
|
|
3961
4249
|
// TypeNode -> Type
|
|
3962
4250
|
// --------------------------------------------------------
|
|
@@ -4007,17 +4295,17 @@ var TypeAnalyzer = class {
|
|
|
4007
4295
|
});
|
|
4008
4296
|
}
|
|
4009
4297
|
const imported = this.importedTypes.get(node.base);
|
|
4010
|
-
if (imported)
|
|
4011
|
-
if (!imported.params.length) return imported.type;
|
|
4012
|
-
const subst = /* @__PURE__ */ new Map();
|
|
4013
|
-
imported.params.forEach((name2, i) => {
|
|
4014
|
-
const arg = node.typeArguments[i];
|
|
4015
|
-
subst.set(name2, arg ? this.resolveType(arg) : unknownType);
|
|
4016
|
-
});
|
|
4017
|
-
return this.reduceType(substitute(imported.type, subst));
|
|
4018
|
-
}
|
|
4298
|
+
if (imported) return this.importedType(imported, node.typeArguments);
|
|
4019
4299
|
const lib = this.options.libTypes?.[node.base];
|
|
4020
4300
|
if (lib) return lib;
|
|
4301
|
+
} else if (this.importedTypes.has(name)) {
|
|
4302
|
+
return this.importedType(this.importedTypes.get(name), node.typeArguments);
|
|
4303
|
+
} else if (this.aliasDefs.has(name)) {
|
|
4304
|
+
return this.expand({
|
|
4305
|
+
kind: "genericRef",
|
|
4306
|
+
name,
|
|
4307
|
+
typeArguments: node.typeArguments.map((a) => this.resolveType(a))
|
|
4308
|
+
});
|
|
4021
4309
|
}
|
|
4022
4310
|
return {
|
|
4023
4311
|
kind: "genericRef",
|
|
@@ -4144,6 +4432,7 @@ var TypeAnalyzer = class {
|
|
|
4144
4432
|
return this.resolveType(node.typeAnnotation);
|
|
4145
4433
|
case "TypePackNode": {
|
|
4146
4434
|
if (node.types.length === 1 && !node.hasVarargs) return this.resolveType(node.types[0]);
|
|
4435
|
+
if (!node.types.length && node.varargType) return this.resolveType(node.varargType);
|
|
4147
4436
|
return tuple(node.types.map((t) => this.resolveType(t)), true);
|
|
4148
4437
|
}
|
|
4149
4438
|
}
|
|
@@ -4167,7 +4456,7 @@ var TypeAnalyzer = class {
|
|
|
4167
4456
|
this.reduceDepth++;
|
|
4168
4457
|
try {
|
|
4169
4458
|
const result = this.reduceTypeInner(t);
|
|
4170
|
-
this.reduceCache.set(t, result);
|
|
4459
|
+
if (result.kind !== "keyof") this.reduceCache.set(t, result);
|
|
4171
4460
|
return result;
|
|
4172
4461
|
} finally {
|
|
4173
4462
|
this.reduceDepth--;
|
|
@@ -4179,6 +4468,7 @@ var TypeAnalyzer = class {
|
|
|
4179
4468
|
case "keyof": {
|
|
4180
4469
|
const target = this.reduceType(t.target);
|
|
4181
4470
|
if (containsTypeParam(target)) return { kind: "keyof", target };
|
|
4471
|
+
if (target.kind === "genericRef" && this.resolvingAliases.has(target.name)) return t;
|
|
4182
4472
|
return this.keysOf(target);
|
|
4183
4473
|
}
|
|
4184
4474
|
case "indexedAccess": {
|
|
@@ -4220,6 +4510,7 @@ var TypeAnalyzer = class {
|
|
|
4220
4510
|
t.predicate
|
|
4221
4511
|
);
|
|
4222
4512
|
case "object": {
|
|
4513
|
+
if (t.class) return t;
|
|
4223
4514
|
const entries = [];
|
|
4224
4515
|
for (const [k, v] of t.properties) entries.push([k, { ...v, type: this.reduceType(v.type) }]);
|
|
4225
4516
|
const reduced = objectType(entries, t.indexer && {
|
|
@@ -4359,6 +4650,7 @@ var TypeAnalyzer = class {
|
|
|
4359
4650
|
t.typeParams
|
|
4360
4651
|
);
|
|
4361
4652
|
case "object": {
|
|
4653
|
+
if (t.class) return t;
|
|
4362
4654
|
const entries = [];
|
|
4363
4655
|
for (const [k, v] of t.properties) entries.push([k, { ...v, type: this.stripInfer(v.type, bindings) }]);
|
|
4364
4656
|
return objectType(entries, t.indexer && {
|
|
@@ -4412,6 +4704,11 @@ var TypeAnalyzer = class {
|
|
|
4412
4704
|
visitStatement(stmt, env) {
|
|
4413
4705
|
switch (stmt.type) {
|
|
4414
4706
|
case "VariableDeclaration": {
|
|
4707
|
+
stmt.names.forEach((target, i) => {
|
|
4708
|
+
if (target.type === "IdentifierPattern" && target.typeAnnotation && stmt.init[i]) {
|
|
4709
|
+
this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
|
|
4710
|
+
}
|
|
4711
|
+
});
|
|
4415
4712
|
const { types: valueTypes, sources } = this.valueList(stmt.init, env);
|
|
4416
4713
|
stmt.names.forEach((target, i) => {
|
|
4417
4714
|
const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
|
|
@@ -4472,6 +4769,16 @@ var TypeAnalyzer = class {
|
|
|
4472
4769
|
return;
|
|
4473
4770
|
}
|
|
4474
4771
|
case "AssignmentStatement": {
|
|
4772
|
+
stmt.targets.forEach((target, i) => {
|
|
4773
|
+
const value = stmt.values[i];
|
|
4774
|
+
if (!value) return;
|
|
4775
|
+
if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
4776
|
+
this.applyContext(value, this.infer(target, env));
|
|
4777
|
+
} else if (target.type === "Identifier") {
|
|
4778
|
+
const id = this.bindingIdOf(target);
|
|
4779
|
+
if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
|
|
4780
|
+
}
|
|
4781
|
+
});
|
|
4475
4782
|
const { types: valueTypes, sources } = this.valueList(stmt.values, env);
|
|
4476
4783
|
stmt.targets.forEach((target, i) => {
|
|
4477
4784
|
const vt = valueTypes[i] ?? unknownType;
|
|
@@ -4596,6 +4903,14 @@ var TypeAnalyzer = class {
|
|
|
4596
4903
|
};
|
|
4597
4904
|
if (resolving && !exports) report(stmt.source, `Cannot find module '${specifier}'`);
|
|
4598
4905
|
const usable = exports && !exports.partial ? exports : void 0;
|
|
4906
|
+
if (stmt.namespaceImport) {
|
|
4907
|
+
const id = this.bindingIdByName(stmt.namespaceImport.name, stmt.namespaceImport);
|
|
4908
|
+
if (id !== void 0) {
|
|
4909
|
+
const members = [...usable?.values ?? []].map(([name, type]) => [name, { type, optional: false, readonly: true }]);
|
|
4910
|
+
if (usable?.default) members.push(["default", { type: usable.default, optional: false, readonly: true }]);
|
|
4911
|
+
this.bindingType.set(id, usable ? objectType(members) : anyType);
|
|
4912
|
+
}
|
|
4913
|
+
}
|
|
4599
4914
|
if (stmt.defaultImport) {
|
|
4600
4915
|
if (usable && usable.default === void 0) {
|
|
4601
4916
|
report(stmt.defaultImport, `Module '${specifier}' has no default export`);
|
|
@@ -4616,6 +4931,9 @@ var TypeAnalyzer = class {
|
|
|
4616
4931
|
case "BreakStatement":
|
|
4617
4932
|
this.breakStates[this.breakStates.length - 1]?.push(forkEnv(env));
|
|
4618
4933
|
return;
|
|
4934
|
+
case "DeclareClassStatement":
|
|
4935
|
+
this.checkClass(stmt);
|
|
4936
|
+
return;
|
|
4619
4937
|
case "ContinueStatement":
|
|
4620
4938
|
case "TypeAliasStatement":
|
|
4621
4939
|
case "ExportTypeAliasStatement":
|
|
@@ -4737,7 +5055,89 @@ var TypeAnalyzer = class {
|
|
|
4737
5055
|
}
|
|
4738
5056
|
if (p.pattern) return this.patternToType(p.pattern, env);
|
|
4739
5057
|
if (p.default) return widen(this.infer(p.default, env));
|
|
4740
|
-
return anyType;
|
|
5058
|
+
return this.contextualParams.get(p) ?? anyType;
|
|
5059
|
+
}
|
|
5060
|
+
/** What a function expression's unannotated parameters are, from where
|
|
5061
|
+
* it is written — see `applyContext`. */
|
|
5062
|
+
contextualParams = /* @__PURE__ */ new WeakMap();
|
|
5063
|
+
/** `expected` is the type the surroundings want for `expr`. A function
|
|
5064
|
+
* expression written there takes its unannotated parameters' types from
|
|
5065
|
+
* it, as in TypeScript: `signal:Connect(function(player) ... end)` knows
|
|
5066
|
+
* `player` from `Connect`'s callback type. Anything else is inferred as
|
|
5067
|
+
* usual. */
|
|
5068
|
+
applyContext(expr, expected) {
|
|
5069
|
+
let e = expr;
|
|
5070
|
+
while (e.type === "ParenthesizedExpression") e = e.expression;
|
|
5071
|
+
if (!expected) return;
|
|
5072
|
+
if (e.type === "ArrayExpression") return this.applyArrayContext(e, expected);
|
|
5073
|
+
if (e.type === "TableExpression") return this.applyTableContext(e, expected);
|
|
5074
|
+
if (e.type !== "FunctionExpression") return;
|
|
5075
|
+
const members = expected.kind === "union" ? expected.types : [expected];
|
|
5076
|
+
const signatures = members.flatMap((m) => this.overloadsOf(this.expand(m)));
|
|
5077
|
+
if (!signatures.length) return;
|
|
5078
|
+
e.func.params.forEach((p, k) => {
|
|
5079
|
+
if (p.typeAnnotation || p.pattern || p.default) return;
|
|
5080
|
+
const candidates = [];
|
|
5081
|
+
for (const signature of signatures) {
|
|
5082
|
+
const t2 = signature.params[k]?.type ?? signature.varargs;
|
|
5083
|
+
if (t2) candidates.push(t2);
|
|
5084
|
+
}
|
|
5085
|
+
if (!candidates.length) return;
|
|
5086
|
+
const t = union(candidates);
|
|
5087
|
+
this.contextualParams.set(p, containsTypeParam(t) ? anyType : t);
|
|
5088
|
+
});
|
|
5089
|
+
}
|
|
5090
|
+
/** What an array literal is expected to be: an empty one takes that type
|
|
5091
|
+
* outright — `let queue: thread[] = []` is a `thread[]`, as in TypeScript
|
|
5092
|
+
* — and the elements of any other get the element type as their own
|
|
5093
|
+
* context. */
|
|
5094
|
+
contextualArrays = /* @__PURE__ */ new WeakMap();
|
|
5095
|
+
applyArrayContext(e, expected) {
|
|
5096
|
+
const target = this.expectedMembers(expected).find((m) => m.kind === "array" || m.kind === "tuple");
|
|
5097
|
+
if (!target) return;
|
|
5098
|
+
if (!e.elements.length) {
|
|
5099
|
+
if (!containsTypeParam(target)) this.contextualArrays.set(e, target);
|
|
5100
|
+
return;
|
|
5101
|
+
}
|
|
5102
|
+
e.elements.forEach((element, i) => {
|
|
5103
|
+
if (element.type === "SpreadElement") return;
|
|
5104
|
+
const elementType = target.kind === "array" ? target.element : target.elements[i];
|
|
5105
|
+
this.applyContext(element, elementType);
|
|
5106
|
+
});
|
|
5107
|
+
}
|
|
5108
|
+
/** `{ list: [] }` where `{ list: thread[] }` is expected: each field's
|
|
5109
|
+
* value gets its property's type as context. */
|
|
5110
|
+
applyTableContext(e, expected) {
|
|
5111
|
+
const objects = this.expectedMembers(expected).filter((m) => m.kind === "object");
|
|
5112
|
+
if (!objects.length) return;
|
|
5113
|
+
for (const field of e.fields) {
|
|
5114
|
+
if (field.type !== "TableFieldNamed") continue;
|
|
5115
|
+
const key = field.key.type === "Identifier" ? field.key.name : field.key.value;
|
|
5116
|
+
const types = objects.flatMap((o) => {
|
|
5117
|
+
const property = o.properties.get(key);
|
|
5118
|
+
return property ? [property.type] : o.indexer ? [o.indexer.value] : [];
|
|
5119
|
+
});
|
|
5120
|
+
if (types.length) this.applyContext(field.value, union(types));
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5123
|
+
/** The members of an expected type worth matching a literal against:
|
|
5124
|
+
* aliases seen through, `nil` left out. */
|
|
5125
|
+
expectedMembers(expected) {
|
|
5126
|
+
const t = this.expand(expected);
|
|
5127
|
+
const members = t.kind === "union" ? t.types : [t];
|
|
5128
|
+
return members.map((m) => this.expand(m)).filter((m) => !(m.kind === "primitive" && m.name === "nil"));
|
|
5129
|
+
}
|
|
5130
|
+
/** The parameter type each written argument lands on, across `fns`. */
|
|
5131
|
+
expectedArguments(written, fns, selfOf) {
|
|
5132
|
+
return written.map((_, j) => {
|
|
5133
|
+
const candidates = [];
|
|
5134
|
+
for (const f of fns) {
|
|
5135
|
+
const i = j + selfOf(f);
|
|
5136
|
+
const param = i < f.params.length ? this.boundParams(f)[i] : f.varargs;
|
|
5137
|
+
if (param) candidates.push(param);
|
|
5138
|
+
}
|
|
5139
|
+
return candidates.length ? union(candidates) : void 0;
|
|
5140
|
+
});
|
|
4741
5141
|
}
|
|
4742
5142
|
/** Synthesize a type from a destructuring pattern used without an
|
|
4743
5143
|
* annotation (`function f({ a, b = 1 })`). */
|
|
@@ -4789,7 +5189,8 @@ var TypeAnalyzer = class {
|
|
|
4789
5189
|
f.params.forEach((p, i) => {
|
|
4790
5190
|
const arg = argTypes[i];
|
|
4791
5191
|
if (arg === void 0) return;
|
|
4792
|
-
|
|
5192
|
+
const param = p.type.kind === "typeParam" && p.type.constraint ? { ...p.type, constraint: this.reduceType(p.type.constraint) } : p.type;
|
|
5193
|
+
unify(p.type, keepsLiterals(param) ? arg : widen(arg), vars, subst);
|
|
4793
5194
|
});
|
|
4794
5195
|
for (const name of f.typeParams ?? []) if (!subst.has(name)) subst.set(name, unknownType);
|
|
4795
5196
|
return subst;
|
|
@@ -4850,12 +5251,13 @@ var TypeAnalyzer = class {
|
|
|
4850
5251
|
return;
|
|
4851
5252
|
}
|
|
4852
5253
|
const t = value;
|
|
5254
|
+
if (t.kind === "object" && t.class) return;
|
|
4853
5255
|
if (t.kind === "typeParam" && typeof t.name === "string" && bounds.has(t.name) && t.constraint && !containsTypeParam(t.constraint)) {
|
|
4854
5256
|
bounds.set(t.name, this.reduceType(t.constraint));
|
|
4855
5257
|
}
|
|
4856
5258
|
for (const child of Object.values(value)) walk(child);
|
|
4857
5259
|
};
|
|
4858
|
-
for (const p of f.params) walk(p.type);
|
|
5260
|
+
for (const p of f.params) if (containsTypeParam(p.type)) walk(p.type);
|
|
4859
5261
|
return f.params.map((p) => substitute(p.type, bounds));
|
|
4860
5262
|
}
|
|
4861
5263
|
/** Record what each written argument is expected to be — see
|
|
@@ -5194,7 +5596,7 @@ var TypeAnalyzer = class {
|
|
|
5194
5596
|
this.expandCache.set(key, t);
|
|
5195
5597
|
this.resolvingAliases.add(t.name);
|
|
5196
5598
|
try {
|
|
5197
|
-
const r = def.params.length ? this.instantiateAlias(def, t.typeArguments) : this.
|
|
5599
|
+
const r = def.params.length ? this.instantiateAlias(def, t.typeArguments) : this.resolveDef(def);
|
|
5198
5600
|
const named = def.params.length === 0 && (r.kind === "object" || r.kind === "intersection") && !r.name ? { ...r, name: t.name } : r;
|
|
5199
5601
|
this.expandCache.set(key, named);
|
|
5200
5602
|
return named;
|
|
@@ -5299,8 +5701,9 @@ var TypeAnalyzer = class {
|
|
|
5299
5701
|
return this.resolveType(expr.typeAnnotation);
|
|
5300
5702
|
}
|
|
5301
5703
|
case "SatisfiesExpression": {
|
|
5302
|
-
const actual = this.infer(expr.expression, env);
|
|
5303
5704
|
const declared = this.resolveType(expr.typeAnnotation);
|
|
5705
|
+
this.applyContext(expr.expression, declared);
|
|
5706
|
+
const actual = this.infer(expr.expression, env);
|
|
5304
5707
|
if (this.emitDiagnostics && declared.kind !== "any" && !this.fitsAnnotation(expr.expression, declared, actual, env)) {
|
|
5305
5708
|
this.diagnostics.push({
|
|
5306
5709
|
node: expr,
|
|
@@ -5317,9 +5720,9 @@ var TypeAnalyzer = class {
|
|
|
5317
5720
|
case "not":
|
|
5318
5721
|
return booleanType;
|
|
5319
5722
|
case "-":
|
|
5320
|
-
return numberType;
|
|
5723
|
+
return this.operatorResult(expr, "-", arg, void 0) ?? numberType;
|
|
5321
5724
|
case "#":
|
|
5322
|
-
return numberType;
|
|
5725
|
+
return this.operatorResult(expr, "#", arg, void 0) ?? numberType;
|
|
5323
5726
|
}
|
|
5324
5727
|
return arg;
|
|
5325
5728
|
}
|
|
@@ -5341,7 +5744,7 @@ var TypeAnalyzer = class {
|
|
|
5341
5744
|
const r = this.infer(expr.right, env);
|
|
5342
5745
|
switch (op) {
|
|
5343
5746
|
case "..":
|
|
5344
|
-
return stringType;
|
|
5747
|
+
return this.operatorResult(expr, op, l, r) ?? stringType;
|
|
5345
5748
|
case "==":
|
|
5346
5749
|
case "~=":
|
|
5347
5750
|
case "<":
|
|
@@ -5356,7 +5759,7 @@ var TypeAnalyzer = class {
|
|
|
5356
5759
|
case "//":
|
|
5357
5760
|
case "%":
|
|
5358
5761
|
case "^":
|
|
5359
|
-
return numberType;
|
|
5762
|
+
return this.operatorResult(expr, op, l, r) ?? numberType;
|
|
5360
5763
|
}
|
|
5361
5764
|
return union([l, r]);
|
|
5362
5765
|
}
|
|
@@ -5375,8 +5778,10 @@ var TypeAnalyzer = class {
|
|
|
5375
5778
|
}
|
|
5376
5779
|
case "CallExpression": {
|
|
5377
5780
|
const callee = this.infer(expr.callee, env);
|
|
5378
|
-
const argTypes = expr.arguments.map((a) => this.infer(a, env));
|
|
5379
5781
|
const fns = this.overloadsOf(callee);
|
|
5782
|
+
const expected = this.expectedArguments(expr.arguments, fns, () => 0);
|
|
5783
|
+
expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
|
|
5784
|
+
const argTypes = expr.arguments.map((a) => this.infer(a, env));
|
|
5380
5785
|
if (fns.length) {
|
|
5381
5786
|
this.recordExpected(expr.arguments, fns, () => 0);
|
|
5382
5787
|
const arityFits = this.checkArity(expr, fns, argTypes.length, 0);
|
|
@@ -5391,8 +5796,10 @@ var TypeAnalyzer = class {
|
|
|
5391
5796
|
}
|
|
5392
5797
|
case "MethodCallExpression": {
|
|
5393
5798
|
const objType = this.infer(expr.object, env);
|
|
5394
|
-
const argTypes = expr.arguments.map((a) => this.infer(a, env));
|
|
5395
5799
|
const fns = this.overloadsOf(this.propertyType(objType, expr.method.name));
|
|
5800
|
+
const expected = this.expectedArguments(expr.arguments, fns, (f) => this.takesSelf(f) ? 1 : 0);
|
|
5801
|
+
expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
|
|
5802
|
+
const argTypes = expr.arguments.map((a) => this.infer(a, env));
|
|
5396
5803
|
if (fns.length) {
|
|
5397
5804
|
const withSelf = (f) => this.takesSelf(f) ? [objType, ...argTypes] : argTypes;
|
|
5398
5805
|
const selfOf = (f) => this.takesSelf(f) ? 1 : 0;
|
|
@@ -5424,6 +5831,8 @@ var TypeAnalyzer = class {
|
|
|
5424
5831
|
}
|
|
5425
5832
|
}
|
|
5426
5833
|
inferArray(expr, env, asConst) {
|
|
5834
|
+
const contextual = this.contextualArrays.get(expr);
|
|
5835
|
+
if (contextual && !asConst) return contextual;
|
|
5427
5836
|
const elems = [];
|
|
5428
5837
|
let hadSpread = false;
|
|
5429
5838
|
for (const el of expr.elements) {
|
|
@@ -5784,6 +6193,35 @@ var TypeAnalyzer = class {
|
|
|
5784
6193
|
this.selfType = saved;
|
|
5785
6194
|
}
|
|
5786
6195
|
}
|
|
6196
|
+
/** What an operator on a value with metamethods gives: `a + b` calls
|
|
6197
|
+
* `__add` on `a`, or failing that on `b` with the operands swapped — the
|
|
6198
|
+
* order Luau tries them in. That is how `Vector3 + Vector3`, `CFrame *
|
|
6199
|
+
* Vector3` and `2 * vector` get their types from the declarations.
|
|
6200
|
+
* `undefined` when neither operand declares the metamethod; an operand
|
|
6201
|
+
* that declares it but accepts neither argument is reported. */
|
|
6202
|
+
operatorResult(node, op, left, right) {
|
|
6203
|
+
const name = right === void 0 ? op === "-" ? "__unm" : "__len" : METAMETHODS[op];
|
|
6204
|
+
if (!name) return void 0;
|
|
6205
|
+
const candidates = right === void 0 ? [[left, void 0]] : [[left, right], [right, left]];
|
|
6206
|
+
let declared;
|
|
6207
|
+
for (const [receiver, other] of candidates) {
|
|
6208
|
+
const t = this.expand(receiver);
|
|
6209
|
+
const method = t.kind === "object" ? t.properties.get(name) : void 0;
|
|
6210
|
+
if (!method) continue;
|
|
6211
|
+
declared ??= receiver;
|
|
6212
|
+
const args = other === void 0 ? [receiver] : [receiver, other];
|
|
6213
|
+
const picked = this.pickOverload(this.overloadsOf(method.type), args);
|
|
6214
|
+
if (picked) return this.callReturn(picked, args);
|
|
6215
|
+
}
|
|
6216
|
+
if (declared && this.emitDiagnostics) {
|
|
6217
|
+
this.diagnostics.push({
|
|
6218
|
+
node,
|
|
6219
|
+
message: right === void 0 ? `Operator '${op}' cannot be applied to type '${formatType(left)}'` : `Operator '${op}' cannot be applied to types '${formatType(left)}' and '${formatType(right)}'`
|
|
6220
|
+
});
|
|
6221
|
+
return anyType;
|
|
6222
|
+
}
|
|
6223
|
+
return void 0;
|
|
6224
|
+
}
|
|
5787
6225
|
/** Does this signature take the receiver as its first parameter?
|
|
5788
6226
|
*
|
|
5789
6227
|
* Luau's `:` is sugar both ways: `function T:m(a)` declares
|
|
@@ -6048,13 +6486,13 @@ function resolveTypeLibraries(config, host = nodeHost) {
|
|
|
6048
6486
|
else problems.push({ file: config.path, message: `'${entry}' has no ${ENTRY_FILE} (or 'luaut.types' in its package.json)`, ...entryPosition(config, entry) });
|
|
6049
6487
|
continue;
|
|
6050
6488
|
}
|
|
6051
|
-
const
|
|
6052
|
-
const found =
|
|
6489
|
+
const name = entry.startsWith("@luaut/") ? entry : `@luaut/${entry}`;
|
|
6490
|
+
const found = findPackage(name, config.directory, host);
|
|
6053
6491
|
if (found) addPackage(found.directory, found.file, /* @__PURE__ */ new Set());
|
|
6054
6492
|
else {
|
|
6055
6493
|
problems.push({
|
|
6056
6494
|
file: config.path,
|
|
6057
|
-
message: `Cannot find type library '${
|
|
6495
|
+
message: `Cannot find type library '${name}'. Install it with: npm i -D ${name}`,
|
|
6058
6496
|
...entryPosition(config, entry)
|
|
6059
6497
|
});
|
|
6060
6498
|
}
|
|
@@ -6157,7 +6595,6 @@ function sourceMapTypes(text, path, options) {
|
|
|
6157
6595
|
const lines = [];
|
|
6158
6596
|
const aliasOfFile = /* @__PURE__ */ new Map();
|
|
6159
6597
|
const used = /* @__PURE__ */ new Set();
|
|
6160
|
-
const canOmit = options.classes.has("Omit");
|
|
6161
6598
|
const aliasOfNode = /* @__PURE__ */ new Map();
|
|
6162
6599
|
const aliasFor = (segments) => {
|
|
6163
6600
|
const base = `SourceMap_${segments.map((s) => s.replace(/[^A-Za-z0-9_]/g, "_")).join("_")}`;
|
|
@@ -6173,7 +6610,7 @@ function sourceMapTypes(text, path, options) {
|
|
|
6173
6610
|
const className = IDENTIFIER.test(node.className) && options.classes.has(node.className) ? node.className : "Instance";
|
|
6174
6611
|
const taken = options.membersOf?.(className) ?? INSTANCE_MEMBERS;
|
|
6175
6612
|
const members = [];
|
|
6176
|
-
if (parent
|
|
6613
|
+
if (parent) members.push(`Parent: ${parent}`);
|
|
6177
6614
|
const named = /* @__PURE__ */ new Set();
|
|
6178
6615
|
for (const child of node.children ?? []) {
|
|
6179
6616
|
if (!isNode(child)) continue;
|
|
@@ -6182,8 +6619,7 @@ function sourceMapTypes(text, path, options) {
|
|
|
6182
6619
|
named.add(child.name);
|
|
6183
6620
|
members.push(`${child.name}: ${childAlias}`);
|
|
6184
6621
|
}
|
|
6185
|
-
|
|
6186
|
-
lines.push(`type ${alias} = ${members.length ? `${base} & { ${members.join(", ")} }` : base}`);
|
|
6622
|
+
lines.push(`declare class ${alias} extends ${className} { ${members.join(", ")} }`);
|
|
6187
6623
|
return alias;
|
|
6188
6624
|
};
|
|
6189
6625
|
const rootAlias = visit(root, [root.name], void 0);
|
|
@@ -6261,6 +6697,7 @@ export {
|
|
|
6261
6697
|
getBinding,
|
|
6262
6698
|
intersection,
|
|
6263
6699
|
isAssignable,
|
|
6700
|
+
isClassType,
|
|
6264
6701
|
isGlobal,
|
|
6265
6702
|
isPossiblyFalsy,
|
|
6266
6703
|
isPossiblyTruthy,
|