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.cjs
CHANGED
|
@@ -54,6 +54,7 @@ __export(index_exports, {
|
|
|
54
54
|
luauLib: () => luauLib,
|
|
55
55
|
luautparser: () => luautparser,
|
|
56
56
|
matchInfer: () => matchInfer,
|
|
57
|
+
moduleExports: () => moduleExports,
|
|
57
58
|
narrowExclude: () => narrowExclude,
|
|
58
59
|
narrowFalsy: () => narrowFalsy,
|
|
59
60
|
narrowTo: () => narrowTo,
|
|
@@ -634,6 +635,17 @@ function spanFrom(start, end) {
|
|
|
634
635
|
column: { start: start.column.start, end: end.column.end }
|
|
635
636
|
};
|
|
636
637
|
}
|
|
638
|
+
function tokenIdentifier(t) {
|
|
639
|
+
return { type: "Identifier", name: t.value, ...spanFrom(t, t) };
|
|
640
|
+
}
|
|
641
|
+
function nameIdentifier(name, at) {
|
|
642
|
+
return {
|
|
643
|
+
type: "Identifier",
|
|
644
|
+
name,
|
|
645
|
+
line: { start: at.line.start, end: at.line.start },
|
|
646
|
+
column: { start: at.column.start, end: at.column.start + name.length }
|
|
647
|
+
};
|
|
648
|
+
}
|
|
637
649
|
var BINARY_PRECEDENCE = {
|
|
638
650
|
"or": 1,
|
|
639
651
|
"and": 2,
|
|
@@ -952,9 +964,11 @@ var Parser = class {
|
|
|
952
964
|
const params = head.params.map((p) => ({
|
|
953
965
|
type: "FunctionTypeParameter",
|
|
954
966
|
name: p.name || void 0,
|
|
967
|
+
id: p.name ? nameIdentifier(p.name, p) : void 0,
|
|
955
968
|
optional: p.optional,
|
|
956
|
-
typeAnnotation: p.typeAnnotation ?? { type: "TypeReference", base: "any", typeArguments: [],
|
|
957
|
-
|
|
969
|
+
typeAnnotation: p.typeAnnotation ?? { type: "TypeReference", base: "any", typeArguments: [], line: p.line, column: p.column },
|
|
970
|
+
line: p.line,
|
|
971
|
+
column: p.column
|
|
958
972
|
}));
|
|
959
973
|
const valueType2 = {
|
|
960
974
|
type: "FunctionTypeNode",
|
|
@@ -966,12 +980,12 @@ var Parser = class {
|
|
|
966
980
|
predicate: head.predicate,
|
|
967
981
|
...spanFrom(start, this.previous())
|
|
968
982
|
};
|
|
969
|
-
return { type: "DeclareStatement", name: nameTok2.value, valueType: valueType2, ...spanFrom(start, this.previous()) };
|
|
983
|
+
return { type: "DeclareStatement", name: nameTok2.value, id: tokenIdentifier(nameTok2), valueType: valueType2, ...spanFrom(start, this.previous()) };
|
|
970
984
|
}
|
|
971
985
|
const nameTok = this.expectIdentifier();
|
|
972
986
|
this.expectPunctuator(":");
|
|
973
987
|
const valueType = this.parseType();
|
|
974
|
-
return { type: "DeclareStatement", name: nameTok.value, valueType, ...spanFrom(start, this.previous()) };
|
|
988
|
+
return { type: "DeclareStatement", name: nameTok.value, id: tokenIdentifier(nameTok), valueType, ...spanFrom(start, this.previous()) };
|
|
975
989
|
}
|
|
976
990
|
// `import { a, b as c } from '...'` / `import Default from '...'` /
|
|
977
991
|
// `import Default, { a } from '...'`. Compiled away entirely by the
|
|
@@ -1030,6 +1044,21 @@ var Parser = class {
|
|
|
1030
1044
|
}
|
|
1031
1045
|
return { type: "ImportSpecifier", imported, local, ...spanFrom(imported, local) };
|
|
1032
1046
|
}
|
|
1047
|
+
/** `from "<path>"`: consumes `from` and the module string. */
|
|
1048
|
+
parseModuleSource() {
|
|
1049
|
+
this.advance();
|
|
1050
|
+
const sourceTok = this.current();
|
|
1051
|
+
if (sourceTok.type !== "Literal" || sourceTok.kind !== "string") {
|
|
1052
|
+
this.error("Expected string literal module path after 'from'");
|
|
1053
|
+
}
|
|
1054
|
+
this.advance();
|
|
1055
|
+
return {
|
|
1056
|
+
type: "StringLiteral",
|
|
1057
|
+
value: sourceTok.value,
|
|
1058
|
+
raw: sourceTok.raw,
|
|
1059
|
+
...spanFrom(sourceTok, sourceTok)
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1033
1062
|
// `export const ...` / `export let ...` / `export const function ...` /
|
|
1034
1063
|
// `export type ...` / `export default <expr>`
|
|
1035
1064
|
parseExportStatement() {
|
|
@@ -1048,7 +1077,30 @@ var Parser = class {
|
|
|
1048
1077
|
const declaration = this.parseVariableDeclaration();
|
|
1049
1078
|
return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
|
|
1050
1079
|
}
|
|
1051
|
-
this.
|
|
1080
|
+
if (this.checkPunctuator("{")) {
|
|
1081
|
+
this.advance();
|
|
1082
|
+
const specifiers = [];
|
|
1083
|
+
while (!this.checkPunctuator("}")) {
|
|
1084
|
+
const local = this.parseIdentifier();
|
|
1085
|
+
let exported = local;
|
|
1086
|
+
if (this.checkKeyword("as")) {
|
|
1087
|
+
this.advance();
|
|
1088
|
+
exported = this.parseIdentifier();
|
|
1089
|
+
}
|
|
1090
|
+
specifiers.push({ type: "ExportSpecifier", local, exported, ...spanFrom(local, exported) });
|
|
1091
|
+
if (!this.matchPunctuator(",")) break;
|
|
1092
|
+
}
|
|
1093
|
+
this.expectPunctuator("}");
|
|
1094
|
+
const source = this.checkKeyword("from") ? this.parseModuleSource() : void 0;
|
|
1095
|
+
return { type: "ExportNamedStatement", specifiers, source, ...spanFrom(start, this.previous()) };
|
|
1096
|
+
}
|
|
1097
|
+
if (this.checkOperator("*")) {
|
|
1098
|
+
this.advance();
|
|
1099
|
+
if (!this.checkKeyword("from")) this.error("Expected 'from' after 'export *'");
|
|
1100
|
+
const source = this.parseModuleSource();
|
|
1101
|
+
return { type: "ExportAllStatement", source, ...spanFrom(start, this.previous()) };
|
|
1102
|
+
}
|
|
1103
|
+
this.error("Expected 'const', 'let', 'type', 'default', '{' or '*' after 'export'");
|
|
1052
1104
|
}
|
|
1053
1105
|
// `const x = ...` / `let x, y = ...` / `const function f() ... end`.
|
|
1054
1106
|
// luaut has no `local` — `const` bindings are immutable, `let` mutable.
|
|
@@ -2082,8 +2134,9 @@ var Parser = class {
|
|
|
2082
2134
|
}
|
|
2083
2135
|
if (this.checkIdentifierValue("infer") && this.peek(1).type === "Identifier") {
|
|
2084
2136
|
this.advance();
|
|
2085
|
-
const
|
|
2086
|
-
|
|
2137
|
+
const nameTok = this.expectIdentifier();
|
|
2138
|
+
const name = nameTok.value;
|
|
2139
|
+
return { type: "InferTypeNode", name, id: tokenIdentifier(nameTok), ...spanFrom(t, this.previous()) };
|
|
2087
2140
|
}
|
|
2088
2141
|
if (t.type === "Operator" && t.value === "<") {
|
|
2089
2142
|
const generics = this.parseGenericTypeParameterList();
|
|
@@ -2120,6 +2173,16 @@ var Parser = class {
|
|
|
2120
2173
|
this.expectPunctuator(")");
|
|
2121
2174
|
return { type: "TypeofTypeNode", expression, ...spanFrom(t, this.previous()) };
|
|
2122
2175
|
}
|
|
2176
|
+
if (t.type === "Identifier" && t.value === "typeof" && this.peek(1).type === "Identifier") {
|
|
2177
|
+
this.advance();
|
|
2178
|
+
let expression = this.parseIdentifier();
|
|
2179
|
+
while (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
|
|
2180
|
+
this.advance();
|
|
2181
|
+
const property = this.parseIdentifier();
|
|
2182
|
+
expression = { type: "MemberExpression", object: expression, property, ...spanFrom(expression, property) };
|
|
2183
|
+
}
|
|
2184
|
+
return { type: "TypeofTypeNode", expression, ...spanFrom(t, this.previous()) };
|
|
2185
|
+
}
|
|
2123
2186
|
if (t.type === "Literal" && t.kind === "string") {
|
|
2124
2187
|
this.advance();
|
|
2125
2188
|
return { type: "TypeLiteralString", value: t.value, ...spanFrom(t, t) };
|
|
@@ -2182,17 +2245,21 @@ var Parser = class {
|
|
|
2182
2245
|
let name;
|
|
2183
2246
|
let optional2 = false;
|
|
2184
2247
|
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 === ":");
|
|
2248
|
+
let nameTok;
|
|
2185
2249
|
if (named) {
|
|
2186
|
-
|
|
2250
|
+
const tok = this.expectIdentifier();
|
|
2251
|
+
nameTok = tok;
|
|
2252
|
+
name = tok.value;
|
|
2187
2253
|
optional2 = this.matchPunctuator("?");
|
|
2188
2254
|
this.advance();
|
|
2189
2255
|
}
|
|
2190
|
-
const paramStart = this.current();
|
|
2256
|
+
const paramStart = nameTok ?? this.current();
|
|
2191
2257
|
const typeAnnotation = this.parseType();
|
|
2192
2258
|
params.push({
|
|
2193
2259
|
type: "FunctionTypeParameter",
|
|
2194
2260
|
name,
|
|
2195
2261
|
typeAnnotation,
|
|
2262
|
+
id: nameTok && tokenIdentifier(nameTok),
|
|
2196
2263
|
optional: optional2 || void 0,
|
|
2197
2264
|
...spanFrom(paramStart, this.previous())
|
|
2198
2265
|
});
|
|
@@ -2254,7 +2321,8 @@ var Parser = class {
|
|
|
2254
2321
|
readonly = false;
|
|
2255
2322
|
}
|
|
2256
2323
|
this.expectPunctuator("[");
|
|
2257
|
-
const
|
|
2324
|
+
const parameterTok = this.expectIdentifier();
|
|
2325
|
+
const parameter = parameterTok.value;
|
|
2258
2326
|
this.advance();
|
|
2259
2327
|
const constraint = this.parseType();
|
|
2260
2328
|
let nameType;
|
|
@@ -2278,6 +2346,7 @@ var Parser = class {
|
|
|
2278
2346
|
return {
|
|
2279
2347
|
type: "MappedTypeNode",
|
|
2280
2348
|
parameter,
|
|
2349
|
+
parameterId: tokenIdentifier(parameterTok),
|
|
2281
2350
|
constraint,
|
|
2282
2351
|
nameType,
|
|
2283
2352
|
template,
|
|
@@ -2304,30 +2373,43 @@ var Parser = class {
|
|
|
2304
2373
|
this.expectPunctuator("{");
|
|
2305
2374
|
const properties = [];
|
|
2306
2375
|
while (!this.checkPunctuator("}")) {
|
|
2376
|
+
const propStart = this.current();
|
|
2307
2377
|
if (this.checkPunctuator("[")) {
|
|
2308
2378
|
this.advance();
|
|
2309
2379
|
const keyType = this.parseType();
|
|
2310
2380
|
this.expectPunctuator("]");
|
|
2311
2381
|
this.expectPunctuator(":");
|
|
2312
2382
|
const valueType = this.parseType();
|
|
2313
|
-
properties.push({ type: "TableTypeIndexer", keyType, valueType });
|
|
2383
|
+
properties.push({ type: "TableTypeIndexer", keyType, valueType, ...spanFrom(propStart, this.previous()) });
|
|
2314
2384
|
} else if (this.checkIdentifierValue("readonly") && this.peek(1).type === "Identifier") {
|
|
2315
2385
|
this.advance();
|
|
2316
|
-
const
|
|
2386
|
+
const keyTok = this.expectIdentifier();
|
|
2387
|
+
const name = keyTok.value;
|
|
2317
2388
|
const optional2 = this.matchPunctuator("?");
|
|
2318
2389
|
this.expectPunctuator(":");
|
|
2319
2390
|
const valueType = this.parseType();
|
|
2320
|
-
properties.push({
|
|
2391
|
+
properties.push({
|
|
2392
|
+
type: "TableTypeProperty",
|
|
2393
|
+
name,
|
|
2394
|
+
key: tokenIdentifier(keyTok),
|
|
2395
|
+
valueType,
|
|
2396
|
+
optional: optional2,
|
|
2397
|
+
readonly: true,
|
|
2398
|
+
...spanFrom(propStart, this.previous())
|
|
2399
|
+
});
|
|
2321
2400
|
} 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 === ":")) {
|
|
2322
|
-
const
|
|
2401
|
+
const keyTok = this.expectIdentifier();
|
|
2402
|
+
const name = keyTok.value;
|
|
2323
2403
|
const optional2 = this.matchPunctuator("?");
|
|
2324
2404
|
this.expectPunctuator(":");
|
|
2325
2405
|
const valueType = this.parseType();
|
|
2326
2406
|
properties.push({
|
|
2327
2407
|
type: "TableTypeProperty",
|
|
2328
2408
|
name,
|
|
2409
|
+
key: tokenIdentifier(keyTok),
|
|
2329
2410
|
valueType,
|
|
2330
|
-
optional: optional2
|
|
2411
|
+
optional: optional2,
|
|
2412
|
+
...spanFrom(propStart, this.previous())
|
|
2331
2413
|
});
|
|
2332
2414
|
} else {
|
|
2333
2415
|
this.error("Expected object type property ('name: T' or '[K]: V'); use 'T[]' for arrays and '[T, U]' for tuples");
|
|
@@ -2389,6 +2471,7 @@ var Parser = class {
|
|
|
2389
2471
|
list.push({
|
|
2390
2472
|
type: "GenericTypeParameter",
|
|
2391
2473
|
name: nameTok.value,
|
|
2474
|
+
id: tokenIdentifier(nameTok),
|
|
2392
2475
|
isPack,
|
|
2393
2476
|
isConst: isConst || void 0,
|
|
2394
2477
|
constraint,
|
|
@@ -2518,6 +2601,15 @@ var Analyzer = class {
|
|
|
2518
2601
|
}
|
|
2519
2602
|
return this.getOrCreateGlobalBinding(name);
|
|
2520
2603
|
}
|
|
2604
|
+
/** Like `resolve`, but never creates a global: `undefined` when no
|
|
2605
|
+
* enclosing scope declares the name. */
|
|
2606
|
+
lookup(scope, name) {
|
|
2607
|
+
for (let s = scope; s; s = s.parent) {
|
|
2608
|
+
const id = s.declarations.get(name);
|
|
2609
|
+
if (id !== void 0) return id;
|
|
2610
|
+
}
|
|
2611
|
+
return void 0;
|
|
2612
|
+
}
|
|
2521
2613
|
getOrCreateGlobalBinding(name) {
|
|
2522
2614
|
const existing = this.globalScope.declarations.get(name);
|
|
2523
2615
|
if (existing !== void 0) return existing;
|
|
@@ -2633,12 +2725,14 @@ var Analyzer = class {
|
|
|
2633
2725
|
switch (stmt.type) {
|
|
2634
2726
|
case "VariableDeclaration": {
|
|
2635
2727
|
for (const init of stmt.init) this.visitExpression(init, scope);
|
|
2728
|
+
for (const name of stmt.names) this.visitType(name.typeAnnotation, scope);
|
|
2636
2729
|
const isConst = stmt.kind === "const";
|
|
2637
2730
|
for (const name of stmt.names) this.declarePattern(scope, name, "local", scope, isConst);
|
|
2638
2731
|
return;
|
|
2639
2732
|
}
|
|
2640
2733
|
case "FunctionDeclaration": {
|
|
2641
2734
|
this.declare(scope, stmt.name.name, "local", stmt.name, stmt.kind === "const");
|
|
2735
|
+
for (const signature of stmt.signatures ?? []) this.visitSignature(signature, scope);
|
|
2642
2736
|
this.visitFunctionBody(stmt.func, scope);
|
|
2643
2737
|
return;
|
|
2644
2738
|
}
|
|
@@ -2648,6 +2742,7 @@ var Analyzer = class {
|
|
|
2648
2742
|
} else {
|
|
2649
2743
|
this.reference(scope, stmt.target.base);
|
|
2650
2744
|
}
|
|
2745
|
+
for (const signature of stmt.signatures ?? []) this.visitSignature(signature, scope);
|
|
2651
2746
|
this.visitFunctionBody(stmt.func, scope, stmt.isMethod);
|
|
2652
2747
|
return;
|
|
2653
2748
|
}
|
|
@@ -2721,10 +2816,13 @@ var Analyzer = class {
|
|
|
2721
2816
|
case "BreakStatement":
|
|
2722
2817
|
case "ContinueStatement":
|
|
2723
2818
|
case "ErrorStatement":
|
|
2819
|
+
return;
|
|
2724
2820
|
case "DeclareStatement":
|
|
2821
|
+
this.visitType(stmt.valueType, scope);
|
|
2725
2822
|
return;
|
|
2726
2823
|
case "TypeAliasStatement":
|
|
2727
2824
|
case "ExportTypeAliasStatement":
|
|
2825
|
+
this.visitType(stmt.definition, scope);
|
|
2728
2826
|
return;
|
|
2729
2827
|
case "ImportStatement": {
|
|
2730
2828
|
if (stmt.defaultImport) {
|
|
@@ -2741,6 +2839,18 @@ var Analyzer = class {
|
|
|
2741
2839
|
case "ExportDefaultStatement":
|
|
2742
2840
|
this.visitExpression(stmt.declaration, scope);
|
|
2743
2841
|
return;
|
|
2842
|
+
case "ExportNamedStatement":
|
|
2843
|
+
if (!stmt.source) {
|
|
2844
|
+
for (const specifier of stmt.specifiers) {
|
|
2845
|
+
const id = this.lookup(scope, specifier.local.name);
|
|
2846
|
+
if (id === void 0) continue;
|
|
2847
|
+
this.bindingOf.set(specifier.local, id);
|
|
2848
|
+
this.bindings.get(id).references.push(specifier.local);
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
return;
|
|
2852
|
+
case "ExportAllStatement":
|
|
2853
|
+
return;
|
|
2744
2854
|
}
|
|
2745
2855
|
}
|
|
2746
2856
|
// ---------------- functions ----------------
|
|
@@ -2748,6 +2858,7 @@ var Analyzer = class {
|
|
|
2748
2858
|
const fnScope = childScope(outerScope);
|
|
2749
2859
|
func.params.forEach((param, i) => {
|
|
2750
2860
|
const kind = isMethod && i === 0 ? "self" : "param";
|
|
2861
|
+
this.visitType(param.typeAnnotation, fnScope);
|
|
2751
2862
|
if (param.default) this.visitExpression(param.default, fnScope);
|
|
2752
2863
|
if (param.pattern) {
|
|
2753
2864
|
this.declarePattern(fnScope, param.pattern, kind, fnScope);
|
|
@@ -2755,8 +2866,37 @@ var Analyzer = class {
|
|
|
2755
2866
|
this.declare(fnScope, param.name, kind, param);
|
|
2756
2867
|
}
|
|
2757
2868
|
});
|
|
2869
|
+
this.visitType(func.varargTypeAnnotation, fnScope);
|
|
2870
|
+
this.visitType(func.returnType, fnScope);
|
|
2758
2871
|
this.visitBlock(func.body, fnScope);
|
|
2759
2872
|
}
|
|
2873
|
+
/** An overload signature: no body and no bindings, but its types can hold
|
|
2874
|
+
* a `typeof x`. */
|
|
2875
|
+
visitSignature(signature, scope) {
|
|
2876
|
+
for (const param of signature.params) this.visitType(param.typeAnnotation, scope);
|
|
2877
|
+
this.visitType(signature.returnType, scope);
|
|
2878
|
+
}
|
|
2879
|
+
/** Resolve the value references inside a type. Only `typeof x` has any —
|
|
2880
|
+
* everything else in a type names types, which live in their own
|
|
2881
|
+
* namespace and are not this pass's business. */
|
|
2882
|
+
visitType(node, scope) {
|
|
2883
|
+
if (!node) return;
|
|
2884
|
+
const walk = (value) => {
|
|
2885
|
+
if (!value || typeof value !== "object") return;
|
|
2886
|
+
if (Array.isArray(value)) {
|
|
2887
|
+
for (const item of value) walk(item);
|
|
2888
|
+
return;
|
|
2889
|
+
}
|
|
2890
|
+
if (value.type === "TypeofTypeNode") {
|
|
2891
|
+
this.visitExpression(value.expression, scope);
|
|
2892
|
+
return;
|
|
2893
|
+
}
|
|
2894
|
+
for (const key of Object.keys(value)) {
|
|
2895
|
+
if (key !== "line" && key !== "column") walk(value[key]);
|
|
2896
|
+
}
|
|
2897
|
+
};
|
|
2898
|
+
walk(node);
|
|
2899
|
+
}
|
|
2760
2900
|
// ---------------- expressions ----------------
|
|
2761
2901
|
visitExpression(expr, scope) {
|
|
2762
2902
|
switch (expr.type) {
|
|
@@ -2815,6 +2955,11 @@ var Analyzer = class {
|
|
|
2815
2955
|
return;
|
|
2816
2956
|
case "TypeAssertionExpression":
|
|
2817
2957
|
this.visitExpression(expr.expression, scope);
|
|
2958
|
+
this.visitType(expr.typeAnnotation, scope);
|
|
2959
|
+
return;
|
|
2960
|
+
case "SatisfiesExpression":
|
|
2961
|
+
this.visitExpression(expr.expression, scope);
|
|
2962
|
+
this.visitType(expr.typeAnnotation, scope);
|
|
2818
2963
|
return;
|
|
2819
2964
|
case "IfElseExpression":
|
|
2820
2965
|
for (const clause of expr.clauses) {
|
|
@@ -3485,6 +3630,94 @@ function formatKey(k) {
|
|
|
3485
3630
|
function analyzeTypes(program, scopes, options = {}) {
|
|
3486
3631
|
return new TypeAnalyzer(program, scopes, options).run();
|
|
3487
3632
|
}
|
|
3633
|
+
function moduleExports(program, scopes, types, resolveModule) {
|
|
3634
|
+
const byDeclaration = /* @__PURE__ */ new Map();
|
|
3635
|
+
for (const binding of scopes.bindings.values()) {
|
|
3636
|
+
if (binding.declarationNode) byDeclaration.set(binding.declarationNode, binding.id);
|
|
3637
|
+
}
|
|
3638
|
+
const values = /* @__PURE__ */ new Map();
|
|
3639
|
+
const exportedTypes = /* @__PURE__ */ new Map();
|
|
3640
|
+
let defaultType;
|
|
3641
|
+
const stars = [];
|
|
3642
|
+
const setValue = (name, type) => {
|
|
3643
|
+
if (name === "default") defaultType = type;
|
|
3644
|
+
else values.set(name, type);
|
|
3645
|
+
};
|
|
3646
|
+
const reexport = (from, name, as) => {
|
|
3647
|
+
if (!from || from.partial) {
|
|
3648
|
+
setValue(as, anyType);
|
|
3649
|
+
return;
|
|
3650
|
+
}
|
|
3651
|
+
if (name === "default") {
|
|
3652
|
+
if (from.default) setValue(as, from.default);
|
|
3653
|
+
return;
|
|
3654
|
+
}
|
|
3655
|
+
const value = from.values.get(name);
|
|
3656
|
+
if (value) setValue(as, value);
|
|
3657
|
+
const type = from.types.get(name);
|
|
3658
|
+
if (type) exportedTypes.set(as, type);
|
|
3659
|
+
};
|
|
3660
|
+
const aliasParams = (name) => {
|
|
3661
|
+
for (const s of program.body.statements) {
|
|
3662
|
+
const alias = s.type === "TypeAliasStatement" ? s : s.type === "ExportTypeAliasStatement" ? s.alias : void 0;
|
|
3663
|
+
if (alias?.name.name === name) return alias.generics.map((g) => g.name);
|
|
3664
|
+
}
|
|
3665
|
+
return [];
|
|
3666
|
+
};
|
|
3667
|
+
const exportName = (declaration, name) => {
|
|
3668
|
+
const id = byDeclaration.get(declaration);
|
|
3669
|
+
values.set(name, (id !== void 0 ? types.bindingType.get(id) : void 0) ?? anyType);
|
|
3670
|
+
};
|
|
3671
|
+
const exportPattern = (target) => {
|
|
3672
|
+
switch (target.type) {
|
|
3673
|
+
case "IdentifierPattern":
|
|
3674
|
+
exportName(target, target.name);
|
|
3675
|
+
return;
|
|
3676
|
+
case "ObjectPattern":
|
|
3677
|
+
for (const p of target.properties) exportPattern(p.value);
|
|
3678
|
+
if (target.rest) exportPattern(target.rest);
|
|
3679
|
+
return;
|
|
3680
|
+
case "ArrayPattern":
|
|
3681
|
+
for (const el of target.elements) if (el) exportPattern(el.value);
|
|
3682
|
+
if (target.rest) exportPattern(target.rest);
|
|
3683
|
+
return;
|
|
3684
|
+
}
|
|
3685
|
+
};
|
|
3686
|
+
for (const stmt of program.body.statements) {
|
|
3687
|
+
if (stmt.type === "ExportStatement") {
|
|
3688
|
+
const declaration = stmt.declaration;
|
|
3689
|
+
if (declaration.type === "FunctionDeclaration") exportName(declaration.name, declaration.name.name);
|
|
3690
|
+
else for (const target of declaration.names) exportPattern(target);
|
|
3691
|
+
} else if (stmt.type === "ExportTypeAliasStatement") {
|
|
3692
|
+
const name = stmt.alias.name.name;
|
|
3693
|
+
const type = types.aliases.get(name);
|
|
3694
|
+
if (type) exportedTypes.set(name, { type, params: stmt.alias.generics.map((g) => g.name) });
|
|
3695
|
+
} else if (stmt.type === "ExportDefaultStatement") {
|
|
3696
|
+
defaultType = types.typeOf.get(stmt.declaration) ?? anyType;
|
|
3697
|
+
} else if (stmt.type === "ExportNamedStatement") {
|
|
3698
|
+
if (stmt.source) {
|
|
3699
|
+
const from = resolveModule?.(stmt.source.value);
|
|
3700
|
+
for (const s of stmt.specifiers) reexport(from, s.local.name, s.exported.name);
|
|
3701
|
+
} else {
|
|
3702
|
+
for (const s of stmt.specifiers) {
|
|
3703
|
+
const id = scopes.bindingOf.get(s.local);
|
|
3704
|
+
if (id !== void 0) setValue(s.exported.name, types.bindingType.get(id) ?? anyType);
|
|
3705
|
+
const alias = types.aliases.get(s.local.name);
|
|
3706
|
+
if (alias) exportedTypes.set(s.exported.name, { type: alias, params: aliasParams(s.local.name) });
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
} else if (stmt.type === "ExportAllStatement") {
|
|
3710
|
+
stars.push(stmt.source.value);
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
for (const specifier of stars) {
|
|
3714
|
+
const from = resolveModule?.(specifier);
|
|
3715
|
+
if (!from || from.partial) continue;
|
|
3716
|
+
for (const [name, type] of from.values) if (!values.has(name)) values.set(name, type);
|
|
3717
|
+
for (const [name, type] of from.types) if (!exportedTypes.has(name)) exportedTypes.set(name, type);
|
|
3718
|
+
}
|
|
3719
|
+
return { values, types: exportedTypes, default: defaultType };
|
|
3720
|
+
}
|
|
3488
3721
|
function bindKey(id) {
|
|
3489
3722
|
return `$${id}`;
|
|
3490
3723
|
}
|
|
@@ -3598,6 +3831,7 @@ var TypeAnalyzer = class {
|
|
|
3598
3831
|
typeOf = /* @__PURE__ */ new Map();
|
|
3599
3832
|
bindingType = /* @__PURE__ */ new Map();
|
|
3600
3833
|
narrowedTypeOf = /* @__PURE__ */ new Map();
|
|
3834
|
+
typeOfTypeNode = /* @__PURE__ */ new Map();
|
|
3601
3835
|
/** Public: each alias resolved once (generic aliases keep their params as
|
|
3602
3836
|
* `typeParam` nodes in the body). */
|
|
3603
3837
|
aliases = /* @__PURE__ */ new Map();
|
|
@@ -3632,6 +3866,10 @@ var TypeAnalyzer = class {
|
|
|
3632
3866
|
* (`type Tree = { children: Tree[] }`); it resolves to a nominal ref. */
|
|
3633
3867
|
resolvingAliases = /* @__PURE__ */ new Set();
|
|
3634
3868
|
diagnostics = [];
|
|
3869
|
+
/** `import`ed type names, from `resolveModule`. */
|
|
3870
|
+
importedTypes = /* @__PURE__ */ new Map();
|
|
3871
|
+
/** `resolveModule` results, one lookup per module path. */
|
|
3872
|
+
resolvedModules = /* @__PURE__ */ new Map();
|
|
3635
3873
|
emitDiagnostics;
|
|
3636
3874
|
/** Recursion guard for `preVisitBody`. */
|
|
3637
3875
|
preVisitDepth = 0;
|
|
@@ -3640,6 +3878,7 @@ var TypeAnalyzer = class {
|
|
|
3640
3878
|
this.registerAliasDefs(this.program.body);
|
|
3641
3879
|
for (const lib of this.options.libs ?? []) this.harvestDeclares(lib.body);
|
|
3642
3880
|
this.harvestDeclares(this.program.body);
|
|
3881
|
+
this.registerImportedTypes();
|
|
3643
3882
|
this.resolveAllAliases();
|
|
3644
3883
|
this.indexDeclarations();
|
|
3645
3884
|
for (const [name, id] of this.scopes.globalsByName) {
|
|
@@ -3657,13 +3896,51 @@ var TypeAnalyzer = class {
|
|
|
3657
3896
|
typeOf: this.typeOf,
|
|
3658
3897
|
bindingType: this.bindingType,
|
|
3659
3898
|
narrowedTypeOf: this.narrowedTypeOf,
|
|
3660
|
-
|
|
3899
|
+
typeOfTypeNode: this.typeOfTypeNode,
|
|
3900
|
+
aliases: this.resolveDeferredAliases(),
|
|
3661
3901
|
diagnostics: this.diagnostics
|
|
3662
3902
|
};
|
|
3663
3903
|
}
|
|
3664
3904
|
// --------------------------------------------------------
|
|
3665
3905
|
// Aliases
|
|
3666
3906
|
// --------------------------------------------------------
|
|
3907
|
+
moduleFor(specifier) {
|
|
3908
|
+
if (!this.resolvedModules.has(specifier)) {
|
|
3909
|
+
this.resolvedModules.set(specifier, this.options.resolveModule?.(specifier));
|
|
3910
|
+
}
|
|
3911
|
+
return this.resolvedModules.get(specifier);
|
|
3912
|
+
}
|
|
3913
|
+
/** `export ... from "./x"`: the module must exist, and so must each name. */
|
|
3914
|
+
checkReexport(source, names) {
|
|
3915
|
+
if (!this.options.resolveModule || !this.emitDiagnostics) return;
|
|
3916
|
+
const exports2 = this.moduleFor(source.value);
|
|
3917
|
+
if (!exports2) {
|
|
3918
|
+
this.diagnostics.push({ node: source, message: `Cannot find module '${source.value}'` });
|
|
3919
|
+
return;
|
|
3920
|
+
}
|
|
3921
|
+
if (exports2.partial) return;
|
|
3922
|
+
for (const name of names) {
|
|
3923
|
+
const found = name.name === "default" ? exports2.default !== void 0 : exports2.values.has(name.name) || exports2.types.has(name.name);
|
|
3924
|
+
if (!found) {
|
|
3925
|
+
this.diagnostics.push({ node: name, message: `Module '${source.value}' has no exported member '${name.name}'` });
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
}
|
|
3929
|
+
registerImportedTypes() {
|
|
3930
|
+
if (!this.options.resolveModule) return;
|
|
3931
|
+
for (const stmt of this.program.body.statements) {
|
|
3932
|
+
if (stmt.type !== "ImportStatement") continue;
|
|
3933
|
+
const exports2 = this.moduleFor(stmt.source.value);
|
|
3934
|
+
if (!exports2) continue;
|
|
3935
|
+
for (const s of stmt.specifiers) {
|
|
3936
|
+
const exported = exports2.types.get(s.imported.name);
|
|
3937
|
+
if (exported) {
|
|
3938
|
+
this.importedTypes.set(s.local.name, exported);
|
|
3939
|
+
this.aliases.set(s.local.name, exported.type);
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3667
3944
|
registerAliasDefs(block) {
|
|
3668
3945
|
for (const stmt of block.statements) {
|
|
3669
3946
|
const alias = stmt.type === "TypeAliasStatement" ? stmt : stmt.type === "ExportTypeAliasStatement" ? stmt.alias : void 0;
|
|
@@ -3683,10 +3960,22 @@ var TypeAnalyzer = class {
|
|
|
3683
3960
|
}
|
|
3684
3961
|
resolveAllAliases() {
|
|
3685
3962
|
for (const [name, def] of this.aliasDefs) {
|
|
3963
|
+
if (containsTypeQuery(def.node)) continue;
|
|
3964
|
+
this.withTypeParams(def.params, () => {
|
|
3965
|
+
this.aliases.set(name, this.resolveType(def.node));
|
|
3966
|
+
});
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
/** The aliases `resolveAllAliases` left for later, now that every binding
|
|
3970
|
+
* has its type. */
|
|
3971
|
+
resolveDeferredAliases() {
|
|
3972
|
+
for (const [name, def] of this.aliasDefs) {
|
|
3973
|
+
if (this.aliases.has(name)) continue;
|
|
3686
3974
|
this.withTypeParams(def.params, () => {
|
|
3687
3975
|
this.aliases.set(name, this.resolveType(def.node));
|
|
3688
3976
|
});
|
|
3689
3977
|
}
|
|
3978
|
+
return this.aliases;
|
|
3690
3979
|
}
|
|
3691
3980
|
withTypeParams(params, fn2) {
|
|
3692
3981
|
const start = this.typeParamScope.length;
|
|
@@ -3727,6 +4016,11 @@ var TypeAnalyzer = class {
|
|
|
3727
4016
|
// TypeNode -> Type
|
|
3728
4017
|
// --------------------------------------------------------
|
|
3729
4018
|
resolveType(node) {
|
|
4019
|
+
const type = this.resolveTypeNode(node);
|
|
4020
|
+
if (this.instantiationDepth === 0) this.typeOfTypeNode.set(node, type);
|
|
4021
|
+
return type;
|
|
4022
|
+
}
|
|
4023
|
+
resolveTypeNode(node) {
|
|
3730
4024
|
switch (node.type) {
|
|
3731
4025
|
case "TypeReference": {
|
|
3732
4026
|
const name = node.namespace ? `${node.namespace}.${node.base}` : node.base;
|
|
@@ -3767,6 +4061,16 @@ var TypeAnalyzer = class {
|
|
|
3767
4061
|
typeArguments: node.typeArguments.map((a) => this.resolveType(a))
|
|
3768
4062
|
});
|
|
3769
4063
|
}
|
|
4064
|
+
const imported = this.importedTypes.get(node.base);
|
|
4065
|
+
if (imported) {
|
|
4066
|
+
if (!imported.params.length) return imported.type;
|
|
4067
|
+
const subst = /* @__PURE__ */ new Map();
|
|
4068
|
+
imported.params.forEach((name2, i) => {
|
|
4069
|
+
const arg = node.typeArguments[i];
|
|
4070
|
+
subst.set(name2, arg ? this.resolveType(arg) : unknownType);
|
|
4071
|
+
});
|
|
4072
|
+
return this.reduceType(substitute(imported.type, subst));
|
|
4073
|
+
}
|
|
3770
4074
|
const lib = this.options.libTypes?.[node.base];
|
|
3771
4075
|
if (lib) return lib;
|
|
3772
4076
|
}
|
|
@@ -4319,11 +4623,49 @@ var TypeAnalyzer = class {
|
|
|
4319
4623
|
case "ExportDefaultStatement":
|
|
4320
4624
|
this.infer(stmt.declaration, env);
|
|
4321
4625
|
return;
|
|
4626
|
+
case "ExportNamedStatement": {
|
|
4627
|
+
if (stmt.source) {
|
|
4628
|
+
this.checkReexport(stmt.source, stmt.specifiers.map((s) => s.local));
|
|
4629
|
+
return;
|
|
4630
|
+
}
|
|
4631
|
+
for (const s of stmt.specifiers) {
|
|
4632
|
+
if (this.bindingIdOf(s.local) !== void 0) {
|
|
4633
|
+
this.infer(s.local, env);
|
|
4634
|
+
} else if (!this.aliasDefs.has(s.local.name) && !this.importedTypes.has(s.local.name)) {
|
|
4635
|
+
if (this.emitDiagnostics) {
|
|
4636
|
+
this.diagnostics.push({ node: s.local, message: `Cannot find name '${s.local.name}' to export` });
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
return;
|
|
4641
|
+
}
|
|
4642
|
+
case "ExportAllStatement":
|
|
4643
|
+
this.checkReexport(stmt.source, []);
|
|
4644
|
+
return;
|
|
4322
4645
|
case "ImportStatement": {
|
|
4323
|
-
const
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4646
|
+
const resolving = this.options.resolveModule !== void 0;
|
|
4647
|
+
const exports2 = resolving ? this.moduleFor(stmt.source.value) : void 0;
|
|
4648
|
+
const specifier = stmt.source.value;
|
|
4649
|
+
const report = (node, message) => {
|
|
4650
|
+
if (this.emitDiagnostics) this.diagnostics.push({ node, message });
|
|
4651
|
+
};
|
|
4652
|
+
if (resolving && !exports2) report(stmt.source, `Cannot find module '${specifier}'`);
|
|
4653
|
+
const usable = exports2 && !exports2.partial ? exports2 : void 0;
|
|
4654
|
+
if (stmt.defaultImport) {
|
|
4655
|
+
if (usable && usable.default === void 0) {
|
|
4656
|
+
report(stmt.defaultImport, `Module '${specifier}' has no default export`);
|
|
4657
|
+
}
|
|
4658
|
+
const id = this.bindingIdByName(stmt.defaultImport.name, stmt.defaultImport);
|
|
4659
|
+
if (id !== void 0) this.bindingType.set(id, usable?.default ?? anyType);
|
|
4660
|
+
}
|
|
4661
|
+
for (const s of stmt.specifiers) {
|
|
4662
|
+
const value = usable?.values.get(s.imported.name);
|
|
4663
|
+
if (usable && !value && !usable.types.has(s.imported.name)) {
|
|
4664
|
+
report(s.imported, `Module '${specifier}' has no exported member '${s.imported.name}'`);
|
|
4665
|
+
}
|
|
4666
|
+
const id = this.bindingIdByName(s.local.name, s.local);
|
|
4667
|
+
if (id !== void 0) this.bindingType.set(id, value ?? anyType);
|
|
4668
|
+
}
|
|
4327
4669
|
return;
|
|
4328
4670
|
}
|
|
4329
4671
|
case "BreakStatement":
|
|
@@ -4626,11 +4968,18 @@ var TypeAnalyzer = class {
|
|
|
4626
4968
|
inferFunctionBody(func, env) {
|
|
4627
4969
|
const names = func.generics.map((g) => g.name);
|
|
4628
4970
|
return this.withTypeParams(func.generics, () => {
|
|
4629
|
-
const params = func.params.map((p) =>
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4971
|
+
const params = func.params.map((p) => {
|
|
4972
|
+
const type = this.paramType(p, env);
|
|
4973
|
+
if (!p.pattern) {
|
|
4974
|
+
const id = this.bindingIdByName(p.name, p);
|
|
4975
|
+
if (id !== void 0 && !this.bindingType.has(id)) this.bindingType.set(id, type);
|
|
4976
|
+
}
|
|
4977
|
+
return {
|
|
4978
|
+
name: p.pattern ? void 0 : p.name,
|
|
4979
|
+
type,
|
|
4980
|
+
optional: p.optional || p.default !== void 0
|
|
4981
|
+
};
|
|
4982
|
+
});
|
|
4634
4983
|
const bodyEnv = forkEnv(env);
|
|
4635
4984
|
for (const p of func.params) {
|
|
4636
4985
|
if (p.pattern) this.bindPattern(p.pattern, this.paramType(p, bodyEnv), bodyEnv, "widen");
|
|
@@ -5484,6 +5833,12 @@ var TypeAnalyzer = class {
|
|
|
5484
5833
|
return this.bindingByDecl.get(node) ?? this.bindingByPos.get(posKey(name, node.line.start, node.column.start));
|
|
5485
5834
|
}
|
|
5486
5835
|
};
|
|
5836
|
+
function containsTypeQuery(node) {
|
|
5837
|
+
if (!node || typeof node !== "object") return false;
|
|
5838
|
+
if (Array.isArray(node)) return node.some(containsTypeQuery);
|
|
5839
|
+
if (node.type === "TypeofTypeNode") return true;
|
|
5840
|
+
return Object.values(node).some(containsTypeQuery);
|
|
5841
|
+
}
|
|
5487
5842
|
|
|
5488
5843
|
// src/lib/luau.ts
|
|
5489
5844
|
var import_node_fs = require("fs");
|
|
@@ -5549,6 +5904,7 @@ var index_default = luautparser;
|
|
|
5549
5904
|
luauLib,
|
|
5550
5905
|
luautparser,
|
|
5551
5906
|
matchInfer,
|
|
5907
|
+
moduleExports,
|
|
5552
5908
|
narrowExclude,
|
|
5553
5909
|
narrowFalsy,
|
|
5554
5910
|
narrowTo,
|