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/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: [], ...spanFrom(start, start) },
957
- ...spanFrom(start, this.previous())
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.error("Expected 'const', 'let', 'type', or 'default' after 'export'");
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 name = this.expectIdentifier().value;
2086
- return { type: "InferTypeNode", name, ...spanFrom(t, this.previous()) };
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
- name = this.expectIdentifier().value;
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 parameter = this.expectIdentifier().value;
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 name = this.expectIdentifier().value;
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({ type: "TableTypeProperty", name, valueType, optional: optional2, readonly: true });
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 name = this.expectIdentifier().value;
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
- aliases: this.aliases,
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 ids = [];
4324
- if (stmt.defaultImport) ids.push(this.bindingIdOf(stmt.defaultImport));
4325
- for (const s of stmt.specifiers) ids.push(this.bindingIdOf(s.local));
4326
- for (const id of ids) if (id !== void 0) this.bindingType.set(id, anyType);
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
- name: p.pattern ? void 0 : p.name,
4631
- type: this.paramType(p, env),
4632
- optional: p.optional || p.default !== void 0
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,