@sandstone-mc/mcdoc-ts-generator 0.1.8 → 0.1.10

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/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { dirname, resolve } from "path";
5
- import { fileURLToPath, pathToFileURL } from "url";
5
+ import { pathToFileURL } from "url";
6
6
  import { promisify } from "util";
7
7
  import * as fs2 from "fs";
8
8
  import {
@@ -198,12 +198,17 @@ function add_import(imports, add_import2) {
198
198
  }
199
199
  return imports;
200
200
  }
201
- function merge_imports(imports, new_imports) {
201
+ function merge_imports(imports, new_imports, filter) {
202
202
  if (imports === undefined) {
203
- return new_imports;
203
+ if (filter === undefined) {
204
+ return new_imports;
205
+ }
204
206
  }
205
207
  for (const import_path of new_imports.ordered) {
206
- if (!imports.check.has(import_path)) {
208
+ if (filter?.has(import_path)) {
209
+ continue;
210
+ }
211
+ if (imports === undefined || !imports.check.has(import_path)) {
207
212
  imports = add_import(imports, import_path);
208
213
  }
209
214
  }
@@ -2232,41 +2237,38 @@ var SimpleKeyIndex = JSON.stringify([{
2232
2237
  }]);
2233
2238
  var Fallback = Bind.StringLiteral("%fallback");
2234
2239
  var None = Bind.StringLiteral("%none");
2235
- function DispatcherGeneric(registry, args) {
2236
- return factory21.createTypeReferenceNode("Dispatcher", [
2237
- Bind.StringLiteral(registry),
2238
- factory21.createTupleTypeNode(args)
2239
- ]);
2240
+ function SymbolGeneric(symbol_name, generics, case_arg) {
2241
+ return factory21.createTypeReferenceNode(symbol_name, [...generics, case_arg]);
2240
2242
  }
2241
- function DispatcherMapIndex(registry, key, generics) {
2242
- const dispatcher = factory21.createTypeReferenceNode("Dispatcher", [Bind.StringLiteral(registry), ...generics.length === 0 ? [] : [factory21.createTupleTypeNode(generics)]]);
2243
+ function SymbolMapIndex(symbol_name, key, generics) {
2244
+ const symbol_type = factory21.createTypeReferenceNode(symbol_name, generics.length === 0 ? undefined : generics);
2243
2245
  if (key.kind === ts22.SyntaxKind.LiteralType) {
2244
- return factory21.createIndexedAccessTypeNode(dispatcher, key);
2246
+ return factory21.createIndexedAccessTypeNode(symbol_type, key);
2245
2247
  }
2246
- return factory21.createParenthesizedType(factory21.createConditionalTypeNode(key, factory21.createTypeOperatorNode(ts22.SyntaxKind.KeyOfKeyword, dispatcher), factory21.createIndexedAccessTypeNode(dispatcher, key), factory21.createTypeReferenceNode("Record", [
2248
+ return factory21.createParenthesizedType(factory21.createConditionalTypeNode(key, factory21.createTypeOperatorNode(ts22.SyntaxKind.KeyOfKeyword, symbol_type), factory21.createIndexedAccessTypeNode(symbol_type, key), factory21.createTypeReferenceNode("Record", [
2247
2249
  factory21.createKeywordTypeNode(ts22.SyntaxKind.StringKeyword),
2248
2250
  factory21.createKeywordTypeNode(ts22.SyntaxKind.UnknownKeyword)
2249
2251
  ])));
2250
2252
  }
2251
- function DispatcherMapSubIndex(registry, member_key, generics, sub_index) {
2252
- const dispatcher = factory21.createTypeReferenceNode("Dispatcher", [Bind.StringLiteral(registry), ...generics.length === 0 ? [] : [factory21.createTupleTypeNode(generics)]]);
2253
- const dispatcher_member = factory21.createIndexedAccessTypeNode(dispatcher, member_key);
2254
- let indexed_dispatcher = undefined;
2253
+ function SymbolMapSubIndex(symbol_name, member_key, generics, sub_index) {
2254
+ const symbol_type = factory21.createTypeReferenceNode(symbol_name, generics.length === 0 ? undefined : generics);
2255
+ const symbol_member = factory21.createIndexedAccessTypeNode(symbol_type, member_key);
2256
+ let indexed_symbol = undefined;
2255
2257
  for (let i = sub_index.length;i > 0; i--) {
2256
2258
  const key = Bind.StringLiteral(sub_index[i - 1]);
2257
- let index_stack = dispatcher_member;
2259
+ let index_stack = symbol_member;
2258
2260
  for (let j = 0;j < i - 1; j++) {
2259
2261
  index_stack = factory21.createIndexedAccessTypeNode(index_stack, Bind.StringLiteral(sub_index[j]));
2260
2262
  }
2261
- if (indexed_dispatcher === undefined) {
2262
- indexed_dispatcher = factory21.createIndexedAccessTypeNode(index_stack, key);
2263
+ if (indexed_symbol === undefined) {
2264
+ indexed_symbol = factory21.createIndexedAccessTypeNode(index_stack, key);
2263
2265
  }
2264
- indexed_dispatcher = factory21.createParenthesizedType(factory21.createConditionalTypeNode(key, factory21.createTypeOperatorNode(ts22.SyntaxKind.KeyOfKeyword, index_stack), indexed_dispatcher, factory21.createTypeReferenceNode("Record", [
2266
+ indexed_symbol = factory21.createParenthesizedType(factory21.createConditionalTypeNode(key, factory21.createTypeOperatorNode(ts22.SyntaxKind.KeyOfKeyword, index_stack), indexed_symbol, factory21.createTypeReferenceNode("Record", [
2265
2267
  factory21.createKeywordTypeNode(ts22.SyntaxKind.StringKeyword),
2266
2268
  factory21.createKeywordTypeNode(ts22.SyntaxKind.UnknownKeyword)
2267
2269
  ])));
2268
2270
  }
2269
- return factory21.createParenthesizedType(factory21.createConditionalTypeNode(member_key, factory21.createTypeOperatorNode(ts22.SyntaxKind.KeyOfKeyword, dispatcher), indexed_dispatcher, factory21.createTypeReferenceNode("Record", [
2271
+ return factory21.createParenthesizedType(factory21.createConditionalTypeNode(member_key, factory21.createTypeOperatorNode(ts22.SyntaxKind.KeyOfKeyword, symbol_type), indexed_symbol, factory21.createTypeReferenceNode("Record", [
2270
2272
  factory21.createKeywordTypeNode(ts22.SyntaxKind.StringKeyword),
2271
2273
  factory21.createKeywordTypeNode(ts22.SyntaxKind.UnknownKeyword)
2272
2274
  ])));
@@ -2278,34 +2280,39 @@ function mcdoc_dispatcher(type) {
2278
2280
  const indices = dispatcher.parallelIndices;
2279
2281
  return (args) => {
2280
2282
  DispatcherArgs(args);
2281
- const dispatcher_import = `::java::dispatcher::Dispatcher`;
2283
+ const info = args.dispatcher_info?.get(registry);
2284
+ if (!info) {
2285
+ throw new Error(`[mcdoc_dispatcher] Unknown dispatcher: ${registry}`);
2286
+ }
2287
+ const { symbol_name } = info;
2288
+ const import_path = `::java::dispatcher::${symbol_name}`;
2282
2289
  let result_type;
2283
2290
  let child_dispatcher;
2284
2291
  const generics = args.generic_types ?? [];
2285
2292
  if (indices.length === 1 && indices[0].kind === "dynamic" && typeof indices[0].accessor.at(-1) === "string") {
2286
2293
  if (args.root_type) {
2287
- result_type = DispatcherGeneric(registry, [...generics, Fallback]);
2294
+ result_type = SymbolGeneric(symbol_name, generics, Fallback);
2288
2295
  } else {
2289
2296
  child_dispatcher = [[indices[0].accessor.length - 1, indices[0].accessor.at(-1)]];
2290
- const indexed_type = DispatcherMapIndex(registry, factory21.createTypeReferenceNode("S"), generics);
2297
+ const indexed_type = SymbolMapIndex(symbol_name, factory21.createTypeReferenceNode("S"), generics);
2291
2298
  const properties = args.dispatcher_properties?.get(registry);
2292
2299
  if (properties?.supports_none) {
2293
- result_type = factory21.createParenthesizedType(factory21.createConditionalTypeNode(factory21.createTypeReferenceNode("S"), factory21.createKeywordTypeNode(ts22.SyntaxKind.UndefinedKeyword), DispatcherGeneric(registry, [...generics, None]), indexed_type));
2300
+ result_type = factory21.createParenthesizedType(factory21.createConditionalTypeNode(factory21.createTypeReferenceNode("S"), factory21.createKeywordTypeNode(ts22.SyntaxKind.UndefinedKeyword), SymbolGeneric(symbol_name, generics, None), indexed_type));
2294
2301
  } else {
2295
2302
  result_type = indexed_type;
2296
2303
  }
2297
2304
  }
2298
2305
  } else if (indices.length === 1 && indices[0].kind === "static") {
2299
2306
  if (indices[0].value === "%fallback") {
2300
- result_type = DispatcherGeneric(registry, [...generics, Fallback]);
2307
+ result_type = SymbolGeneric(symbol_name, generics, Fallback);
2301
2308
  } else {
2302
- result_type = DispatcherMapIndex(registry, Bind.StringLiteral(indices[0].value), generics);
2309
+ result_type = SymbolMapIndex(symbol_name, Bind.StringLiteral(indices[0].value), generics);
2303
2310
  }
2304
2311
  } else if (JSON.stringify(indices) === SimpleKeyIndex) {
2305
2312
  if (args.index_keys !== undefined) {
2306
- result_type = DispatcherMapSubIndex(registry, factory21.createTypeReferenceNode("Key"), generics, args.index_keys);
2313
+ result_type = SymbolMapSubIndex(symbol_name, factory21.createTypeReferenceNode("Key"), generics, args.index_keys);
2307
2314
  } else {
2308
- result_type = DispatcherMapIndex(registry, factory21.createTypeReferenceNode("Key"), generics);
2315
+ result_type = SymbolMapIndex(symbol_name, factory21.createTypeReferenceNode("Key"), generics);
2309
2316
  }
2310
2317
  } else {
2311
2318
  throw new Error(`[mcdoc_dispatcher] Unsupported dispatcher: ${dispatcher}`);
@@ -2313,8 +2320,8 @@ function mcdoc_dispatcher(type) {
2313
2320
  return {
2314
2321
  type: result_type,
2315
2322
  imports: {
2316
- ordered: [dispatcher_import],
2317
- check: new Map([[dispatcher_import, 0]])
2323
+ ordered: [import_path],
2324
+ check: new Map([[import_path, 0]])
2318
2325
  },
2319
2326
  ...add({ child_dispatcher })
2320
2327
  };
@@ -2413,9 +2420,11 @@ function get_type_handler(type) {
2413
2420
  import ts24 from "typescript";
2414
2421
  var { factory: factory23 } = ts24;
2415
2422
  var dispatcher_references = new Map;
2416
- function dispatcher_symbol(id, name, members, dispatcher_properties, module_map, symbols) {
2423
+ var dispatcher_symbol_paths = new Map;
2424
+ function dispatcher_symbol(id, name, members, dispatcher_properties, dispatcher_info, module_map, symbols) {
2417
2425
  let imports = undefined;
2418
2426
  let has_references = false;
2427
+ const self_import = new Set([`::java::dispatcher::Symbol${name}`]);
2419
2428
  const member_types = [];
2420
2429
  const map_properties = [];
2421
2430
  const member_type_refs = [];
@@ -2451,17 +2460,18 @@ function dispatcher_symbol(id, name, members, dispatcher_properties, module_map,
2451
2460
  name: unknown_type_name,
2452
2461
  dispatcher_symbol: add_reference,
2453
2462
  dispatcher_properties,
2463
+ dispatcher_info,
2454
2464
  module_map,
2455
2465
  symbols
2456
2466
  });
2457
2467
  if ("imports" in result) {
2458
- imports = merge_imports(imports, result.imports);
2468
+ imports = merge_imports(imports, result.imports, self_import);
2459
2469
  }
2460
2470
  fallback_type_name = factory23.createTypeReferenceNode(unknown_type_name, has_generics ? generic_names : undefined);
2461
2471
  if (ts24.isTypeAliasDeclaration(result.type)) {
2462
2472
  member_types.push(result.type);
2463
2473
  } else {
2464
- member_types.push(factory23.createTypeAliasDeclaration(undefined, unknown_type_name, undefined, result.type));
2474
+ member_types.push(factory23.createTypeAliasDeclaration([factory23.createModifier(ts24.SyntaxKind.ExportKeyword)], unknown_type_name, undefined, result.type));
2465
2475
  }
2466
2476
  }
2467
2477
  const has_none = "%none" in members;
@@ -2473,11 +2483,12 @@ function dispatcher_symbol(id, name, members, dispatcher_properties, module_map,
2473
2483
  name: none_type_name,
2474
2484
  dispatcher_symbol: add_reference,
2475
2485
  dispatcher_properties,
2486
+ dispatcher_info,
2476
2487
  module_map,
2477
2488
  symbols
2478
2489
  });
2479
2490
  if ("imports" in result) {
2480
- imports = merge_imports(imports, result.imports);
2491
+ imports = merge_imports(imports, result.imports, self_import);
2481
2492
  }
2482
2493
  dispatcher_properties.set(id, {
2483
2494
  supports_none: true
@@ -2500,11 +2511,12 @@ function dispatcher_symbol(id, name, members, dispatcher_properties, module_map,
2500
2511
  name: member_type_name,
2501
2512
  dispatcher_symbol: add_reference,
2502
2513
  dispatcher_properties,
2514
+ dispatcher_info,
2503
2515
  module_map,
2504
2516
  symbols
2505
2517
  });
2506
2518
  if ("imports" in result) {
2507
- imports = merge_imports(imports, result.imports);
2519
+ imports = merge_imports(imports, result.imports, self_import);
2508
2520
  }
2509
2521
  if (ts24.isTypeAliasDeclaration(result.type)) {
2510
2522
  member_types.push(result.type);
@@ -2545,6 +2557,23 @@ var DispatcherSymbol = dispatcher_symbol;
2545
2557
  import { TaggableResourceLocationCategories } from "@spyglassmc/core";
2546
2558
  import ts25 from "typescript";
2547
2559
  var { factory: factory24 } = ts25;
2560
+ function export_dispatchers(paths) {
2561
+ const exports = [];
2562
+ for (const [path, info] of paths) {
2563
+ const relative_path = "./" + path.split("::").slice(2).join("/");
2564
+ const export_specifiers = [
2565
+ factory24.createExportSpecifier(false, undefined, info.symbol_name)
2566
+ ];
2567
+ if (info.has_fallback_type) {
2568
+ export_specifiers.push(factory24.createExportSpecifier(false, undefined, `${info.base_name}FallbackType`));
2569
+ }
2570
+ exports.push(factory24.createExportDeclaration(undefined, false, factory24.createNamedExports(export_specifiers), factory24.createStringLiteral(relative_path, true)));
2571
+ }
2572
+ return {
2573
+ exports,
2574
+ paths: new Set
2575
+ };
2576
+ }
2548
2577
  function export_registry(resolved_registries) {
2549
2578
  let imports;
2550
2579
  imports = add_import(imports, "sandstone::Set");
@@ -2564,68 +2593,6 @@ function export_registry(resolved_registries) {
2564
2593
  ...add({ imports })
2565
2594
  };
2566
2595
  }
2567
- function export_dispatcher(resolved_dispatchers) {
2568
- let imports;
2569
- const required_args_properties = [];
2570
- for (const [dispatcher_id, { import_path, generic_count }] of resolved_dispatchers) {
2571
- imports = add_import(imports, import_path);
2572
- const tuple_elements = [];
2573
- for (let i = 0;i < generic_count; i++) {
2574
- tuple_elements.push(factory24.createKeywordTypeNode(ts25.SyntaxKind.UnknownKeyword));
2575
- }
2576
- required_args_properties.push(factory24.createPropertySignature(undefined, factory24.createStringLiteral(dispatcher_id, true), undefined, factory24.createTupleTypeNode(tuple_elements)));
2577
- }
2578
- const required_args_type = factory24.createTypeAliasDeclaration(undefined, "DispatcherRequiredArgs", undefined, factory24.createTypeLiteralNode(required_args_properties));
2579
- const default_args_type = factory24.createTypeAliasDeclaration(undefined, "DefaultArgs", [factory24.createTypeParameterDeclaration(undefined, "R", factory24.createTypeOperatorNode(ts25.SyntaxKind.KeyOfKeyword, factory24.createTypeReferenceNode("DispatcherRequiredArgs")))], factory24.createConditionalTypeNode(factory24.createIndexedAccessTypeNode(factory24.createIndexedAccessTypeNode(factory24.createTypeReferenceNode("DispatcherRequiredArgs"), factory24.createTypeReferenceNode("R")), Bind.StringLiteral("length")), Bind.NumericLiteral(0), factory24.createTupleTypeNode([]), factory24.createKeywordTypeNode(ts25.SyntaxKind.NeverKeyword)));
2580
- const dispatchers_by_count = new Map;
2581
- for (const [id, dispatcher] of resolved_dispatchers) {
2582
- const count = dispatcher.generic_count;
2583
- if (!dispatchers_by_count.has(count)) {
2584
- dispatchers_by_count.set(count, []);
2585
- }
2586
- dispatchers_by_count.get(count).push([id, dispatcher]);
2587
- }
2588
- let apply_dispatcher_body = factory24.createKeywordTypeNode(ts25.SyntaxKind.NeverKeyword);
2589
- const sorted_counts = [...dispatchers_by_count.keys()].sort((a, b) => b - a);
2590
- for (const count of sorted_counts) {
2591
- const dispatchers = dispatchers_by_count.get(count);
2592
- for (const [dispatcher_id, { symbol_name }] of dispatchers) {
2593
- const param_names = ["A", "B", "C", "D"].slice(0, count);
2594
- const infer_params = param_names.map((name) => factory24.createInferTypeNode(factory24.createTypeParameterDeclaration(undefined, name)));
2595
- const type_refs = param_names.map((name) => factory24.createTypeReferenceNode(name));
2596
- const map_case = factory24.createConditionalTypeNode(factory24.createTypeReferenceNode("Args"), factory24.createTupleTypeNode(infer_params), factory24.createTypeReferenceNode(symbol_name, [...type_refs, Bind.StringLiteral("map")]), factory24.createKeywordTypeNode(ts25.SyntaxKind.NeverKeyword));
2597
- const none_case = factory24.createConditionalTypeNode(factory24.createTypeReferenceNode("Args"), factory24.createTupleTypeNode([...infer_params, Bind.StringLiteral("%none")]), factory24.createTypeReferenceNode(symbol_name, [...type_refs, Bind.StringLiteral("%none")]), map_case);
2598
- const fallback_case = factory24.createConditionalTypeNode(factory24.createTypeReferenceNode("Args"), factory24.createTupleTypeNode([...infer_params, Bind.StringLiteral("%fallback")]), factory24.createTypeReferenceNode(symbol_name, [...type_refs, Bind.StringLiteral("%fallback")]), none_case);
2599
- apply_dispatcher_body = factory24.createConditionalTypeNode(factory24.createTypeReferenceNode("R"), Bind.StringLiteral(dispatcher_id), fallback_case, apply_dispatcher_body);
2600
- }
2601
- }
2602
- const apply_dispatcher_type = factory24.createTypeAliasDeclaration(undefined, "ApplyDispatcher", [
2603
- factory24.createTypeParameterDeclaration(undefined, "R", factory24.createTypeOperatorNode(ts25.SyntaxKind.KeyOfKeyword, factory24.createTypeReferenceNode("DispatcherRequiredArgs"))),
2604
- factory24.createTypeParameterDeclaration(undefined, "Args", factory24.createTypeReferenceNode("Array", [
2605
- factory24.createKeywordTypeNode(ts25.SyntaxKind.UnknownKeyword)
2606
- ]))
2607
- ], apply_dispatcher_body);
2608
- const args_constraint = factory24.createTypeReferenceNode("Array", [
2609
- factory24.createKeywordTypeNode(ts25.SyntaxKind.UnknownKeyword)
2610
- ]);
2611
- const dispatcher_type = factory24.createTypeAliasDeclaration([factory24.createToken(ts25.SyntaxKind.ExportKeyword)], "Dispatcher", [
2612
- factory24.createTypeParameterDeclaration(undefined, "R", factory24.createTypeOperatorNode(ts25.SyntaxKind.KeyOfKeyword, factory24.createTypeReferenceNode("DispatcherRequiredArgs"))),
2613
- factory24.createTypeParameterDeclaration(undefined, "Args", args_constraint, factory24.createTypeReferenceNode("DefaultArgs", [factory24.createTypeReferenceNode("R")]))
2614
- ], factory24.createTypeReferenceNode("ApplyDispatcher", [
2615
- factory24.createTypeReferenceNode("R"),
2616
- factory24.createTypeReferenceNode("Args")
2617
- ]));
2618
- return {
2619
- exports: [
2620
- required_args_type,
2621
- default_args_type,
2622
- apply_dispatcher_type,
2623
- dispatcher_type
2624
- ],
2625
- paths: new Set,
2626
- ...add({ imports })
2627
- };
2628
- }
2629
2596
 
2630
2597
  // src/typegen/index.ts
2631
2598
  var { factory: factory25 } = ts26;
@@ -2635,6 +2602,7 @@ class TypesGenerator {
2635
2602
  dispatcher_properties = new Map;
2636
2603
  resolved_symbols = new Map;
2637
2604
  resolved_dispatchers = new Map;
2605
+ dispatcher_info = new Map;
2638
2606
  constructor() {}
2639
2607
  resolve_types(symbols, translation_keys) {
2640
2608
  console.log("registries");
@@ -2642,18 +2610,42 @@ class TypesGenerator {
2642
2610
  const registry_exports = export_registry(this.resolved_registries);
2643
2611
  this.resolved_symbols.set("::java::registry", registry_exports);
2644
2612
  const dispatchers = symbols.getVisibleSymbols("mcdoc/dispatcher");
2645
- for (const id of Object.keys(dispatchers)) {
2646
- if ("%none" in dispatchers[id].members) {
2647
- this.dispatcher_properties.set(id, { supports_none: true });
2648
- }
2649
- }
2613
+ console.log("dispatcher info");
2614
+ this.precompute_dispatcher_info(dispatchers);
2650
2615
  console.log("modules");
2651
2616
  const module_map = symbols.getVisibleSymbols("mcdoc");
2652
2617
  this.resolve_module_symbols(module_map, symbols);
2653
2618
  console.log("dispatchers");
2654
2619
  this.resolve_dispatcher_symbols(dispatchers, module_map, symbols);
2655
- const dispatcher_exports = export_dispatcher(this.resolved_dispatchers);
2656
- this.resolved_symbols.set("mcdoc::dispatcher", dispatcher_exports);
2620
+ const dispatcher_exports = export_dispatchers(dispatcher_symbol_paths);
2621
+ this.resolved_symbols.set("::java::dispatcher", dispatcher_exports);
2622
+ }
2623
+ precompute_dispatcher_info(dispatchers) {
2624
+ for (const id of Object.keys(dispatchers)) {
2625
+ const { members } = dispatchers[id];
2626
+ if (members === undefined) {
2627
+ continue;
2628
+ }
2629
+ if ("%none" in members) {
2630
+ this.dispatcher_properties.set(id, { supports_none: true });
2631
+ }
2632
+ const [namespace, _name] = id.split(":");
2633
+ const name = pascal_case(`${namespace === "mcdoc" ? "mcdoc_" : ""}${_name}`);
2634
+ const symbol_name = `Symbol${name}`;
2635
+ const first_member_key = Object.keys(members).find((k) => !k.startsWith("%"));
2636
+ let generic_count = 0;
2637
+ if (first_member_key) {
2638
+ const first_type = members[first_member_key].data.typeDef;
2639
+ if (first_type.kind === "template") {
2640
+ generic_count = first_type.typeParams.length;
2641
+ }
2642
+ }
2643
+ this.dispatcher_info.set(id, {
2644
+ symbol_name,
2645
+ generic_count,
2646
+ has_fallback_type: "%unknown" in members
2647
+ });
2648
+ }
2657
2649
  }
2658
2650
  resolve_registry_symbols(registries, translation_keys) {
2659
2651
  for (const registry_name of [...AllCategories]) {
@@ -2712,6 +2704,7 @@ class TypesGenerator {
2712
2704
  }
2713
2705
  const resolved_member = get_type_handler(type)(type)({
2714
2706
  dispatcher_properties: this.dispatcher_properties,
2707
+ dispatcher_info: this.dispatcher_info,
2715
2708
  root_type: true,
2716
2709
  name,
2717
2710
  module_path,
@@ -2749,7 +2742,7 @@ class TypesGenerator {
2749
2742
  }
2750
2743
  const [namespace, _name] = id.split(":");
2751
2744
  const name = pascal_case(`${namespace === "mcdoc" ? "mcdoc_" : ""}${_name}`);
2752
- const { types, imports, references, generic_count } = DispatcherSymbol(id, name, members, this.dispatcher_properties, module_map, symbols);
2745
+ const { types, imports, references, generic_count } = DispatcherSymbol(id, name, members, this.dispatcher_properties, this.dispatcher_info, module_map, symbols);
2753
2746
  let in_module = false;
2754
2747
  const symbol_path = (() => {
2755
2748
  if (namespace === "mcdoc") {
@@ -2764,6 +2757,12 @@ class TypesGenerator {
2764
2757
  }
2765
2758
  return `::java::_dispatcher::${_name}`;
2766
2759
  })();
2760
+ const info = this.dispatcher_info.get(id);
2761
+ dispatcher_symbol_paths.set(symbol_path, {
2762
+ symbol_name: `Symbol${name}`,
2763
+ base_name: name,
2764
+ has_fallback_type: info.has_fallback_type
2765
+ });
2767
2766
  const dispatcher_type_name = `Symbol${name}`;
2768
2767
  this.resolved_dispatchers.set(id, {
2769
2768
  import_path: `${symbol_path}::${dispatcher_type_name}`,
@@ -2774,18 +2773,35 @@ class TypesGenerator {
2774
2773
  if (in_module && this.resolved_symbols.has(symbol_path)) {
2775
2774
  const mod = this.resolved_symbols.get(symbol_path);
2776
2775
  mod.exports.push(...types);
2776
+ let module_has_imports = false;
2777
+ if (mod.imports !== undefined) {
2778
+ module_has_imports = true;
2779
+ }
2777
2780
  if (imports !== undefined) {
2778
- for (const path of imports.ordered) {
2779
- if (!mod.paths.has(path)) {
2780
- mod.imports = add_import(mod.imports, path);
2781
- }
2782
- }
2781
+ mod.imports = merge_imports(mod.imports, imports);
2782
+ }
2783
+ if (mod.imports !== undefined) {
2784
+ const same_module_pattern = new RegExp(`^${symbol_path}::[^:]+$`);
2785
+ mod.imports.ordered = mod.imports.ordered.filter((imp) => imp !== `::java::dispatcher::Symbol${name}` && !same_module_pattern.test(imp));
2783
2786
  }
2784
2787
  } else {
2788
+ let filtered_imports = imports;
2789
+ if (imports !== undefined) {
2790
+ const same_module_pattern = new RegExp(`^${symbol_path}::[^:]+$`);
2791
+ const filtered_ordered = imports.ordered.filter((imp) => imp !== `::java::dispatcher::Symbol${name}` && !same_module_pattern.test(imp));
2792
+ if (filtered_ordered.length > 0) {
2793
+ filtered_imports = {
2794
+ ordered: filtered_ordered,
2795
+ check: new Map(filtered_ordered.map((imp, i) => [imp, i]))
2796
+ };
2797
+ } else {
2798
+ filtered_imports = undefined;
2799
+ }
2800
+ }
2785
2801
  this.resolved_symbols.set(symbol_path, {
2786
2802
  exports: types,
2787
2803
  paths: new Set2,
2788
- ...add({ imports })
2804
+ ...add({ imports: filtered_imports })
2789
2805
  });
2790
2806
  }
2791
2807
  }
@@ -2821,7 +2837,7 @@ var eslint = new ESLint({
2821
2837
  "@stylistic": stylistic
2822
2838
  },
2823
2839
  rules: {
2824
- "@stylistic/indent": ["error", 4],
2840
+ "@stylistic/indent": ["error", 2],
2825
2841
  "@stylistic/quotes": ["error", "single"],
2826
2842
  "@stylistic/semi": ["error", "never"],
2827
2843
  "@stylistic/member-delimiter-style": ["error", {
@@ -2885,14 +2901,14 @@ function handle_imports(imports) {
2885
2901
  if (path.length === 0) {
2886
2902
  throw new Error(`[mcdoc_import] Import path has no module prefix: "${import_path}"`);
2887
2903
  } else if (path[1] === "java") {
2888
- file = `sandstone/arguments/generated/${path.slice(2).join("/")}.js`;
2904
+ file = `sandstone/arguments/generated/${path.slice(2).join("/")}.ts`;
2889
2905
  } else if (path[0] === "sandstone") {
2890
2906
  if (path.length === 1) {
2891
2907
  file = "sandstone";
2892
2908
  } else if (path[1] === "arguments" && path.length === 2) {
2893
2909
  file = "sandstone/arguments";
2894
2910
  } else {
2895
- file = `${path.join("/")}.js`;
2911
+ file = `${path.join("/")}.ts`;
2896
2912
  }
2897
2913
  } else {
2898
2914
  throw new Error(`[mcdoc_import] Unsupported import location "${path[0]}" in "${import_path}"`);
@@ -2924,7 +2940,7 @@ function handle_imports(imports) {
2924
2940
  // src/index.ts
2925
2941
  var writeFile2 = promisify(fs2.writeFile);
2926
2942
  var mkdir2 = promisify(fs2.mkdir);
2927
- var cache_root = join(dirname(fileURLToPath(import.meta.url)), "cache");
2943
+ var cache_root = join(process.cwd(), "cache");
2928
2944
  function registerAttributes(meta, release) {
2929
2945
  mcdoc5.runtime.registerAttribute(meta, "since", mcdoc5.runtime.attribute.validator.string, {
2930
2946
  filterElement: () => false
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
2
  import { dirname, resolve } from "path";
3
- import { fileURLToPath, pathToFileURL } from "url";
3
+ import { pathToFileURL } from "url";
4
4
  import { promisify } from "util";
5
5
  import * as fs2 from "fs";
6
6
  import {
@@ -196,12 +196,17 @@ function add_import(imports, add_import2) {
196
196
  }
197
197
  return imports;
198
198
  }
199
- function merge_imports(imports, new_imports) {
199
+ function merge_imports(imports, new_imports, filter) {
200
200
  if (imports === undefined) {
201
- return new_imports;
201
+ if (filter === undefined) {
202
+ return new_imports;
203
+ }
202
204
  }
203
205
  for (const import_path of new_imports.ordered) {
204
- if (!imports.check.has(import_path)) {
206
+ if (filter?.has(import_path)) {
207
+ continue;
208
+ }
209
+ if (imports === undefined || !imports.check.has(import_path)) {
205
210
  imports = add_import(imports, import_path);
206
211
  }
207
212
  }
@@ -2230,41 +2235,38 @@ var SimpleKeyIndex = JSON.stringify([{
2230
2235
  }]);
2231
2236
  var Fallback = Bind.StringLiteral("%fallback");
2232
2237
  var None = Bind.StringLiteral("%none");
2233
- function DispatcherGeneric(registry, args) {
2234
- return factory21.createTypeReferenceNode("Dispatcher", [
2235
- Bind.StringLiteral(registry),
2236
- factory21.createTupleTypeNode(args)
2237
- ]);
2238
+ function SymbolGeneric(symbol_name, generics, case_arg) {
2239
+ return factory21.createTypeReferenceNode(symbol_name, [...generics, case_arg]);
2238
2240
  }
2239
- function DispatcherMapIndex(registry, key, generics) {
2240
- const dispatcher = factory21.createTypeReferenceNode("Dispatcher", [Bind.StringLiteral(registry), ...generics.length === 0 ? [] : [factory21.createTupleTypeNode(generics)]]);
2241
+ function SymbolMapIndex(symbol_name, key, generics) {
2242
+ const symbol_type = factory21.createTypeReferenceNode(symbol_name, generics.length === 0 ? undefined : generics);
2241
2243
  if (key.kind === ts22.SyntaxKind.LiteralType) {
2242
- return factory21.createIndexedAccessTypeNode(dispatcher, key);
2244
+ return factory21.createIndexedAccessTypeNode(symbol_type, key);
2243
2245
  }
2244
- return factory21.createParenthesizedType(factory21.createConditionalTypeNode(key, factory21.createTypeOperatorNode(ts22.SyntaxKind.KeyOfKeyword, dispatcher), factory21.createIndexedAccessTypeNode(dispatcher, key), factory21.createTypeReferenceNode("Record", [
2246
+ return factory21.createParenthesizedType(factory21.createConditionalTypeNode(key, factory21.createTypeOperatorNode(ts22.SyntaxKind.KeyOfKeyword, symbol_type), factory21.createIndexedAccessTypeNode(symbol_type, key), factory21.createTypeReferenceNode("Record", [
2245
2247
  factory21.createKeywordTypeNode(ts22.SyntaxKind.StringKeyword),
2246
2248
  factory21.createKeywordTypeNode(ts22.SyntaxKind.UnknownKeyword)
2247
2249
  ])));
2248
2250
  }
2249
- function DispatcherMapSubIndex(registry, member_key, generics, sub_index) {
2250
- const dispatcher = factory21.createTypeReferenceNode("Dispatcher", [Bind.StringLiteral(registry), ...generics.length === 0 ? [] : [factory21.createTupleTypeNode(generics)]]);
2251
- const dispatcher_member = factory21.createIndexedAccessTypeNode(dispatcher, member_key);
2252
- let indexed_dispatcher = undefined;
2251
+ function SymbolMapSubIndex(symbol_name, member_key, generics, sub_index) {
2252
+ const symbol_type = factory21.createTypeReferenceNode(symbol_name, generics.length === 0 ? undefined : generics);
2253
+ const symbol_member = factory21.createIndexedAccessTypeNode(symbol_type, member_key);
2254
+ let indexed_symbol = undefined;
2253
2255
  for (let i = sub_index.length;i > 0; i--) {
2254
2256
  const key = Bind.StringLiteral(sub_index[i - 1]);
2255
- let index_stack = dispatcher_member;
2257
+ let index_stack = symbol_member;
2256
2258
  for (let j = 0;j < i - 1; j++) {
2257
2259
  index_stack = factory21.createIndexedAccessTypeNode(index_stack, Bind.StringLiteral(sub_index[j]));
2258
2260
  }
2259
- if (indexed_dispatcher === undefined) {
2260
- indexed_dispatcher = factory21.createIndexedAccessTypeNode(index_stack, key);
2261
+ if (indexed_symbol === undefined) {
2262
+ indexed_symbol = factory21.createIndexedAccessTypeNode(index_stack, key);
2261
2263
  }
2262
- indexed_dispatcher = factory21.createParenthesizedType(factory21.createConditionalTypeNode(key, factory21.createTypeOperatorNode(ts22.SyntaxKind.KeyOfKeyword, index_stack), indexed_dispatcher, factory21.createTypeReferenceNode("Record", [
2264
+ indexed_symbol = factory21.createParenthesizedType(factory21.createConditionalTypeNode(key, factory21.createTypeOperatorNode(ts22.SyntaxKind.KeyOfKeyword, index_stack), indexed_symbol, factory21.createTypeReferenceNode("Record", [
2263
2265
  factory21.createKeywordTypeNode(ts22.SyntaxKind.StringKeyword),
2264
2266
  factory21.createKeywordTypeNode(ts22.SyntaxKind.UnknownKeyword)
2265
2267
  ])));
2266
2268
  }
2267
- return factory21.createParenthesizedType(factory21.createConditionalTypeNode(member_key, factory21.createTypeOperatorNode(ts22.SyntaxKind.KeyOfKeyword, dispatcher), indexed_dispatcher, factory21.createTypeReferenceNode("Record", [
2269
+ return factory21.createParenthesizedType(factory21.createConditionalTypeNode(member_key, factory21.createTypeOperatorNode(ts22.SyntaxKind.KeyOfKeyword, symbol_type), indexed_symbol, factory21.createTypeReferenceNode("Record", [
2268
2270
  factory21.createKeywordTypeNode(ts22.SyntaxKind.StringKeyword),
2269
2271
  factory21.createKeywordTypeNode(ts22.SyntaxKind.UnknownKeyword)
2270
2272
  ])));
@@ -2276,34 +2278,39 @@ function mcdoc_dispatcher(type) {
2276
2278
  const indices = dispatcher.parallelIndices;
2277
2279
  return (args) => {
2278
2280
  DispatcherArgs(args);
2279
- const dispatcher_import = `::java::dispatcher::Dispatcher`;
2281
+ const info = args.dispatcher_info?.get(registry);
2282
+ if (!info) {
2283
+ throw new Error(`[mcdoc_dispatcher] Unknown dispatcher: ${registry}`);
2284
+ }
2285
+ const { symbol_name } = info;
2286
+ const import_path = `::java::dispatcher::${symbol_name}`;
2280
2287
  let result_type;
2281
2288
  let child_dispatcher;
2282
2289
  const generics = args.generic_types ?? [];
2283
2290
  if (indices.length === 1 && indices[0].kind === "dynamic" && typeof indices[0].accessor.at(-1) === "string") {
2284
2291
  if (args.root_type) {
2285
- result_type = DispatcherGeneric(registry, [...generics, Fallback]);
2292
+ result_type = SymbolGeneric(symbol_name, generics, Fallback);
2286
2293
  } else {
2287
2294
  child_dispatcher = [[indices[0].accessor.length - 1, indices[0].accessor.at(-1)]];
2288
- const indexed_type = DispatcherMapIndex(registry, factory21.createTypeReferenceNode("S"), generics);
2295
+ const indexed_type = SymbolMapIndex(symbol_name, factory21.createTypeReferenceNode("S"), generics);
2289
2296
  const properties = args.dispatcher_properties?.get(registry);
2290
2297
  if (properties?.supports_none) {
2291
- result_type = factory21.createParenthesizedType(factory21.createConditionalTypeNode(factory21.createTypeReferenceNode("S"), factory21.createKeywordTypeNode(ts22.SyntaxKind.UndefinedKeyword), DispatcherGeneric(registry, [...generics, None]), indexed_type));
2298
+ result_type = factory21.createParenthesizedType(factory21.createConditionalTypeNode(factory21.createTypeReferenceNode("S"), factory21.createKeywordTypeNode(ts22.SyntaxKind.UndefinedKeyword), SymbolGeneric(symbol_name, generics, None), indexed_type));
2292
2299
  } else {
2293
2300
  result_type = indexed_type;
2294
2301
  }
2295
2302
  }
2296
2303
  } else if (indices.length === 1 && indices[0].kind === "static") {
2297
2304
  if (indices[0].value === "%fallback") {
2298
- result_type = DispatcherGeneric(registry, [...generics, Fallback]);
2305
+ result_type = SymbolGeneric(symbol_name, generics, Fallback);
2299
2306
  } else {
2300
- result_type = DispatcherMapIndex(registry, Bind.StringLiteral(indices[0].value), generics);
2307
+ result_type = SymbolMapIndex(symbol_name, Bind.StringLiteral(indices[0].value), generics);
2301
2308
  }
2302
2309
  } else if (JSON.stringify(indices) === SimpleKeyIndex) {
2303
2310
  if (args.index_keys !== undefined) {
2304
- result_type = DispatcherMapSubIndex(registry, factory21.createTypeReferenceNode("Key"), generics, args.index_keys);
2311
+ result_type = SymbolMapSubIndex(symbol_name, factory21.createTypeReferenceNode("Key"), generics, args.index_keys);
2305
2312
  } else {
2306
- result_type = DispatcherMapIndex(registry, factory21.createTypeReferenceNode("Key"), generics);
2313
+ result_type = SymbolMapIndex(symbol_name, factory21.createTypeReferenceNode("Key"), generics);
2307
2314
  }
2308
2315
  } else {
2309
2316
  throw new Error(`[mcdoc_dispatcher] Unsupported dispatcher: ${dispatcher}`);
@@ -2311,8 +2318,8 @@ function mcdoc_dispatcher(type) {
2311
2318
  return {
2312
2319
  type: result_type,
2313
2320
  imports: {
2314
- ordered: [dispatcher_import],
2315
- check: new Map([[dispatcher_import, 0]])
2321
+ ordered: [import_path],
2322
+ check: new Map([[import_path, 0]])
2316
2323
  },
2317
2324
  ...add({ child_dispatcher })
2318
2325
  };
@@ -2411,9 +2418,11 @@ function get_type_handler(type) {
2411
2418
  import ts24 from "typescript";
2412
2419
  var { factory: factory23 } = ts24;
2413
2420
  var dispatcher_references = new Map;
2414
- function dispatcher_symbol(id, name, members, dispatcher_properties, module_map, symbols) {
2421
+ var dispatcher_symbol_paths = new Map;
2422
+ function dispatcher_symbol(id, name, members, dispatcher_properties, dispatcher_info, module_map, symbols) {
2415
2423
  let imports = undefined;
2416
2424
  let has_references = false;
2425
+ const self_import = new Set([`::java::dispatcher::Symbol${name}`]);
2417
2426
  const member_types = [];
2418
2427
  const map_properties = [];
2419
2428
  const member_type_refs = [];
@@ -2449,17 +2458,18 @@ function dispatcher_symbol(id, name, members, dispatcher_properties, module_map,
2449
2458
  name: unknown_type_name,
2450
2459
  dispatcher_symbol: add_reference,
2451
2460
  dispatcher_properties,
2461
+ dispatcher_info,
2452
2462
  module_map,
2453
2463
  symbols
2454
2464
  });
2455
2465
  if ("imports" in result) {
2456
- imports = merge_imports(imports, result.imports);
2466
+ imports = merge_imports(imports, result.imports, self_import);
2457
2467
  }
2458
2468
  fallback_type_name = factory23.createTypeReferenceNode(unknown_type_name, has_generics ? generic_names : undefined);
2459
2469
  if (ts24.isTypeAliasDeclaration(result.type)) {
2460
2470
  member_types.push(result.type);
2461
2471
  } else {
2462
- member_types.push(factory23.createTypeAliasDeclaration(undefined, unknown_type_name, undefined, result.type));
2472
+ member_types.push(factory23.createTypeAliasDeclaration([factory23.createModifier(ts24.SyntaxKind.ExportKeyword)], unknown_type_name, undefined, result.type));
2463
2473
  }
2464
2474
  }
2465
2475
  const has_none = "%none" in members;
@@ -2471,11 +2481,12 @@ function dispatcher_symbol(id, name, members, dispatcher_properties, module_map,
2471
2481
  name: none_type_name,
2472
2482
  dispatcher_symbol: add_reference,
2473
2483
  dispatcher_properties,
2484
+ dispatcher_info,
2474
2485
  module_map,
2475
2486
  symbols
2476
2487
  });
2477
2488
  if ("imports" in result) {
2478
- imports = merge_imports(imports, result.imports);
2489
+ imports = merge_imports(imports, result.imports, self_import);
2479
2490
  }
2480
2491
  dispatcher_properties.set(id, {
2481
2492
  supports_none: true
@@ -2498,11 +2509,12 @@ function dispatcher_symbol(id, name, members, dispatcher_properties, module_map,
2498
2509
  name: member_type_name,
2499
2510
  dispatcher_symbol: add_reference,
2500
2511
  dispatcher_properties,
2512
+ dispatcher_info,
2501
2513
  module_map,
2502
2514
  symbols
2503
2515
  });
2504
2516
  if ("imports" in result) {
2505
- imports = merge_imports(imports, result.imports);
2517
+ imports = merge_imports(imports, result.imports, self_import);
2506
2518
  }
2507
2519
  if (ts24.isTypeAliasDeclaration(result.type)) {
2508
2520
  member_types.push(result.type);
@@ -2543,6 +2555,23 @@ var DispatcherSymbol = dispatcher_symbol;
2543
2555
  import { TaggableResourceLocationCategories } from "@spyglassmc/core";
2544
2556
  import ts25 from "typescript";
2545
2557
  var { factory: factory24 } = ts25;
2558
+ function export_dispatchers(paths) {
2559
+ const exports = [];
2560
+ for (const [path, info] of paths) {
2561
+ const relative_path = "./" + path.split("::").slice(2).join("/");
2562
+ const export_specifiers = [
2563
+ factory24.createExportSpecifier(false, undefined, info.symbol_name)
2564
+ ];
2565
+ if (info.has_fallback_type) {
2566
+ export_specifiers.push(factory24.createExportSpecifier(false, undefined, `${info.base_name}FallbackType`));
2567
+ }
2568
+ exports.push(factory24.createExportDeclaration(undefined, false, factory24.createNamedExports(export_specifiers), factory24.createStringLiteral(relative_path, true)));
2569
+ }
2570
+ return {
2571
+ exports,
2572
+ paths: new Set
2573
+ };
2574
+ }
2546
2575
  function export_registry(resolved_registries) {
2547
2576
  let imports;
2548
2577
  imports = add_import(imports, "sandstone::Set");
@@ -2562,68 +2591,6 @@ function export_registry(resolved_registries) {
2562
2591
  ...add({ imports })
2563
2592
  };
2564
2593
  }
2565
- function export_dispatcher(resolved_dispatchers) {
2566
- let imports;
2567
- const required_args_properties = [];
2568
- for (const [dispatcher_id, { import_path, generic_count }] of resolved_dispatchers) {
2569
- imports = add_import(imports, import_path);
2570
- const tuple_elements = [];
2571
- for (let i = 0;i < generic_count; i++) {
2572
- tuple_elements.push(factory24.createKeywordTypeNode(ts25.SyntaxKind.UnknownKeyword));
2573
- }
2574
- required_args_properties.push(factory24.createPropertySignature(undefined, factory24.createStringLiteral(dispatcher_id, true), undefined, factory24.createTupleTypeNode(tuple_elements)));
2575
- }
2576
- const required_args_type = factory24.createTypeAliasDeclaration(undefined, "DispatcherRequiredArgs", undefined, factory24.createTypeLiteralNode(required_args_properties));
2577
- const default_args_type = factory24.createTypeAliasDeclaration(undefined, "DefaultArgs", [factory24.createTypeParameterDeclaration(undefined, "R", factory24.createTypeOperatorNode(ts25.SyntaxKind.KeyOfKeyword, factory24.createTypeReferenceNode("DispatcherRequiredArgs")))], factory24.createConditionalTypeNode(factory24.createIndexedAccessTypeNode(factory24.createIndexedAccessTypeNode(factory24.createTypeReferenceNode("DispatcherRequiredArgs"), factory24.createTypeReferenceNode("R")), Bind.StringLiteral("length")), Bind.NumericLiteral(0), factory24.createTupleTypeNode([]), factory24.createKeywordTypeNode(ts25.SyntaxKind.NeverKeyword)));
2578
- const dispatchers_by_count = new Map;
2579
- for (const [id, dispatcher] of resolved_dispatchers) {
2580
- const count = dispatcher.generic_count;
2581
- if (!dispatchers_by_count.has(count)) {
2582
- dispatchers_by_count.set(count, []);
2583
- }
2584
- dispatchers_by_count.get(count).push([id, dispatcher]);
2585
- }
2586
- let apply_dispatcher_body = factory24.createKeywordTypeNode(ts25.SyntaxKind.NeverKeyword);
2587
- const sorted_counts = [...dispatchers_by_count.keys()].sort((a, b) => b - a);
2588
- for (const count of sorted_counts) {
2589
- const dispatchers = dispatchers_by_count.get(count);
2590
- for (const [dispatcher_id, { symbol_name }] of dispatchers) {
2591
- const param_names = ["A", "B", "C", "D"].slice(0, count);
2592
- const infer_params = param_names.map((name) => factory24.createInferTypeNode(factory24.createTypeParameterDeclaration(undefined, name)));
2593
- const type_refs = param_names.map((name) => factory24.createTypeReferenceNode(name));
2594
- const map_case = factory24.createConditionalTypeNode(factory24.createTypeReferenceNode("Args"), factory24.createTupleTypeNode(infer_params), factory24.createTypeReferenceNode(symbol_name, [...type_refs, Bind.StringLiteral("map")]), factory24.createKeywordTypeNode(ts25.SyntaxKind.NeverKeyword));
2595
- const none_case = factory24.createConditionalTypeNode(factory24.createTypeReferenceNode("Args"), factory24.createTupleTypeNode([...infer_params, Bind.StringLiteral("%none")]), factory24.createTypeReferenceNode(symbol_name, [...type_refs, Bind.StringLiteral("%none")]), map_case);
2596
- const fallback_case = factory24.createConditionalTypeNode(factory24.createTypeReferenceNode("Args"), factory24.createTupleTypeNode([...infer_params, Bind.StringLiteral("%fallback")]), factory24.createTypeReferenceNode(symbol_name, [...type_refs, Bind.StringLiteral("%fallback")]), none_case);
2597
- apply_dispatcher_body = factory24.createConditionalTypeNode(factory24.createTypeReferenceNode("R"), Bind.StringLiteral(dispatcher_id), fallback_case, apply_dispatcher_body);
2598
- }
2599
- }
2600
- const apply_dispatcher_type = factory24.createTypeAliasDeclaration(undefined, "ApplyDispatcher", [
2601
- factory24.createTypeParameterDeclaration(undefined, "R", factory24.createTypeOperatorNode(ts25.SyntaxKind.KeyOfKeyword, factory24.createTypeReferenceNode("DispatcherRequiredArgs"))),
2602
- factory24.createTypeParameterDeclaration(undefined, "Args", factory24.createTypeReferenceNode("Array", [
2603
- factory24.createKeywordTypeNode(ts25.SyntaxKind.UnknownKeyword)
2604
- ]))
2605
- ], apply_dispatcher_body);
2606
- const args_constraint = factory24.createTypeReferenceNode("Array", [
2607
- factory24.createKeywordTypeNode(ts25.SyntaxKind.UnknownKeyword)
2608
- ]);
2609
- const dispatcher_type = factory24.createTypeAliasDeclaration([factory24.createToken(ts25.SyntaxKind.ExportKeyword)], "Dispatcher", [
2610
- factory24.createTypeParameterDeclaration(undefined, "R", factory24.createTypeOperatorNode(ts25.SyntaxKind.KeyOfKeyword, factory24.createTypeReferenceNode("DispatcherRequiredArgs"))),
2611
- factory24.createTypeParameterDeclaration(undefined, "Args", args_constraint, factory24.createTypeReferenceNode("DefaultArgs", [factory24.createTypeReferenceNode("R")]))
2612
- ], factory24.createTypeReferenceNode("ApplyDispatcher", [
2613
- factory24.createTypeReferenceNode("R"),
2614
- factory24.createTypeReferenceNode("Args")
2615
- ]));
2616
- return {
2617
- exports: [
2618
- required_args_type,
2619
- default_args_type,
2620
- apply_dispatcher_type,
2621
- dispatcher_type
2622
- ],
2623
- paths: new Set,
2624
- ...add({ imports })
2625
- };
2626
- }
2627
2594
 
2628
2595
  // src/typegen/index.ts
2629
2596
  var { factory: factory25 } = ts26;
@@ -2633,6 +2600,7 @@ class TypesGenerator {
2633
2600
  dispatcher_properties = new Map;
2634
2601
  resolved_symbols = new Map;
2635
2602
  resolved_dispatchers = new Map;
2603
+ dispatcher_info = new Map;
2636
2604
  constructor() {}
2637
2605
  resolve_types(symbols, translation_keys) {
2638
2606
  console.log("registries");
@@ -2640,18 +2608,42 @@ class TypesGenerator {
2640
2608
  const registry_exports = export_registry(this.resolved_registries);
2641
2609
  this.resolved_symbols.set("::java::registry", registry_exports);
2642
2610
  const dispatchers = symbols.getVisibleSymbols("mcdoc/dispatcher");
2643
- for (const id of Object.keys(dispatchers)) {
2644
- if ("%none" in dispatchers[id].members) {
2645
- this.dispatcher_properties.set(id, { supports_none: true });
2646
- }
2647
- }
2611
+ console.log("dispatcher info");
2612
+ this.precompute_dispatcher_info(dispatchers);
2648
2613
  console.log("modules");
2649
2614
  const module_map = symbols.getVisibleSymbols("mcdoc");
2650
2615
  this.resolve_module_symbols(module_map, symbols);
2651
2616
  console.log("dispatchers");
2652
2617
  this.resolve_dispatcher_symbols(dispatchers, module_map, symbols);
2653
- const dispatcher_exports = export_dispatcher(this.resolved_dispatchers);
2654
- this.resolved_symbols.set("mcdoc::dispatcher", dispatcher_exports);
2618
+ const dispatcher_exports = export_dispatchers(dispatcher_symbol_paths);
2619
+ this.resolved_symbols.set("::java::dispatcher", dispatcher_exports);
2620
+ }
2621
+ precompute_dispatcher_info(dispatchers) {
2622
+ for (const id of Object.keys(dispatchers)) {
2623
+ const { members } = dispatchers[id];
2624
+ if (members === undefined) {
2625
+ continue;
2626
+ }
2627
+ if ("%none" in members) {
2628
+ this.dispatcher_properties.set(id, { supports_none: true });
2629
+ }
2630
+ const [namespace, _name] = id.split(":");
2631
+ const name = pascal_case(`${namespace === "mcdoc" ? "mcdoc_" : ""}${_name}`);
2632
+ const symbol_name = `Symbol${name}`;
2633
+ const first_member_key = Object.keys(members).find((k) => !k.startsWith("%"));
2634
+ let generic_count = 0;
2635
+ if (first_member_key) {
2636
+ const first_type = members[first_member_key].data.typeDef;
2637
+ if (first_type.kind === "template") {
2638
+ generic_count = first_type.typeParams.length;
2639
+ }
2640
+ }
2641
+ this.dispatcher_info.set(id, {
2642
+ symbol_name,
2643
+ generic_count,
2644
+ has_fallback_type: "%unknown" in members
2645
+ });
2646
+ }
2655
2647
  }
2656
2648
  resolve_registry_symbols(registries, translation_keys) {
2657
2649
  for (const registry_name of [...AllCategories]) {
@@ -2710,6 +2702,7 @@ class TypesGenerator {
2710
2702
  }
2711
2703
  const resolved_member = get_type_handler(type)(type)({
2712
2704
  dispatcher_properties: this.dispatcher_properties,
2705
+ dispatcher_info: this.dispatcher_info,
2713
2706
  root_type: true,
2714
2707
  name,
2715
2708
  module_path,
@@ -2747,7 +2740,7 @@ class TypesGenerator {
2747
2740
  }
2748
2741
  const [namespace, _name] = id.split(":");
2749
2742
  const name = pascal_case(`${namespace === "mcdoc" ? "mcdoc_" : ""}${_name}`);
2750
- const { types, imports, references, generic_count } = DispatcherSymbol(id, name, members, this.dispatcher_properties, module_map, symbols);
2743
+ const { types, imports, references, generic_count } = DispatcherSymbol(id, name, members, this.dispatcher_properties, this.dispatcher_info, module_map, symbols);
2751
2744
  let in_module = false;
2752
2745
  const symbol_path = (() => {
2753
2746
  if (namespace === "mcdoc") {
@@ -2762,6 +2755,12 @@ class TypesGenerator {
2762
2755
  }
2763
2756
  return `::java::_dispatcher::${_name}`;
2764
2757
  })();
2758
+ const info = this.dispatcher_info.get(id);
2759
+ dispatcher_symbol_paths.set(symbol_path, {
2760
+ symbol_name: `Symbol${name}`,
2761
+ base_name: name,
2762
+ has_fallback_type: info.has_fallback_type
2763
+ });
2765
2764
  const dispatcher_type_name = `Symbol${name}`;
2766
2765
  this.resolved_dispatchers.set(id, {
2767
2766
  import_path: `${symbol_path}::${dispatcher_type_name}`,
@@ -2772,18 +2771,35 @@ class TypesGenerator {
2772
2771
  if (in_module && this.resolved_symbols.has(symbol_path)) {
2773
2772
  const mod = this.resolved_symbols.get(symbol_path);
2774
2773
  mod.exports.push(...types);
2774
+ let module_has_imports = false;
2775
+ if (mod.imports !== undefined) {
2776
+ module_has_imports = true;
2777
+ }
2775
2778
  if (imports !== undefined) {
2776
- for (const path of imports.ordered) {
2777
- if (!mod.paths.has(path)) {
2778
- mod.imports = add_import(mod.imports, path);
2779
- }
2780
- }
2779
+ mod.imports = merge_imports(mod.imports, imports);
2780
+ }
2781
+ if (mod.imports !== undefined) {
2782
+ const same_module_pattern = new RegExp(`^${symbol_path}::[^:]+$`);
2783
+ mod.imports.ordered = mod.imports.ordered.filter((imp) => imp !== `::java::dispatcher::Symbol${name}` && !same_module_pattern.test(imp));
2781
2784
  }
2782
2785
  } else {
2786
+ let filtered_imports = imports;
2787
+ if (imports !== undefined) {
2788
+ const same_module_pattern = new RegExp(`^${symbol_path}::[^:]+$`);
2789
+ const filtered_ordered = imports.ordered.filter((imp) => imp !== `::java::dispatcher::Symbol${name}` && !same_module_pattern.test(imp));
2790
+ if (filtered_ordered.length > 0) {
2791
+ filtered_imports = {
2792
+ ordered: filtered_ordered,
2793
+ check: new Map(filtered_ordered.map((imp, i) => [imp, i]))
2794
+ };
2795
+ } else {
2796
+ filtered_imports = undefined;
2797
+ }
2798
+ }
2783
2799
  this.resolved_symbols.set(symbol_path, {
2784
2800
  exports: types,
2785
2801
  paths: new Set2,
2786
- ...add({ imports })
2802
+ ...add({ imports: filtered_imports })
2787
2803
  });
2788
2804
  }
2789
2805
  }
@@ -2819,7 +2835,7 @@ var eslint = new ESLint({
2819
2835
  "@stylistic": stylistic
2820
2836
  },
2821
2837
  rules: {
2822
- "@stylistic/indent": ["error", 4],
2838
+ "@stylistic/indent": ["error", 2],
2823
2839
  "@stylistic/quotes": ["error", "single"],
2824
2840
  "@stylistic/semi": ["error", "never"],
2825
2841
  "@stylistic/member-delimiter-style": ["error", {
@@ -2883,14 +2899,14 @@ function handle_imports(imports) {
2883
2899
  if (path.length === 0) {
2884
2900
  throw new Error(`[mcdoc_import] Import path has no module prefix: "${import_path}"`);
2885
2901
  } else if (path[1] === "java") {
2886
- file = `sandstone/arguments/generated/${path.slice(2).join("/")}.js`;
2902
+ file = `sandstone/arguments/generated/${path.slice(2).join("/")}.ts`;
2887
2903
  } else if (path[0] === "sandstone") {
2888
2904
  if (path.length === 1) {
2889
2905
  file = "sandstone";
2890
2906
  } else if (path[1] === "arguments" && path.length === 2) {
2891
2907
  file = "sandstone/arguments";
2892
2908
  } else {
2893
- file = `${path.join("/")}.js`;
2909
+ file = `${path.join("/")}.ts`;
2894
2910
  }
2895
2911
  } else {
2896
2912
  throw new Error(`[mcdoc_import] Unsupported import location "${path[0]}" in "${import_path}"`);
@@ -2922,7 +2938,7 @@ function handle_imports(imports) {
2922
2938
  // src/index.ts
2923
2939
  var writeFile2 = promisify(fs2.writeFile);
2924
2940
  var mkdir2 = promisify(fs2.mkdir);
2925
- var cache_root = join(dirname(fileURLToPath(import.meta.url)), "cache");
2941
+ var cache_root = join(process.cwd(), "cache");
2926
2942
  function registerAttributes(meta, release) {
2927
2943
  mcdoc5.runtime.registerAttribute(meta, "since", mcdoc5.runtime.attribute.validator.string, {
2928
2944
  filterElement: () => false
@@ -1,6 +1,15 @@
1
1
  import ts from 'typescript';
2
2
  import { type NonEmptyList } from './mcdoc/utils';
3
- import type { ResolvedDispatcher, ResolvedRegistry } from '.';
3
+ import type { ResolvedRegistry, ResolvedSymbol } from '.';
4
+ /**
5
+ * Generates named export statements for all dispatcher symbols.
6
+ * Exports `SymbolName` and optionally `NameFallbackType` when %unknown is present.
7
+ */
8
+ export declare function export_dispatchers(paths: Map<string, {
9
+ symbol_name: string;
10
+ base_name: string;
11
+ has_fallback_type: boolean;
12
+ }>): ResolvedSymbol;
4
13
  export declare function export_registry(resolved_registries: Map<string, ResolvedRegistry>): {
5
14
  readonly exports: (ts.TypeAliasDeclaration | ts.VariableStatement)[];
6
15
  readonly paths: Set<string>;
@@ -12,28 +21,3 @@ export declare function export_registry(resolved_registries: Map<string, Resolve
12
21
  readonly exports: (ts.TypeAliasDeclaration | ts.VariableStatement)[];
13
22
  readonly paths: Set<string>;
14
23
  };
15
- /**
16
- * Generates a `Dispatcher` type system with type-safe generic enforcement.
17
- *
18
- * Example output:
19
- * ```ts
20
- * interface DispatcherRequiredArgs {
21
- * 'minecraft:data_component': []
22
- * 'minecraft:entity_effect': [unknown]
23
- * }
24
- * type DefaultArgs<R extends keyof DispatcherRequiredArgs> = ...
25
- * type ApplyDispatcher<R, Args> = ...
26
- * export type Dispatcher<R, Args> = ApplyDispatcher<R, Args>
27
- * ```
28
- */
29
- export declare function export_dispatcher(resolved_dispatchers: Map<string, ResolvedDispatcher>): {
30
- readonly exports: ts.TypeAliasDeclaration[];
31
- readonly paths: Set<string>;
32
- } | {
33
- readonly imports: {
34
- readonly ordered: NonEmptyList<string>;
35
- readonly check: Map<string, number>;
36
- };
37
- readonly exports: ts.TypeAliasDeclaration[];
38
- readonly paths: Set<string>;
39
- };
@@ -23,6 +23,18 @@ export type ResolvedDispatcher = {
23
23
  */
24
24
  symbol_name: string;
25
25
  };
26
+ /**
27
+ * Pre-computed dispatcher info for use during type resolution.
28
+ * Maps dispatcher ID (e.g., 'minecraft:entity_effect') to symbol info.
29
+ */
30
+ export type DispatcherInfo = {
31
+ /** The symbol type name (e.g., "SymbolEntityEffect") */
32
+ symbol_name: string;
33
+ /** Number of generic parameters (excluding CASE) */
34
+ generic_count: number;
35
+ /** Whether this dispatcher has a %unknown member (exports FallbackType) */
36
+ has_fallback_type: boolean;
37
+ };
26
38
  export declare class TypesGenerator {
27
39
  readonly resolved_registries: Map<string, ResolvedRegistry>;
28
40
  readonly dispatcher_properties: Map<string, {
@@ -30,8 +42,19 @@ export declare class TypesGenerator {
30
42
  }>;
31
43
  readonly resolved_symbols: Map<string, ResolvedSymbol>;
32
44
  readonly resolved_dispatchers: Map<string, ResolvedDispatcher>;
45
+ /** Pre-computed dispatcher info for use during type resolution */
46
+ readonly dispatcher_info: Map<string, DispatcherInfo>;
33
47
  constructor();
34
48
  resolve_types(symbols: SymbolUtil, translation_keys: string[]): void;
49
+ /**
50
+ * Pre-computes dispatcher info (symbol names and import paths) before module resolution.
51
+ * This allows modules to directly reference SymbolX types instead of the central Dispatcher type.
52
+ *
53
+ * Also populates dispatcher_properties with supports_none info.
54
+ *
55
+ * Note: All dispatchers are placed in _dispatcher/ (or _builtin/ for mcdoc namespace) for predictability.
56
+ */
57
+ private precompute_dispatcher_info;
35
58
  private resolve_registry_symbols;
36
59
  private resolve_module_symbols;
37
60
  private resolve_dispatcher_symbols;
@@ -8,11 +8,11 @@ import type { NonEmptyList } from '..';
8
8
  * - `registry`: The dispatcher identifier (e.g., `minecraft:entity_effect`)
9
9
  * - `parallelIndices`: How to look up into the dispatcher (static or dynamic)
10
10
  *
11
- * The generated type references the central Dispatcher type:
12
- * - Static index: `Dispatcher<'minecraft:entity_effect'>['specific_key']`
13
- * - Dynamic index: `Dispatcher<'minecraft:entity_effect'>[Key]`
14
- * - Fallback: `Dispatcher<'minecraft:entity_effect', ['%fallback']>`
15
- * - None: `Dispatcher<'minecraft:entity_effect', ['%none']>`
11
+ * The generated type directly references the Symbol type:
12
+ * - Static index: `SymbolEntityEffect['specific_key']`
13
+ * - Dynamic index: `SymbolEntityEffect[Key]`
14
+ * - Fallback: `SymbolEntityEffect<'%fallback'>`
15
+ * - None: `SymbolEntityEffect<'%none'>`
16
16
  */
17
17
  declare function mcdoc_dispatcher(type: mcdoc.McdocType): (args: Record<string, unknown>) => {
18
18
  readonly type: ts.TypeNode;
@@ -1,6 +1,7 @@
1
1
  import ts from 'typescript';
2
2
  import type { SymbolMap, SymbolUtil } from '@spyglassmc/core';
3
3
  import { type NonEmptyList } from '.';
4
+ import type { DispatcherInfo } from '..';
4
5
  export type DispatcherReferenceCounter = {
5
6
  /**
6
7
  * Map<path: string, location_counts_index: number>
@@ -9,6 +10,19 @@ export type DispatcherReferenceCounter = {
9
10
  location_counts: [path: string, count: number][];
10
11
  };
11
12
  export declare const dispatcher_references: Map<string, DispatcherReferenceCounter>;
13
+ export type DispatcherExportInfo = {
14
+ /** The symbol name without the path (e.g., "SymbolEntityEffect") */
15
+ symbol_name: string;
16
+ /** The base name without Symbol prefix (e.g., "EntityEffect") */
17
+ base_name: string;
18
+ /** Whether this dispatcher exports a FallbackType */
19
+ has_fallback_type: boolean;
20
+ };
21
+ /**
22
+ * Global map of dispatcher symbol paths to their export info.
23
+ * Populated during `resolve_dispatcher_symbols`.
24
+ */
25
+ export declare const dispatcher_symbol_paths: Map<string, DispatcherExportInfo>;
12
26
  type DispatcherSymbolResult = {
13
27
  /**
14
28
  * The main exported type `SymbolName<CASE>` and all supporting type aliases (member types, map, keys, fallback, unknown)
@@ -53,6 +67,6 @@ type DispatcherSymbolResult = {
53
67
  */
54
68
  export declare function dispatcher_symbol(id: string, name: string, members: SymbolMap, dispatcher_properties: Map<string, {
55
69
  supports_none?: true;
56
- }>, module_map: SymbolMap, symbols: SymbolUtil): DispatcherSymbolResult;
70
+ }>, dispatcher_info: Map<string, DispatcherInfo>, module_map: SymbolMap, symbols: SymbolUtil): DispatcherSymbolResult;
57
71
  export declare const DispatcherSymbol: typeof dispatcher_symbol;
58
72
  export {};
@@ -11,7 +11,7 @@ export type NonEmptyList<T> = T[] & {
11
11
  */
12
12
  export declare function is_valid_registry(symbols: SymbolUtil | undefined, registry_id: string): boolean;
13
13
  export declare function add_import(imports: TypeHandlerResult['imports'], add_import: string): NonNullable<TypeHandlerResult['imports']>;
14
- export declare function merge_imports(imports: TypeHandlerResult['imports'], new_imports: NonNullable<TypeHandlerResult['imports']>): NonNullable<TypeHandlerResult['imports']>;
14
+ export declare function merge_imports(imports: TypeHandlerResult['imports'], new_imports: NonNullable<TypeHandlerResult['imports']>, filter?: Set<string>): NonNullable<TypeHandlerResult['imports']>;
15
15
  type GetConstructorArgs<T> = T extends new (...args: infer U) => any ? U : never;
16
16
  export declare class Set<T> extends global.Set<T> {
17
17
  constructor(...args: GetConstructorArgs<typeof global.Set<T>>);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sandstone-mc/mcdoc-ts-generator",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Generate TypeScript types from Minecraft mcdoc definitions",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,7 +19,8 @@
19
19
  "dist"
20
20
  ],
21
21
  "scripts": {
22
- "compile": "bun run ./src/cli.ts",
22
+ "compile": "rm -rf types/ && bun run ./src/cli.ts",
23
+ "typecheck": "bun tsc --emitDeclarationOnly false --noEmit",
23
24
  "build": "bun run build:bundle && bun run build:types",
24
25
  "build:bundle": "bun build ./src/cli.ts --outfile ./dist/cli.js --target node --format esm --packages external && bun build ./src/index.ts --outfile ./dist/index.js --target node --format esm --packages external",
25
26
  "build:types": "bun tsc --emitDeclarationOnly --declaration --outDir ./dist",