luaut-parser 1.0.0 → 1.1.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 +17 -2
- package/dist/index.cjs +381 -25
- package/dist/index.d.cts +73 -7
- package/dist/index.d.ts +73 -7
- package/dist/index.js +380 -25
- package/dist/luau.d.luaut +245 -244
- package/dist/roblox.d.luaut +503 -503
- package/package.json +43 -40
package/dist/index.js
CHANGED
|
@@ -541,6 +541,17 @@ function spanFrom(start, end) {
|
|
|
541
541
|
column: { start: start.column.start, end: end.column.end }
|
|
542
542
|
};
|
|
543
543
|
}
|
|
544
|
+
function tokenIdentifier(t) {
|
|
545
|
+
return { type: "Identifier", name: t.value, ...spanFrom(t, t) };
|
|
546
|
+
}
|
|
547
|
+
function nameIdentifier(name, at) {
|
|
548
|
+
return {
|
|
549
|
+
type: "Identifier",
|
|
550
|
+
name,
|
|
551
|
+
line: { start: at.line.start, end: at.line.start },
|
|
552
|
+
column: { start: at.column.start, end: at.column.start + name.length }
|
|
553
|
+
};
|
|
554
|
+
}
|
|
544
555
|
var BINARY_PRECEDENCE = {
|
|
545
556
|
"or": 1,
|
|
546
557
|
"and": 2,
|
|
@@ -859,9 +870,11 @@ var Parser = class {
|
|
|
859
870
|
const params = head.params.map((p) => ({
|
|
860
871
|
type: "FunctionTypeParameter",
|
|
861
872
|
name: p.name || void 0,
|
|
873
|
+
id: p.name ? nameIdentifier(p.name, p) : void 0,
|
|
862
874
|
optional: p.optional,
|
|
863
|
-
typeAnnotation: p.typeAnnotation ?? { type: "TypeReference", base: "any", typeArguments: [],
|
|
864
|
-
|
|
875
|
+
typeAnnotation: p.typeAnnotation ?? { type: "TypeReference", base: "any", typeArguments: [], line: p.line, column: p.column },
|
|
876
|
+
line: p.line,
|
|
877
|
+
column: p.column
|
|
865
878
|
}));
|
|
866
879
|
const valueType2 = {
|
|
867
880
|
type: "FunctionTypeNode",
|
|
@@ -873,12 +886,12 @@ var Parser = class {
|
|
|
873
886
|
predicate: head.predicate,
|
|
874
887
|
...spanFrom(start, this.previous())
|
|
875
888
|
};
|
|
876
|
-
return { type: "DeclareStatement", name: nameTok2.value, valueType: valueType2, ...spanFrom(start, this.previous()) };
|
|
889
|
+
return { type: "DeclareStatement", name: nameTok2.value, id: tokenIdentifier(nameTok2), valueType: valueType2, ...spanFrom(start, this.previous()) };
|
|
877
890
|
}
|
|
878
891
|
const nameTok = this.expectIdentifier();
|
|
879
892
|
this.expectPunctuator(":");
|
|
880
893
|
const valueType = this.parseType();
|
|
881
|
-
return { type: "DeclareStatement", name: nameTok.value, valueType, ...spanFrom(start, this.previous()) };
|
|
894
|
+
return { type: "DeclareStatement", name: nameTok.value, id: tokenIdentifier(nameTok), valueType, ...spanFrom(start, this.previous()) };
|
|
882
895
|
}
|
|
883
896
|
// `import { a, b as c } from '...'` / `import Default from '...'` /
|
|
884
897
|
// `import Default, { a } from '...'`. Compiled away entirely by the
|
|
@@ -937,6 +950,21 @@ var Parser = class {
|
|
|
937
950
|
}
|
|
938
951
|
return { type: "ImportSpecifier", imported, local, ...spanFrom(imported, local) };
|
|
939
952
|
}
|
|
953
|
+
/** `from "<path>"`: consumes `from` and the module string. */
|
|
954
|
+
parseModuleSource() {
|
|
955
|
+
this.advance();
|
|
956
|
+
const sourceTok = this.current();
|
|
957
|
+
if (sourceTok.type !== "Literal" || sourceTok.kind !== "string") {
|
|
958
|
+
this.error("Expected string literal module path after 'from'");
|
|
959
|
+
}
|
|
960
|
+
this.advance();
|
|
961
|
+
return {
|
|
962
|
+
type: "StringLiteral",
|
|
963
|
+
value: sourceTok.value,
|
|
964
|
+
raw: sourceTok.raw,
|
|
965
|
+
...spanFrom(sourceTok, sourceTok)
|
|
966
|
+
};
|
|
967
|
+
}
|
|
940
968
|
// `export const ...` / `export let ...` / `export const function ...` /
|
|
941
969
|
// `export type ...` / `export default <expr>`
|
|
942
970
|
parseExportStatement() {
|
|
@@ -955,7 +983,30 @@ var Parser = class {
|
|
|
955
983
|
const declaration = this.parseVariableDeclaration();
|
|
956
984
|
return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
|
|
957
985
|
}
|
|
958
|
-
this.
|
|
986
|
+
if (this.checkPunctuator("{")) {
|
|
987
|
+
this.advance();
|
|
988
|
+
const specifiers = [];
|
|
989
|
+
while (!this.checkPunctuator("}")) {
|
|
990
|
+
const local = this.parseIdentifier();
|
|
991
|
+
let exported = local;
|
|
992
|
+
if (this.checkKeyword("as")) {
|
|
993
|
+
this.advance();
|
|
994
|
+
exported = this.parseIdentifier();
|
|
995
|
+
}
|
|
996
|
+
specifiers.push({ type: "ExportSpecifier", local, exported, ...spanFrom(local, exported) });
|
|
997
|
+
if (!this.matchPunctuator(",")) break;
|
|
998
|
+
}
|
|
999
|
+
this.expectPunctuator("}");
|
|
1000
|
+
const source = this.checkKeyword("from") ? this.parseModuleSource() : void 0;
|
|
1001
|
+
return { type: "ExportNamedStatement", specifiers, source, ...spanFrom(start, this.previous()) };
|
|
1002
|
+
}
|
|
1003
|
+
if (this.checkOperator("*")) {
|
|
1004
|
+
this.advance();
|
|
1005
|
+
if (!this.checkKeyword("from")) this.error("Expected 'from' after 'export *'");
|
|
1006
|
+
const source = this.parseModuleSource();
|
|
1007
|
+
return { type: "ExportAllStatement", source, ...spanFrom(start, this.previous()) };
|
|
1008
|
+
}
|
|
1009
|
+
this.error("Expected 'const', 'let', 'type', 'default', '{' or '*' after 'export'");
|
|
959
1010
|
}
|
|
960
1011
|
// `const x = ...` / `let x, y = ...` / `const function f() ... end`.
|
|
961
1012
|
// luaut has no `local` — `const` bindings are immutable, `let` mutable.
|
|
@@ -1989,8 +2040,9 @@ var Parser = class {
|
|
|
1989
2040
|
}
|
|
1990
2041
|
if (this.checkIdentifierValue("infer") && this.peek(1).type === "Identifier") {
|
|
1991
2042
|
this.advance();
|
|
1992
|
-
const
|
|
1993
|
-
|
|
2043
|
+
const nameTok = this.expectIdentifier();
|
|
2044
|
+
const name = nameTok.value;
|
|
2045
|
+
return { type: "InferTypeNode", name, id: tokenIdentifier(nameTok), ...spanFrom(t, this.previous()) };
|
|
1994
2046
|
}
|
|
1995
2047
|
if (t.type === "Operator" && t.value === "<") {
|
|
1996
2048
|
const generics = this.parseGenericTypeParameterList();
|
|
@@ -2027,6 +2079,16 @@ var Parser = class {
|
|
|
2027
2079
|
this.expectPunctuator(")");
|
|
2028
2080
|
return { type: "TypeofTypeNode", expression, ...spanFrom(t, this.previous()) };
|
|
2029
2081
|
}
|
|
2082
|
+
if (t.type === "Identifier" && t.value === "typeof" && this.peek(1).type === "Identifier") {
|
|
2083
|
+
this.advance();
|
|
2084
|
+
let expression = this.parseIdentifier();
|
|
2085
|
+
while (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
|
|
2086
|
+
this.advance();
|
|
2087
|
+
const property = this.parseIdentifier();
|
|
2088
|
+
expression = { type: "MemberExpression", object: expression, property, ...spanFrom(expression, property) };
|
|
2089
|
+
}
|
|
2090
|
+
return { type: "TypeofTypeNode", expression, ...spanFrom(t, this.previous()) };
|
|
2091
|
+
}
|
|
2030
2092
|
if (t.type === "Literal" && t.kind === "string") {
|
|
2031
2093
|
this.advance();
|
|
2032
2094
|
return { type: "TypeLiteralString", value: t.value, ...spanFrom(t, t) };
|
|
@@ -2089,17 +2151,21 @@ var Parser = class {
|
|
|
2089
2151
|
let name;
|
|
2090
2152
|
let optional2 = false;
|
|
2091
2153
|
const named = this.checkType("Identifier") && this.peek(1).type === "Punctuator" && (this.peek(1).value === ":" || this.peek(1).value === "?" && this.peek(2).type === "Punctuator" && this.peek(2).value === ":");
|
|
2154
|
+
let nameTok;
|
|
2092
2155
|
if (named) {
|
|
2093
|
-
|
|
2156
|
+
const tok = this.expectIdentifier();
|
|
2157
|
+
nameTok = tok;
|
|
2158
|
+
name = tok.value;
|
|
2094
2159
|
optional2 = this.matchPunctuator("?");
|
|
2095
2160
|
this.advance();
|
|
2096
2161
|
}
|
|
2097
|
-
const paramStart = this.current();
|
|
2162
|
+
const paramStart = nameTok ?? this.current();
|
|
2098
2163
|
const typeAnnotation = this.parseType();
|
|
2099
2164
|
params.push({
|
|
2100
2165
|
type: "FunctionTypeParameter",
|
|
2101
2166
|
name,
|
|
2102
2167
|
typeAnnotation,
|
|
2168
|
+
id: nameTok && tokenIdentifier(nameTok),
|
|
2103
2169
|
optional: optional2 || void 0,
|
|
2104
2170
|
...spanFrom(paramStart, this.previous())
|
|
2105
2171
|
});
|
|
@@ -2161,7 +2227,8 @@ var Parser = class {
|
|
|
2161
2227
|
readonly = false;
|
|
2162
2228
|
}
|
|
2163
2229
|
this.expectPunctuator("[");
|
|
2164
|
-
const
|
|
2230
|
+
const parameterTok = this.expectIdentifier();
|
|
2231
|
+
const parameter = parameterTok.value;
|
|
2165
2232
|
this.advance();
|
|
2166
2233
|
const constraint = this.parseType();
|
|
2167
2234
|
let nameType;
|
|
@@ -2185,6 +2252,7 @@ var Parser = class {
|
|
|
2185
2252
|
return {
|
|
2186
2253
|
type: "MappedTypeNode",
|
|
2187
2254
|
parameter,
|
|
2255
|
+
parameterId: tokenIdentifier(parameterTok),
|
|
2188
2256
|
constraint,
|
|
2189
2257
|
nameType,
|
|
2190
2258
|
template,
|
|
@@ -2211,30 +2279,43 @@ var Parser = class {
|
|
|
2211
2279
|
this.expectPunctuator("{");
|
|
2212
2280
|
const properties = [];
|
|
2213
2281
|
while (!this.checkPunctuator("}")) {
|
|
2282
|
+
const propStart = this.current();
|
|
2214
2283
|
if (this.checkPunctuator("[")) {
|
|
2215
2284
|
this.advance();
|
|
2216
2285
|
const keyType = this.parseType();
|
|
2217
2286
|
this.expectPunctuator("]");
|
|
2218
2287
|
this.expectPunctuator(":");
|
|
2219
2288
|
const valueType = this.parseType();
|
|
2220
|
-
properties.push({ type: "TableTypeIndexer", keyType, valueType });
|
|
2289
|
+
properties.push({ type: "TableTypeIndexer", keyType, valueType, ...spanFrom(propStart, this.previous()) });
|
|
2221
2290
|
} else if (this.checkIdentifierValue("readonly") && this.peek(1).type === "Identifier") {
|
|
2222
2291
|
this.advance();
|
|
2223
|
-
const
|
|
2292
|
+
const keyTok = this.expectIdentifier();
|
|
2293
|
+
const name = keyTok.value;
|
|
2224
2294
|
const optional2 = this.matchPunctuator("?");
|
|
2225
2295
|
this.expectPunctuator(":");
|
|
2226
2296
|
const valueType = this.parseType();
|
|
2227
|
-
properties.push({
|
|
2297
|
+
properties.push({
|
|
2298
|
+
type: "TableTypeProperty",
|
|
2299
|
+
name,
|
|
2300
|
+
key: tokenIdentifier(keyTok),
|
|
2301
|
+
valueType,
|
|
2302
|
+
optional: optional2,
|
|
2303
|
+
readonly: true,
|
|
2304
|
+
...spanFrom(propStart, this.previous())
|
|
2305
|
+
});
|
|
2228
2306
|
} else if (this.checkType("Identifier") && (this.peek(1).type === "Punctuator" && this.peek(1).value === ":" || this.peek(1).type === "Punctuator" && this.peek(1).value === "?" && this.peek(2).type === "Punctuator" && this.peek(2).value === ":")) {
|
|
2229
|
-
const
|
|
2307
|
+
const keyTok = this.expectIdentifier();
|
|
2308
|
+
const name = keyTok.value;
|
|
2230
2309
|
const optional2 = this.matchPunctuator("?");
|
|
2231
2310
|
this.expectPunctuator(":");
|
|
2232
2311
|
const valueType = this.parseType();
|
|
2233
2312
|
properties.push({
|
|
2234
2313
|
type: "TableTypeProperty",
|
|
2235
2314
|
name,
|
|
2315
|
+
key: tokenIdentifier(keyTok),
|
|
2236
2316
|
valueType,
|
|
2237
|
-
optional: optional2
|
|
2317
|
+
optional: optional2,
|
|
2318
|
+
...spanFrom(propStart, this.previous())
|
|
2238
2319
|
});
|
|
2239
2320
|
} else {
|
|
2240
2321
|
this.error("Expected object type property ('name: T' or '[K]: V'); use 'T[]' for arrays and '[T, U]' for tuples");
|
|
@@ -2296,6 +2377,7 @@ var Parser = class {
|
|
|
2296
2377
|
list.push({
|
|
2297
2378
|
type: "GenericTypeParameter",
|
|
2298
2379
|
name: nameTok.value,
|
|
2380
|
+
id: tokenIdentifier(nameTok),
|
|
2299
2381
|
isPack,
|
|
2300
2382
|
isConst: isConst || void 0,
|
|
2301
2383
|
constraint,
|
|
@@ -2425,6 +2507,15 @@ var Analyzer = class {
|
|
|
2425
2507
|
}
|
|
2426
2508
|
return this.getOrCreateGlobalBinding(name);
|
|
2427
2509
|
}
|
|
2510
|
+
/** Like `resolve`, but never creates a global: `undefined` when no
|
|
2511
|
+
* enclosing scope declares the name. */
|
|
2512
|
+
lookup(scope, name) {
|
|
2513
|
+
for (let s = scope; s; s = s.parent) {
|
|
2514
|
+
const id = s.declarations.get(name);
|
|
2515
|
+
if (id !== void 0) return id;
|
|
2516
|
+
}
|
|
2517
|
+
return void 0;
|
|
2518
|
+
}
|
|
2428
2519
|
getOrCreateGlobalBinding(name) {
|
|
2429
2520
|
const existing = this.globalScope.declarations.get(name);
|
|
2430
2521
|
if (existing !== void 0) return existing;
|
|
@@ -2540,12 +2631,14 @@ var Analyzer = class {
|
|
|
2540
2631
|
switch (stmt.type) {
|
|
2541
2632
|
case "VariableDeclaration": {
|
|
2542
2633
|
for (const init of stmt.init) this.visitExpression(init, scope);
|
|
2634
|
+
for (const name of stmt.names) this.visitType(name.typeAnnotation, scope);
|
|
2543
2635
|
const isConst = stmt.kind === "const";
|
|
2544
2636
|
for (const name of stmt.names) this.declarePattern(scope, name, "local", scope, isConst);
|
|
2545
2637
|
return;
|
|
2546
2638
|
}
|
|
2547
2639
|
case "FunctionDeclaration": {
|
|
2548
2640
|
this.declare(scope, stmt.name.name, "local", stmt.name, stmt.kind === "const");
|
|
2641
|
+
for (const signature of stmt.signatures ?? []) this.visitSignature(signature, scope);
|
|
2549
2642
|
this.visitFunctionBody(stmt.func, scope);
|
|
2550
2643
|
return;
|
|
2551
2644
|
}
|
|
@@ -2555,6 +2648,7 @@ var Analyzer = class {
|
|
|
2555
2648
|
} else {
|
|
2556
2649
|
this.reference(scope, stmt.target.base);
|
|
2557
2650
|
}
|
|
2651
|
+
for (const signature of stmt.signatures ?? []) this.visitSignature(signature, scope);
|
|
2558
2652
|
this.visitFunctionBody(stmt.func, scope, stmt.isMethod);
|
|
2559
2653
|
return;
|
|
2560
2654
|
}
|
|
@@ -2628,10 +2722,13 @@ var Analyzer = class {
|
|
|
2628
2722
|
case "BreakStatement":
|
|
2629
2723
|
case "ContinueStatement":
|
|
2630
2724
|
case "ErrorStatement":
|
|
2725
|
+
return;
|
|
2631
2726
|
case "DeclareStatement":
|
|
2727
|
+
this.visitType(stmt.valueType, scope);
|
|
2632
2728
|
return;
|
|
2633
2729
|
case "TypeAliasStatement":
|
|
2634
2730
|
case "ExportTypeAliasStatement":
|
|
2731
|
+
this.visitType(stmt.definition, scope);
|
|
2635
2732
|
return;
|
|
2636
2733
|
case "ImportStatement": {
|
|
2637
2734
|
if (stmt.defaultImport) {
|
|
@@ -2648,6 +2745,18 @@ var Analyzer = class {
|
|
|
2648
2745
|
case "ExportDefaultStatement":
|
|
2649
2746
|
this.visitExpression(stmt.declaration, scope);
|
|
2650
2747
|
return;
|
|
2748
|
+
case "ExportNamedStatement":
|
|
2749
|
+
if (!stmt.source) {
|
|
2750
|
+
for (const specifier of stmt.specifiers) {
|
|
2751
|
+
const id = this.lookup(scope, specifier.local.name);
|
|
2752
|
+
if (id === void 0) continue;
|
|
2753
|
+
this.bindingOf.set(specifier.local, id);
|
|
2754
|
+
this.bindings.get(id).references.push(specifier.local);
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
return;
|
|
2758
|
+
case "ExportAllStatement":
|
|
2759
|
+
return;
|
|
2651
2760
|
}
|
|
2652
2761
|
}
|
|
2653
2762
|
// ---------------- functions ----------------
|
|
@@ -2655,6 +2764,7 @@ var Analyzer = class {
|
|
|
2655
2764
|
const fnScope = childScope(outerScope);
|
|
2656
2765
|
func.params.forEach((param, i) => {
|
|
2657
2766
|
const kind = isMethod && i === 0 ? "self" : "param";
|
|
2767
|
+
this.visitType(param.typeAnnotation, fnScope);
|
|
2658
2768
|
if (param.default) this.visitExpression(param.default, fnScope);
|
|
2659
2769
|
if (param.pattern) {
|
|
2660
2770
|
this.declarePattern(fnScope, param.pattern, kind, fnScope);
|
|
@@ -2662,8 +2772,37 @@ var Analyzer = class {
|
|
|
2662
2772
|
this.declare(fnScope, param.name, kind, param);
|
|
2663
2773
|
}
|
|
2664
2774
|
});
|
|
2775
|
+
this.visitType(func.varargTypeAnnotation, fnScope);
|
|
2776
|
+
this.visitType(func.returnType, fnScope);
|
|
2665
2777
|
this.visitBlock(func.body, fnScope);
|
|
2666
2778
|
}
|
|
2779
|
+
/** An overload signature: no body and no bindings, but its types can hold
|
|
2780
|
+
* a `typeof x`. */
|
|
2781
|
+
visitSignature(signature, scope) {
|
|
2782
|
+
for (const param of signature.params) this.visitType(param.typeAnnotation, scope);
|
|
2783
|
+
this.visitType(signature.returnType, scope);
|
|
2784
|
+
}
|
|
2785
|
+
/** Resolve the value references inside a type. Only `typeof x` has any —
|
|
2786
|
+
* everything else in a type names types, which live in their own
|
|
2787
|
+
* namespace and are not this pass's business. */
|
|
2788
|
+
visitType(node, scope) {
|
|
2789
|
+
if (!node) return;
|
|
2790
|
+
const walk = (value) => {
|
|
2791
|
+
if (!value || typeof value !== "object") return;
|
|
2792
|
+
if (Array.isArray(value)) {
|
|
2793
|
+
for (const item of value) walk(item);
|
|
2794
|
+
return;
|
|
2795
|
+
}
|
|
2796
|
+
if (value.type === "TypeofTypeNode") {
|
|
2797
|
+
this.visitExpression(value.expression, scope);
|
|
2798
|
+
return;
|
|
2799
|
+
}
|
|
2800
|
+
for (const key of Object.keys(value)) {
|
|
2801
|
+
if (key !== "line" && key !== "column") walk(value[key]);
|
|
2802
|
+
}
|
|
2803
|
+
};
|
|
2804
|
+
walk(node);
|
|
2805
|
+
}
|
|
2667
2806
|
// ---------------- expressions ----------------
|
|
2668
2807
|
visitExpression(expr, scope) {
|
|
2669
2808
|
switch (expr.type) {
|
|
@@ -2722,6 +2861,11 @@ var Analyzer = class {
|
|
|
2722
2861
|
return;
|
|
2723
2862
|
case "TypeAssertionExpression":
|
|
2724
2863
|
this.visitExpression(expr.expression, scope);
|
|
2864
|
+
this.visitType(expr.typeAnnotation, scope);
|
|
2865
|
+
return;
|
|
2866
|
+
case "SatisfiesExpression":
|
|
2867
|
+
this.visitExpression(expr.expression, scope);
|
|
2868
|
+
this.visitType(expr.typeAnnotation, scope);
|
|
2725
2869
|
return;
|
|
2726
2870
|
case "IfElseExpression":
|
|
2727
2871
|
for (const clause of expr.clauses) {
|
|
@@ -3392,6 +3536,94 @@ function formatKey(k) {
|
|
|
3392
3536
|
function analyzeTypes(program, scopes, options = {}) {
|
|
3393
3537
|
return new TypeAnalyzer(program, scopes, options).run();
|
|
3394
3538
|
}
|
|
3539
|
+
function moduleExports(program, scopes, types, resolveModule) {
|
|
3540
|
+
const byDeclaration = /* @__PURE__ */ new Map();
|
|
3541
|
+
for (const binding of scopes.bindings.values()) {
|
|
3542
|
+
if (binding.declarationNode) byDeclaration.set(binding.declarationNode, binding.id);
|
|
3543
|
+
}
|
|
3544
|
+
const values = /* @__PURE__ */ new Map();
|
|
3545
|
+
const exportedTypes = /* @__PURE__ */ new Map();
|
|
3546
|
+
let defaultType;
|
|
3547
|
+
const stars = [];
|
|
3548
|
+
const setValue = (name, type) => {
|
|
3549
|
+
if (name === "default") defaultType = type;
|
|
3550
|
+
else values.set(name, type);
|
|
3551
|
+
};
|
|
3552
|
+
const reexport = (from, name, as) => {
|
|
3553
|
+
if (!from || from.partial) {
|
|
3554
|
+
setValue(as, anyType);
|
|
3555
|
+
return;
|
|
3556
|
+
}
|
|
3557
|
+
if (name === "default") {
|
|
3558
|
+
if (from.default) setValue(as, from.default);
|
|
3559
|
+
return;
|
|
3560
|
+
}
|
|
3561
|
+
const value = from.values.get(name);
|
|
3562
|
+
if (value) setValue(as, value);
|
|
3563
|
+
const type = from.types.get(name);
|
|
3564
|
+
if (type) exportedTypes.set(as, type);
|
|
3565
|
+
};
|
|
3566
|
+
const aliasParams = (name) => {
|
|
3567
|
+
for (const s of program.body.statements) {
|
|
3568
|
+
const alias = s.type === "TypeAliasStatement" ? s : s.type === "ExportTypeAliasStatement" ? s.alias : void 0;
|
|
3569
|
+
if (alias?.name.name === name) return alias.generics.map((g) => g.name);
|
|
3570
|
+
}
|
|
3571
|
+
return [];
|
|
3572
|
+
};
|
|
3573
|
+
const exportName = (declaration, name) => {
|
|
3574
|
+
const id = byDeclaration.get(declaration);
|
|
3575
|
+
values.set(name, (id !== void 0 ? types.bindingType.get(id) : void 0) ?? anyType);
|
|
3576
|
+
};
|
|
3577
|
+
const exportPattern = (target) => {
|
|
3578
|
+
switch (target.type) {
|
|
3579
|
+
case "IdentifierPattern":
|
|
3580
|
+
exportName(target, target.name);
|
|
3581
|
+
return;
|
|
3582
|
+
case "ObjectPattern":
|
|
3583
|
+
for (const p of target.properties) exportPattern(p.value);
|
|
3584
|
+
if (target.rest) exportPattern(target.rest);
|
|
3585
|
+
return;
|
|
3586
|
+
case "ArrayPattern":
|
|
3587
|
+
for (const el of target.elements) if (el) exportPattern(el.value);
|
|
3588
|
+
if (target.rest) exportPattern(target.rest);
|
|
3589
|
+
return;
|
|
3590
|
+
}
|
|
3591
|
+
};
|
|
3592
|
+
for (const stmt of program.body.statements) {
|
|
3593
|
+
if (stmt.type === "ExportStatement") {
|
|
3594
|
+
const declaration = stmt.declaration;
|
|
3595
|
+
if (declaration.type === "FunctionDeclaration") exportName(declaration.name, declaration.name.name);
|
|
3596
|
+
else for (const target of declaration.names) exportPattern(target);
|
|
3597
|
+
} else if (stmt.type === "ExportTypeAliasStatement") {
|
|
3598
|
+
const name = stmt.alias.name.name;
|
|
3599
|
+
const type = types.aliases.get(name);
|
|
3600
|
+
if (type) exportedTypes.set(name, { type, params: stmt.alias.generics.map((g) => g.name) });
|
|
3601
|
+
} else if (stmt.type === "ExportDefaultStatement") {
|
|
3602
|
+
defaultType = types.typeOf.get(stmt.declaration) ?? anyType;
|
|
3603
|
+
} else if (stmt.type === "ExportNamedStatement") {
|
|
3604
|
+
if (stmt.source) {
|
|
3605
|
+
const from = resolveModule?.(stmt.source.value);
|
|
3606
|
+
for (const s of stmt.specifiers) reexport(from, s.local.name, s.exported.name);
|
|
3607
|
+
} else {
|
|
3608
|
+
for (const s of stmt.specifiers) {
|
|
3609
|
+
const id = scopes.bindingOf.get(s.local);
|
|
3610
|
+
if (id !== void 0) setValue(s.exported.name, types.bindingType.get(id) ?? anyType);
|
|
3611
|
+
const alias = types.aliases.get(s.local.name);
|
|
3612
|
+
if (alias) exportedTypes.set(s.exported.name, { type: alias, params: aliasParams(s.local.name) });
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
} else if (stmt.type === "ExportAllStatement") {
|
|
3616
|
+
stars.push(stmt.source.value);
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
for (const specifier of stars) {
|
|
3620
|
+
const from = resolveModule?.(specifier);
|
|
3621
|
+
if (!from || from.partial) continue;
|
|
3622
|
+
for (const [name, type] of from.values) if (!values.has(name)) values.set(name, type);
|
|
3623
|
+
for (const [name, type] of from.types) if (!exportedTypes.has(name)) exportedTypes.set(name, type);
|
|
3624
|
+
}
|
|
3625
|
+
return { values, types: exportedTypes, default: defaultType };
|
|
3626
|
+
}
|
|
3395
3627
|
function bindKey(id) {
|
|
3396
3628
|
return `$${id}`;
|
|
3397
3629
|
}
|
|
@@ -3505,6 +3737,7 @@ var TypeAnalyzer = class {
|
|
|
3505
3737
|
typeOf = /* @__PURE__ */ new Map();
|
|
3506
3738
|
bindingType = /* @__PURE__ */ new Map();
|
|
3507
3739
|
narrowedTypeOf = /* @__PURE__ */ new Map();
|
|
3740
|
+
typeOfTypeNode = /* @__PURE__ */ new Map();
|
|
3508
3741
|
/** Public: each alias resolved once (generic aliases keep their params as
|
|
3509
3742
|
* `typeParam` nodes in the body). */
|
|
3510
3743
|
aliases = /* @__PURE__ */ new Map();
|
|
@@ -3539,6 +3772,10 @@ var TypeAnalyzer = class {
|
|
|
3539
3772
|
* (`type Tree = { children: Tree[] }`); it resolves to a nominal ref. */
|
|
3540
3773
|
resolvingAliases = /* @__PURE__ */ new Set();
|
|
3541
3774
|
diagnostics = [];
|
|
3775
|
+
/** `import`ed type names, from `resolveModule`. */
|
|
3776
|
+
importedTypes = /* @__PURE__ */ new Map();
|
|
3777
|
+
/** `resolveModule` results, one lookup per module path. */
|
|
3778
|
+
resolvedModules = /* @__PURE__ */ new Map();
|
|
3542
3779
|
emitDiagnostics;
|
|
3543
3780
|
/** Recursion guard for `preVisitBody`. */
|
|
3544
3781
|
preVisitDepth = 0;
|
|
@@ -3547,6 +3784,7 @@ var TypeAnalyzer = class {
|
|
|
3547
3784
|
this.registerAliasDefs(this.program.body);
|
|
3548
3785
|
for (const lib of this.options.libs ?? []) this.harvestDeclares(lib.body);
|
|
3549
3786
|
this.harvestDeclares(this.program.body);
|
|
3787
|
+
this.registerImportedTypes();
|
|
3550
3788
|
this.resolveAllAliases();
|
|
3551
3789
|
this.indexDeclarations();
|
|
3552
3790
|
for (const [name, id] of this.scopes.globalsByName) {
|
|
@@ -3564,13 +3802,51 @@ var TypeAnalyzer = class {
|
|
|
3564
3802
|
typeOf: this.typeOf,
|
|
3565
3803
|
bindingType: this.bindingType,
|
|
3566
3804
|
narrowedTypeOf: this.narrowedTypeOf,
|
|
3567
|
-
|
|
3805
|
+
typeOfTypeNode: this.typeOfTypeNode,
|
|
3806
|
+
aliases: this.resolveDeferredAliases(),
|
|
3568
3807
|
diagnostics: this.diagnostics
|
|
3569
3808
|
};
|
|
3570
3809
|
}
|
|
3571
3810
|
// --------------------------------------------------------
|
|
3572
3811
|
// Aliases
|
|
3573
3812
|
// --------------------------------------------------------
|
|
3813
|
+
moduleFor(specifier) {
|
|
3814
|
+
if (!this.resolvedModules.has(specifier)) {
|
|
3815
|
+
this.resolvedModules.set(specifier, this.options.resolveModule?.(specifier));
|
|
3816
|
+
}
|
|
3817
|
+
return this.resolvedModules.get(specifier);
|
|
3818
|
+
}
|
|
3819
|
+
/** `export ... from "./x"`: the module must exist, and so must each name. */
|
|
3820
|
+
checkReexport(source, names) {
|
|
3821
|
+
if (!this.options.resolveModule || !this.emitDiagnostics) return;
|
|
3822
|
+
const exports = this.moduleFor(source.value);
|
|
3823
|
+
if (!exports) {
|
|
3824
|
+
this.diagnostics.push({ node: source, message: `Cannot find module '${source.value}'` });
|
|
3825
|
+
return;
|
|
3826
|
+
}
|
|
3827
|
+
if (exports.partial) return;
|
|
3828
|
+
for (const name of names) {
|
|
3829
|
+
const found = name.name === "default" ? exports.default !== void 0 : exports.values.has(name.name) || exports.types.has(name.name);
|
|
3830
|
+
if (!found) {
|
|
3831
|
+
this.diagnostics.push({ node: name, message: `Module '${source.value}' has no exported member '${name.name}'` });
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
registerImportedTypes() {
|
|
3836
|
+
if (!this.options.resolveModule) return;
|
|
3837
|
+
for (const stmt of this.program.body.statements) {
|
|
3838
|
+
if (stmt.type !== "ImportStatement") continue;
|
|
3839
|
+
const exports = this.moduleFor(stmt.source.value);
|
|
3840
|
+
if (!exports) continue;
|
|
3841
|
+
for (const s of stmt.specifiers) {
|
|
3842
|
+
const exported = exports.types.get(s.imported.name);
|
|
3843
|
+
if (exported) {
|
|
3844
|
+
this.importedTypes.set(s.local.name, exported);
|
|
3845
|
+
this.aliases.set(s.local.name, exported.type);
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3574
3850
|
registerAliasDefs(block) {
|
|
3575
3851
|
for (const stmt of block.statements) {
|
|
3576
3852
|
const alias = stmt.type === "TypeAliasStatement" ? stmt : stmt.type === "ExportTypeAliasStatement" ? stmt.alias : void 0;
|
|
@@ -3590,10 +3866,22 @@ var TypeAnalyzer = class {
|
|
|
3590
3866
|
}
|
|
3591
3867
|
resolveAllAliases() {
|
|
3592
3868
|
for (const [name, def] of this.aliasDefs) {
|
|
3869
|
+
if (containsTypeQuery(def.node)) continue;
|
|
3870
|
+
this.withTypeParams(def.params, () => {
|
|
3871
|
+
this.aliases.set(name, this.resolveType(def.node));
|
|
3872
|
+
});
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3875
|
+
/** The aliases `resolveAllAliases` left for later, now that every binding
|
|
3876
|
+
* has its type. */
|
|
3877
|
+
resolveDeferredAliases() {
|
|
3878
|
+
for (const [name, def] of this.aliasDefs) {
|
|
3879
|
+
if (this.aliases.has(name)) continue;
|
|
3593
3880
|
this.withTypeParams(def.params, () => {
|
|
3594
3881
|
this.aliases.set(name, this.resolveType(def.node));
|
|
3595
3882
|
});
|
|
3596
3883
|
}
|
|
3884
|
+
return this.aliases;
|
|
3597
3885
|
}
|
|
3598
3886
|
withTypeParams(params, fn2) {
|
|
3599
3887
|
const start = this.typeParamScope.length;
|
|
@@ -3634,6 +3922,11 @@ var TypeAnalyzer = class {
|
|
|
3634
3922
|
// TypeNode -> Type
|
|
3635
3923
|
// --------------------------------------------------------
|
|
3636
3924
|
resolveType(node) {
|
|
3925
|
+
const type = this.resolveTypeNode(node);
|
|
3926
|
+
if (this.instantiationDepth === 0) this.typeOfTypeNode.set(node, type);
|
|
3927
|
+
return type;
|
|
3928
|
+
}
|
|
3929
|
+
resolveTypeNode(node) {
|
|
3637
3930
|
switch (node.type) {
|
|
3638
3931
|
case "TypeReference": {
|
|
3639
3932
|
const name = node.namespace ? `${node.namespace}.${node.base}` : node.base;
|
|
@@ -3674,6 +3967,16 @@ var TypeAnalyzer = class {
|
|
|
3674
3967
|
typeArguments: node.typeArguments.map((a) => this.resolveType(a))
|
|
3675
3968
|
});
|
|
3676
3969
|
}
|
|
3970
|
+
const imported = this.importedTypes.get(node.base);
|
|
3971
|
+
if (imported) {
|
|
3972
|
+
if (!imported.params.length) return imported.type;
|
|
3973
|
+
const subst = /* @__PURE__ */ new Map();
|
|
3974
|
+
imported.params.forEach((name2, i) => {
|
|
3975
|
+
const arg = node.typeArguments[i];
|
|
3976
|
+
subst.set(name2, arg ? this.resolveType(arg) : unknownType);
|
|
3977
|
+
});
|
|
3978
|
+
return this.reduceType(substitute(imported.type, subst));
|
|
3979
|
+
}
|
|
3677
3980
|
const lib = this.options.libTypes?.[node.base];
|
|
3678
3981
|
if (lib) return lib;
|
|
3679
3982
|
}
|
|
@@ -4226,11 +4529,49 @@ var TypeAnalyzer = class {
|
|
|
4226
4529
|
case "ExportDefaultStatement":
|
|
4227
4530
|
this.infer(stmt.declaration, env);
|
|
4228
4531
|
return;
|
|
4532
|
+
case "ExportNamedStatement": {
|
|
4533
|
+
if (stmt.source) {
|
|
4534
|
+
this.checkReexport(stmt.source, stmt.specifiers.map((s) => s.local));
|
|
4535
|
+
return;
|
|
4536
|
+
}
|
|
4537
|
+
for (const s of stmt.specifiers) {
|
|
4538
|
+
if (this.bindingIdOf(s.local) !== void 0) {
|
|
4539
|
+
this.infer(s.local, env);
|
|
4540
|
+
} else if (!this.aliasDefs.has(s.local.name) && !this.importedTypes.has(s.local.name)) {
|
|
4541
|
+
if (this.emitDiagnostics) {
|
|
4542
|
+
this.diagnostics.push({ node: s.local, message: `Cannot find name '${s.local.name}' to export` });
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
4545
|
+
}
|
|
4546
|
+
return;
|
|
4547
|
+
}
|
|
4548
|
+
case "ExportAllStatement":
|
|
4549
|
+
this.checkReexport(stmt.source, []);
|
|
4550
|
+
return;
|
|
4229
4551
|
case "ImportStatement": {
|
|
4230
|
-
const
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4552
|
+
const resolving = this.options.resolveModule !== void 0;
|
|
4553
|
+
const exports = resolving ? this.moduleFor(stmt.source.value) : void 0;
|
|
4554
|
+
const specifier = stmt.source.value;
|
|
4555
|
+
const report = (node, message) => {
|
|
4556
|
+
if (this.emitDiagnostics) this.diagnostics.push({ node, message });
|
|
4557
|
+
};
|
|
4558
|
+
if (resolving && !exports) report(stmt.source, `Cannot find module '${specifier}'`);
|
|
4559
|
+
const usable = exports && !exports.partial ? exports : void 0;
|
|
4560
|
+
if (stmt.defaultImport) {
|
|
4561
|
+
if (usable && usable.default === void 0) {
|
|
4562
|
+
report(stmt.defaultImport, `Module '${specifier}' has no default export`);
|
|
4563
|
+
}
|
|
4564
|
+
const id = this.bindingIdByName(stmt.defaultImport.name, stmt.defaultImport);
|
|
4565
|
+
if (id !== void 0) this.bindingType.set(id, usable?.default ?? anyType);
|
|
4566
|
+
}
|
|
4567
|
+
for (const s of stmt.specifiers) {
|
|
4568
|
+
const value = usable?.values.get(s.imported.name);
|
|
4569
|
+
if (usable && !value && !usable.types.has(s.imported.name)) {
|
|
4570
|
+
report(s.imported, `Module '${specifier}' has no exported member '${s.imported.name}'`);
|
|
4571
|
+
}
|
|
4572
|
+
const id = this.bindingIdByName(s.local.name, s.local);
|
|
4573
|
+
if (id !== void 0) this.bindingType.set(id, value ?? anyType);
|
|
4574
|
+
}
|
|
4234
4575
|
return;
|
|
4235
4576
|
}
|
|
4236
4577
|
case "BreakStatement":
|
|
@@ -4533,11 +4874,18 @@ var TypeAnalyzer = class {
|
|
|
4533
4874
|
inferFunctionBody(func, env) {
|
|
4534
4875
|
const names = func.generics.map((g) => g.name);
|
|
4535
4876
|
return this.withTypeParams(func.generics, () => {
|
|
4536
|
-
const params = func.params.map((p) =>
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4877
|
+
const params = func.params.map((p) => {
|
|
4878
|
+
const type = this.paramType(p, env);
|
|
4879
|
+
if (!p.pattern) {
|
|
4880
|
+
const id = this.bindingIdByName(p.name, p);
|
|
4881
|
+
if (id !== void 0 && !this.bindingType.has(id)) this.bindingType.set(id, type);
|
|
4882
|
+
}
|
|
4883
|
+
return {
|
|
4884
|
+
name: p.pattern ? void 0 : p.name,
|
|
4885
|
+
type,
|
|
4886
|
+
optional: p.optional || p.default !== void 0
|
|
4887
|
+
};
|
|
4888
|
+
});
|
|
4541
4889
|
const bodyEnv = forkEnv(env);
|
|
4542
4890
|
for (const p of func.params) {
|
|
4543
4891
|
if (p.pattern) this.bindPattern(p.pattern, this.paramType(p, bodyEnv), bodyEnv, "widen");
|
|
@@ -5391,6 +5739,12 @@ var TypeAnalyzer = class {
|
|
|
5391
5739
|
return this.bindingByDecl.get(node) ?? this.bindingByPos.get(posKey(name, node.line.start, node.column.start));
|
|
5392
5740
|
}
|
|
5393
5741
|
};
|
|
5742
|
+
function containsTypeQuery(node) {
|
|
5743
|
+
if (!node || typeof node !== "object") return false;
|
|
5744
|
+
if (Array.isArray(node)) return node.some(containsTypeQuery);
|
|
5745
|
+
if (node.type === "TypeofTypeNode") return true;
|
|
5746
|
+
return Object.values(node).some(containsTypeQuery);
|
|
5747
|
+
}
|
|
5394
5748
|
|
|
5395
5749
|
// src/lib/luau.ts
|
|
5396
5750
|
import { readFileSync } from "fs";
|
|
@@ -5456,6 +5810,7 @@ export {
|
|
|
5456
5810
|
luauLib,
|
|
5457
5811
|
luautparser,
|
|
5458
5812
|
matchInfer,
|
|
5813
|
+
moduleExports,
|
|
5459
5814
|
narrowExclude,
|
|
5460
5815
|
narrowFalsy,
|
|
5461
5816
|
narrowTo,
|