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