@defold-typescript/types 0.39.0 → 0.40.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/api-availability.json +117 -0
- package/api-signatures.json +262 -237
- package/examples/translations.json +81 -81
- package/generated/b2d_shape.d.ts +23 -1
- package/generated/buffer.d.ts +18 -15
- package/generated/camera.d.ts +2 -2
- package/generated/collectionfactory.d.ts +12 -10
- package/generated/collectionproxy.d.ts +1 -1
- package/generated/compute.d.ts +1 -1
- package/generated/go.d.ts +9 -5
- package/generated/graphics.d.ts +52 -0
- package/generated/gui.d.ts +53 -32
- package/generated/html5.d.ts +3 -1
- package/generated/http.d.ts +4 -2
- package/generated/image.d.ts +24 -15
- package/generated/kinds/gui-script.d.ts +1 -0
- package/generated/kinds/render-script.d.ts +1 -0
- package/generated/kinds/script.d.ts +1 -0
- package/generated/material.d.ts +1 -1
- package/generated/model.d.ts +32 -3
- package/generated/physics.d.ts +13 -11
- package/generated/profiler.d.ts +1 -1
- package/generated/render.d.ts +72 -15
- package/generated/resource.d.ts +100 -69
- package/generated/socket.d.ts +23 -13
- package/generated/sound.d.ts +3 -3
- package/generated/sprite.d.ts +9 -1
- package/generated/sys.d.ts +34 -23
- package/generated/versions/defold-1.12.4/buffer.d.ts +18 -15
- package/generated/versions/defold-1.12.4/camera.d.ts +2 -2
- package/generated/versions/defold-1.12.4/collectionfactory.d.ts +12 -10
- package/generated/versions/defold-1.12.4/go.d.ts +9 -5
- package/generated/versions/defold-1.12.4/gui.d.ts +47 -31
- package/generated/versions/defold-1.12.4/html5.d.ts +3 -1
- package/generated/versions/defold-1.12.4/http.d.ts +4 -2
- package/generated/versions/defold-1.12.4/image.d.ts +24 -15
- package/generated/versions/defold-1.12.4/index.d.ts +1 -0
- package/generated/versions/defold-1.12.4/model.d.ts +32 -3
- package/generated/versions/defold-1.12.4/physics.d.ts +13 -11
- package/generated/versions/defold-1.12.4/profiler.d.ts +1 -1
- package/generated/versions/defold-1.12.4/render.d.ts +72 -15
- package/generated/versions/defold-1.12.4/resource.d.ts +100 -69
- package/generated/versions/defold-1.12.4/socket.d.ts +23 -13
- package/generated/versions/defold-1.12.4/sound.d.ts +3 -3
- package/generated/versions/defold-1.12.4/sprite.d.ts +9 -1
- package/generated/versions/defold-1.12.4/sys.d.ts +34 -23
- package/generated/versions/defold-1.12.4/vmath.d.ts +5 -5
- package/generated/versions/defold-1.13.0/b2d_shape.d.ts +23 -1
- package/generated/versions/defold-1.13.0/buffer.d.ts +18 -15
- package/generated/versions/defold-1.13.0/camera.d.ts +2 -2
- package/generated/versions/defold-1.13.0/collectionfactory.d.ts +12 -10
- package/generated/versions/defold-1.13.0/compute.d.ts +1 -1
- package/generated/versions/defold-1.13.0/go.d.ts +9 -5
- package/generated/versions/defold-1.13.0/graphics.d.ts +48 -0
- package/generated/versions/defold-1.13.0/gui.d.ts +47 -31
- package/generated/versions/defold-1.13.0/html5.d.ts +3 -1
- package/generated/versions/defold-1.13.0/http.d.ts +4 -2
- package/generated/versions/defold-1.13.0/image.d.ts +24 -15
- package/generated/versions/defold-1.13.0/index.d.ts +1 -0
- package/generated/versions/defold-1.13.0/material.d.ts +1 -1
- package/generated/versions/defold-1.13.0/model.d.ts +32 -3
- package/generated/versions/defold-1.13.0/physics.d.ts +13 -11
- package/generated/versions/defold-1.13.0/profiler.d.ts +1 -1
- package/generated/versions/defold-1.13.0/render.d.ts +72 -15
- package/generated/versions/defold-1.13.0/resource.d.ts +100 -69
- package/generated/versions/defold-1.13.0/socket.d.ts +23 -13
- package/generated/versions/defold-1.13.0/sound.d.ts +3 -3
- package/generated/versions/defold-1.13.0/sprite.d.ts +9 -1
- package/generated/versions/defold-1.13.0/sys.d.ts +34 -23
- package/generated/versions/defold-1.13.0/vmath.d.ts +5 -5
- package/generated/vmath.d.ts +5 -5
- package/index.d.ts +1 -0
- package/package.json +1 -1
- package/scripts/generate-api-signatures.ts +2 -1
- package/scripts/prose-constants.ts +96 -0
- package/scripts/regen.ts +6 -2
- package/src/component-properties.d.ts +33 -0
- package/src/core-types.ts +4 -2
- package/src/custom-messages.d.ts +4 -3
- package/src/doc-comment.ts +35 -12
- package/src/emit-dts.ts +457 -47
- package/src/engine-globals.d.ts +2 -2
- package/src/go-overloads.d.ts +11 -10
- package/src/index.ts +4 -0
- package/src/param-correction-provenance.ts +67 -0
- package/src/render-overloads.d.ts +15 -0
- package/src/script-api.ts +3 -1
package/src/emit-dts.ts
CHANGED
|
@@ -131,6 +131,8 @@ export const ARBITRARY_TABLE_SLOTS = new Set([
|
|
|
131
131
|
// names; bare `on_message`'s receive `message` is the pre-hashed, user-routed
|
|
132
132
|
// payload (mitigated for authored code by isMessage/onMessage). No static shape
|
|
133
133
|
// exists, so `Record<string | number, unknown>` is faithful, not a loss.
|
|
134
|
+
// collectionfactory.create curates the instance-id layer of `properties` as a
|
|
135
|
+
// mapping; each per-instance property table stays that faithful `Record`.
|
|
134
136
|
"factory.create",
|
|
135
137
|
"collectionfactory.create",
|
|
136
138
|
"on_message",
|
|
@@ -254,6 +256,16 @@ export const RETURN_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
|
|
|
254
256
|
// ref-doc. Upstream adding `nil` to `types` hands the case to the top-level
|
|
255
257
|
// `nil` projection in `mapSlotUnion` and reds the entry, which is then deleted
|
|
256
258
|
// rather than re-pinned.
|
|
259
|
+
//
|
|
260
|
+
// The same table holds a `function` return whose prose documents the returned
|
|
261
|
+
// function's contract, which the bare token cannot carry: `socket.protect`'s
|
|
262
|
+
// safe function "returns nil followed by an error message", and
|
|
263
|
+
// `socket.newtry`'s try function returns its first argument when it is truthy
|
|
264
|
+
// and throws otherwise, the LuaSocket contract its example relies on to unwrap
|
|
265
|
+
// `socket.connect`'s `client, err` pair. The try function takes that pair
|
|
266
|
+
// destructured into separate arguments; a boxed `LuaMultiReturn` is rejected
|
|
267
|
+
// because TSTL passes it as one table, which is always truthy. `reason` quotes
|
|
268
|
+
// the slot prose the provenance test searches for, as for the `nil` entries.
|
|
257
269
|
export interface ReturnTypeCorrection {
|
|
258
270
|
readonly ts: string;
|
|
259
271
|
readonly upstream: readonly string[];
|
|
@@ -270,8 +282,193 @@ export const RETURN_TYPE_CORRECTIONS: ReadonlyMap<string, ReturnTypeCorrection>
|
|
|
270
282
|
reason: "the body if successful. Otherwise nil.",
|
|
271
283
|
},
|
|
272
284
|
],
|
|
285
|
+
[
|
|
286
|
+
"socket.protect",
|
|
287
|
+
{
|
|
288
|
+
ts: "(...args: unknown[]) => LuaMultiReturn<[unknown, string | undefined]>",
|
|
289
|
+
upstream: ["function(function())"],
|
|
290
|
+
reason: "returns nil followed by an error message.",
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
[
|
|
294
|
+
"socket.newtry",
|
|
295
|
+
{
|
|
296
|
+
ts: "<T>(value: T | undefined, ...rest: T extends { readonly __tstlMultiReturn: unknown } ? [boxedMultiReturn: never] : unknown[]) => T",
|
|
297
|
+
upstream: ["function"],
|
|
298
|
+
reason: "the customized try function.",
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
]);
|
|
302
|
+
|
|
303
|
+
// A parameter whose declared `types` reject what the same element's prose, or
|
|
304
|
+
// an upstream example, demonstrably passes — a render target by resource name,
|
|
305
|
+
// a texture unit, a Lua sequence where `table` is declared. Admission rule: only
|
|
306
|
+
// a slot upstream's own prose or example contradicts; a slot that is merely
|
|
307
|
+
// imprecise is not a candidate. Every such slot is too narrow, never wrong, so
|
|
308
|
+
// `adds` is appended to the mapped union rather than replacing it: the URL
|
|
309
|
+
// address alias, array curation and constant mapping stay live, and optionality
|
|
310
|
+
// stays with `isDocOptional`. Each entry records the upstream tokens it
|
|
311
|
+
// contradicts and `param-correction-provenance.test.ts` pins them against every
|
|
312
|
+
// retained vendored ref-doc. Once no retained target still declares those
|
|
313
|
+
// tokens the entry is deleted, never re-pinned.
|
|
314
|
+
export interface ParamTypeCorrection {
|
|
315
|
+
readonly adds: string;
|
|
316
|
+
readonly upstream: readonly string[];
|
|
317
|
+
readonly reason: string;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Keyed `<element>:param:<slot>` like OPTIONAL_SLOT_CORRECTIONS, so a
|
|
321
|
+
// same-named slot on another element is untouched.
|
|
322
|
+
export const PARAM_TYPE_CORRECTIONS: ReadonlyMap<string, ParamTypeCorrection> = new Map([
|
|
323
|
+
[
|
|
324
|
+
"render.set_render_target:param:render_target",
|
|
325
|
+
{
|
|
326
|
+
adds: "string | Hash | typeof render.RENDER_TARGET_DEFAULT",
|
|
327
|
+
upstream: ["render_target"],
|
|
328
|
+
reason:
|
|
329
|
+
"\"render.RENDER_TARGET_DEFAULT to set the default render target\"; example `render.set_render_target('my_rt_resource')`",
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
[
|
|
333
|
+
"render.get_render_target_width:param:render_target",
|
|
334
|
+
{
|
|
335
|
+
adds: "string | Hash",
|
|
336
|
+
upstream: ["render_target"],
|
|
337
|
+
reason:
|
|
338
|
+
"example `render.get_render_target_width('my_rt_resource', graphics.BUFFER_TYPE_COLOR0_BIT)`",
|
|
339
|
+
},
|
|
340
|
+
],
|
|
341
|
+
[
|
|
342
|
+
"render.get_render_target_height:param:render_target",
|
|
343
|
+
{
|
|
344
|
+
adds: "string | Hash",
|
|
345
|
+
upstream: ["render_target"],
|
|
346
|
+
reason:
|
|
347
|
+
"example `render.get_render_target_height('my_rt_resource', graphics.BUFFER_TYPE_COLOR0_BIT)`",
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
[
|
|
351
|
+
"render.set_render_target_size:param:render_target",
|
|
352
|
+
{
|
|
353
|
+
adds: "string | Hash",
|
|
354
|
+
upstream: ["render_target"],
|
|
355
|
+
reason:
|
|
356
|
+
"\"a render target created from either a render script, or from a render target resource\"; example `render.set_render_target_size('my_rt_resource', ...)`",
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
[
|
|
360
|
+
"render.enable_texture:param:handle_or_name",
|
|
361
|
+
{
|
|
362
|
+
adds: 'Opaque<"render_target"> | number',
|
|
363
|
+
upstream: ["texture", "string", "hash"],
|
|
364
|
+
reason:
|
|
365
|
+
"\"render target or texture handle that should be bound, or a named resource\"; examples `render.enable_texture(0, self.my_render_target, ...)` and `render.enable_texture(0, 'my_rt_resource', ...)`; `resource.get_texture_info` returns the handle as a number",
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
[
|
|
369
|
+
"render.disable_texture:param:binding",
|
|
370
|
+
{
|
|
371
|
+
adds: "number",
|
|
372
|
+
upstream: ["texture", "string", "hash"],
|
|
373
|
+
reason:
|
|
374
|
+
'"texture binding, either by texture unit, string or hash"; example `render.disable_texture(0)`',
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
[
|
|
378
|
+
"render.set_camera:param:camera",
|
|
379
|
+
{
|
|
380
|
+
adds: "string",
|
|
381
|
+
upstream: ["url", "number", "nil"],
|
|
382
|
+
reason: 'example `render.set_camera("main:/my_go#camera")`',
|
|
383
|
+
},
|
|
384
|
+
],
|
|
385
|
+
[
|
|
386
|
+
"go.delete:param:id",
|
|
387
|
+
{
|
|
388
|
+
adds: "boolean",
|
|
389
|
+
upstream: ["string", "hash", "url", "table"],
|
|
390
|
+
reason: "example `go.delete(true)` deletes the script game object and its children",
|
|
391
|
+
},
|
|
392
|
+
],
|
|
393
|
+
[
|
|
394
|
+
"buffer.get_bytes:param:stream_name",
|
|
395
|
+
{
|
|
396
|
+
adds: "string",
|
|
397
|
+
upstream: ["hash"],
|
|
398
|
+
reason: 'sys.load_buffer example `buffer.get_bytes(my_buffer, "data")`',
|
|
399
|
+
},
|
|
400
|
+
],
|
|
401
|
+
[
|
|
402
|
+
"gui.set:param:value",
|
|
403
|
+
{
|
|
404
|
+
adds: "Matrix4 | Hash",
|
|
405
|
+
upstream: ["number", "vector4", "vector3", "quaternion"],
|
|
406
|
+
reason:
|
|
407
|
+
'example `gui.set(node, "light_matrix", vmath.matrix4())` ("matrix4 is also supported"); example `gui.set(msg.url(), "textures", atlas_id, {key = "runtime_texture"})` passes the hash `resource.create_atlas` returns',
|
|
408
|
+
},
|
|
409
|
+
],
|
|
410
|
+
[
|
|
411
|
+
"gui.set_rotation:param:rotation",
|
|
412
|
+
{
|
|
413
|
+
adds: "Vector3",
|
|
414
|
+
upstream: ["quaternion", "vector4"],
|
|
415
|
+
reason: "gui.set example `gui.set_rotation(node, vmath.vector3(0,0,45))`",
|
|
416
|
+
},
|
|
417
|
+
],
|
|
418
|
+
[
|
|
419
|
+
"sys.save:param:table",
|
|
420
|
+
{
|
|
421
|
+
adds: "readonly unknown[]",
|
|
422
|
+
upstream: ["table"],
|
|
423
|
+
reason:
|
|
424
|
+
'example builds `my_table` with `table.insert(my_table, "my_value")` and passes it to `sys.save`',
|
|
425
|
+
},
|
|
426
|
+
],
|
|
427
|
+
[
|
|
428
|
+
"sys.serialize:param:table",
|
|
429
|
+
{
|
|
430
|
+
adds: "readonly unknown[]",
|
|
431
|
+
upstream: ["table"],
|
|
432
|
+
reason:
|
|
433
|
+
'example builds `my_table` with `table.insert(my_table, "my_value")` and passes it to `sys.serialize`',
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
]);
|
|
437
|
+
|
|
438
|
+
// Parameter doc text whose upstream wording describes Lua rather than the
|
|
439
|
+
// TypeScript a user writes. The transpiler adds one to a literal `options.index`
|
|
440
|
+
// at these calls, so the 1-based wording would tell the author to be off by one.
|
|
441
|
+
// The `.d.ts` emit and the API reference both apply it, and applying it throws
|
|
442
|
+
// when `from` is absent: an upstream rewording fails the build instead of
|
|
443
|
+
// shipping the stale text alongside the lowering.
|
|
444
|
+
export interface ParamDocRewrite {
|
|
445
|
+
readonly from: string;
|
|
446
|
+
readonly to: string;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const ZERO_BASED_INDEX_OPTION_DOC: ParamDocRewrite = {
|
|
450
|
+
from: "index into array property (1 based)",
|
|
451
|
+
to: "zero-based index into array property; the transpiler emits Defold's 1-based index",
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
// Keyed `<element>:param:<slot>` like PARAM_TYPE_CORRECTIONS.
|
|
455
|
+
export const PARAM_DOC_REWRITES: ReadonlyMap<string, ParamDocRewrite> = new Map([
|
|
456
|
+
["go.get:param:options", ZERO_BASED_INDEX_OPTION_DOC],
|
|
457
|
+
["go.set:param:options", ZERO_BASED_INDEX_OPTION_DOC],
|
|
458
|
+
["gui.get:param:options", ZERO_BASED_INDEX_OPTION_DOC],
|
|
459
|
+
["gui.set:param:options", ZERO_BASED_INDEX_OPTION_DOC],
|
|
273
460
|
]);
|
|
274
461
|
|
|
462
|
+
export function rewriteParamDoc(elementName: string, slotName: string, doc: string): string {
|
|
463
|
+
const key = tableSlotKey(elementName, "param", slotName);
|
|
464
|
+
const rewrite = PARAM_DOC_REWRITES.get(key);
|
|
465
|
+
if (rewrite === undefined) return doc;
|
|
466
|
+
if (!doc.includes(rewrite.from)) {
|
|
467
|
+
throw new Error(`PARAM_DOC_REWRITES ${key}: upstream doc no longer contains "${rewrite.from}"`);
|
|
468
|
+
}
|
|
469
|
+
return doc.replaceAll(rewrite.from, rewrite.to);
|
|
470
|
+
}
|
|
471
|
+
|
|
275
472
|
// A property whose upstream `<span class="type">` states a type the engine does
|
|
276
473
|
// not use. Unlike every other override here, which fills a gap upstream left
|
|
277
474
|
// empty, these contradict a token upstream explicitly declares — so each entry
|
|
@@ -320,6 +517,26 @@ export const PROPERTY_TYPE_CORRECTIONS: ReadonlyMap<string, PropertyTypeCorrecti
|
|
|
320
517
|
],
|
|
321
518
|
]);
|
|
322
519
|
|
|
520
|
+
// A property upstream names with a placeholder standing for a numbered family of
|
|
521
|
+
// keys, so its literal name is not a key the engine accepts. The emitted members
|
|
522
|
+
// are one literal key per number in `first..last`, each typed like the
|
|
523
|
+
// placeholder. `evidence` is the brief phrase that defines the range;
|
|
524
|
+
// `property-correction-provenance.test.ts` reds when a vendored ref-doc stops
|
|
525
|
+
// saying it, or when the phrase stops stating `first-last`. Curated rather than
|
|
526
|
+
// matched on a trailing `N`, so each new upstream placeholder is a deliberate
|
|
527
|
+
// addition.
|
|
528
|
+
export interface PropertyKeyRange {
|
|
529
|
+
readonly prefix: string;
|
|
530
|
+
readonly first: number;
|
|
531
|
+
readonly last: number;
|
|
532
|
+
readonly evidence: string;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Keyed `<namespace>.<property>`, mirroring PROPERTY_TYPE_CORRECTIONS.
|
|
536
|
+
export const PROPERTY_KEY_RANGES: ReadonlyMap<string, PropertyKeyRange> = new Map([
|
|
537
|
+
["model.textureN", { prefix: "texture", first: 0, last: 7, evidence: "textureN where N is 0-7" }],
|
|
538
|
+
]);
|
|
539
|
+
|
|
323
540
|
// A parameter the ref-doc metadata marks required while the same element's prose
|
|
324
541
|
// or examples show it being omitted. `is_optional` is the only optionality
|
|
325
542
|
// signal the emitter has, and upstream leaves it `False` on slots its own
|
|
@@ -357,11 +574,16 @@ export const OPTIONAL_SLOT_CORRECTIONS: ReadonlyMap<string, string> = new Map([
|
|
|
357
574
|
"b2d.fixture.set_shape:param:update_mass",
|
|
358
575
|
'2 of 3 examples omit it; "The body mass is not updated unless update_mass is true"',
|
|
359
576
|
],
|
|
577
|
+
[
|
|
578
|
+
"b2d.shape.set_shape:param:update_mass",
|
|
579
|
+
'1 of 3 examples omits it; "The body mass is not updated unless update_mass is true"',
|
|
580
|
+
],
|
|
360
581
|
[
|
|
361
582
|
"b2d.shape.ray_cast:param:max_fraction",
|
|
362
583
|
'"optional maximum translation fraction, defaults to 1"',
|
|
363
584
|
],
|
|
364
585
|
["iap.buy:param:options", '"optional parameters as properties"'],
|
|
586
|
+
["socket.newtry:param:finalizer", "socket.protect's example `local try = socket.newtry()`"],
|
|
365
587
|
...(
|
|
366
588
|
[
|
|
367
589
|
"create_distance",
|
|
@@ -383,6 +605,69 @@ export const OPTIONAL_SLOT_CORRECTIONS: ReadonlyMap<string, string> = new Map([
|
|
|
383
605
|
),
|
|
384
606
|
]);
|
|
385
607
|
|
|
608
|
+
// A first slot upstream documents as standing in for a second argument list:
|
|
609
|
+
// every `b2d.shape` function takes a shape handle "or pass `body, shape_index`".
|
|
610
|
+
// The element keeps its declared signature and gains an overload with
|
|
611
|
+
// `parameters` in that slot's place. Keyed `<namespace>:param:<slot>`, so only a
|
|
612
|
+
// curated entry adds an overload: a reworded doc cannot add or drop one, and the
|
|
613
|
+
// same prose in another namespace adds nothing.
|
|
614
|
+
//
|
|
615
|
+
// `evidence` is the upstream slot-doc fragment that names the alternative. The
|
|
616
|
+
// emitter throws once a curated slot stops carrying it, so upstream dropping the
|
|
617
|
+
// alternative reds here rather than shipping an overload nothing documents.
|
|
618
|
+
export interface FirstSlotAlternative {
|
|
619
|
+
readonly parameters: readonly ApiParameter[];
|
|
620
|
+
readonly evidence: string;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
export const FIRST_SLOT_ALTERNATIVES: ReadonlyMap<string, FirstSlotAlternative> = new Map([
|
|
624
|
+
[
|
|
625
|
+
"b2d.shape:param:shape_id",
|
|
626
|
+
{
|
|
627
|
+
parameters: [
|
|
628
|
+
{
|
|
629
|
+
name: "body",
|
|
630
|
+
doc: "The body that owns the shape.",
|
|
631
|
+
types: ["b2Body"],
|
|
632
|
+
isOptional: false,
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
name: "shape_index",
|
|
636
|
+
doc: "The shape's 1-based index on that body, as <code>b2d.body.get_shapes</code> lists them.",
|
|
637
|
+
types: ["number"],
|
|
638
|
+
isOptional: false,
|
|
639
|
+
},
|
|
640
|
+
],
|
|
641
|
+
evidence: "or pass <code>body, shape_index</code>",
|
|
642
|
+
},
|
|
643
|
+
],
|
|
644
|
+
]);
|
|
645
|
+
|
|
646
|
+
// The alternative signature FIRST_SLOT_ALTERNATIVES gives `fn`, or null when
|
|
647
|
+
// its first slot is not curated. Shared with the fidelity audit so the arity it
|
|
648
|
+
// measures is the arity the emitter declares.
|
|
649
|
+
export function firstSlotAlternativeOf(fn: ApiFunction, namespace: string): ApiFunction | null {
|
|
650
|
+
const [first, ...rest] = fn.parameters;
|
|
651
|
+
if (first === undefined) return null;
|
|
652
|
+
const key = `${namespace}:param:${first.name}`;
|
|
653
|
+
const alternative = FIRST_SLOT_ALTERNATIVES.get(key);
|
|
654
|
+
if (alternative === undefined) return null;
|
|
655
|
+
if (!first.doc.includes(alternative.evidence)) {
|
|
656
|
+
throw new Error(
|
|
657
|
+
`first-slot alternative ${key}: ${fn.name} no longer documents "${alternative.evidence}"`,
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
return { ...fn, parameters: [...alternative.parameters, ...rest] };
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
function firstSlotAlternative(
|
|
664
|
+
prepared: PreparedFunction,
|
|
665
|
+
namespace: string,
|
|
666
|
+
): PreparedFunction | null {
|
|
667
|
+
const original = firstSlotAlternativeOf(prepared.original, namespace);
|
|
668
|
+
return original === null ? null : { ...prepared, original };
|
|
669
|
+
}
|
|
670
|
+
|
|
386
671
|
export type ConstantSlotResolution =
|
|
387
672
|
| { readonly borrow: string }
|
|
388
673
|
| { readonly family: string; readonly evidence: string }
|
|
@@ -1047,6 +1332,27 @@ const PHYSICS_SHAPE_TABLE_FIELDS: readonly TableField[] = [
|
|
|
1047
1332
|
];
|
|
1048
1333
|
|
|
1049
1334
|
export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new Map([
|
|
1335
|
+
// buffer.create's `declaration` is "a table where each entry (table) describes
|
|
1336
|
+
// a stream", and upstream's example passes a list of stream records whose
|
|
1337
|
+
// `type` is a buffer.VALUE_TYPE_* constant.
|
|
1338
|
+
[
|
|
1339
|
+
"buffer.create:param:declaration",
|
|
1340
|
+
{
|
|
1341
|
+
kind: "array-object",
|
|
1342
|
+
fields: [
|
|
1343
|
+
{ name: "name", types: ["hash", "string"] },
|
|
1344
|
+
{ name: "type", types: ["constant"], tsType: "buffer.ValueType" },
|
|
1345
|
+
{ name: "count", types: ["number"] },
|
|
1346
|
+
],
|
|
1347
|
+
},
|
|
1348
|
+
],
|
|
1349
|
+
// collectionfactory.create's `properties` is keyed by "game object ids (hash)",
|
|
1350
|
+
// which upstream's example builds with `hash("/go")`. The `string` key keeps
|
|
1351
|
+
// the object-literal form this slot accepted as a bare `table`.
|
|
1352
|
+
[
|
|
1353
|
+
"collectionfactory.create:param:properties",
|
|
1354
|
+
{ kind: "mapping", key: "hash | string", value: "table" },
|
|
1355
|
+
],
|
|
1050
1356
|
["collectionfactory.create:return:ids", { kind: "mapping", key: "hash", value: "hash" }],
|
|
1051
1357
|
// font.get_info's `info` return is a `<dl>` with `path: hash` and a nested
|
|
1052
1358
|
// `fonts` field whose `<dd>` is itself a `<dl>` (`path: string`, `path_hash:
|
|
@@ -1752,11 +2058,33 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
1752
2058
|
// its `data` is a bare record (only resize carries fields — `isWindowEvent` is the
|
|
1753
2059
|
// path to typed `data`), mirroring the `msg.post` (send, typed) / `isMessage`
|
|
1754
2060
|
// (receive, narrow) split.
|
|
2061
|
+
//
|
|
2062
|
+
// The other entries type what the slot's own prose lists and nothing more.
|
|
2063
|
+
// `http.request`'s response fields are the `<ul>` under "The response data.
|
|
2064
|
+
// Contains the fields:"; each is conditional ("if not saved on disc", "only if
|
|
2065
|
+
// option report_progress is true") except `status`, and `headers` is the same
|
|
2066
|
+
// string map the request sends. `sprite.play_flipbook`'s message is the
|
|
2067
|
+
// "Information about the completion" list. `collectionproxy.load` documents
|
|
2068
|
+
// only that the callback receives `proxy_loading`, `proxy_ready` or
|
|
2069
|
+
// `proxy_error` messages and lists no fields, so its message is the bare record
|
|
2070
|
+
// `window.set_listener`'s `data` is.
|
|
1755
2071
|
export const CALLBACK_SIGNATURE_CURATIONS: ReadonlyMap<string, string> = new Map([
|
|
1756
2072
|
[
|
|
1757
2073
|
"window.set_listener:param:callback",
|
|
1758
2074
|
"(self: unknown, event: typeof WINDOW_EVENT_FOCUS_LOST | typeof WINDOW_EVENT_FOCUS_GAINED | typeof WINDOW_EVENT_RESIZED | typeof WINDOW_EVENT_ICONFIED | typeof WINDOW_EVENT_DEICONIFIED, data: Record<string | number, unknown>) => void",
|
|
1759
2075
|
],
|
|
2076
|
+
[
|
|
2077
|
+
"http.request:param:callback",
|
|
2078
|
+
"(self: unknown, id: Hash, response: { status: number; response?: string; headers?: LuaMap<string, string>; path?: string; error?: string; bytes_received?: number; bytes_total?: number; range_start?: number; range_end?: number; document_size?: number }) => void",
|
|
2079
|
+
],
|
|
2080
|
+
[
|
|
2081
|
+
"sprite.play_flipbook:param:complete_function",
|
|
2082
|
+
"(self: unknown, message_id: Hash, message: { current_tile: number; id: Hash }, sender: Url) => void",
|
|
2083
|
+
],
|
|
2084
|
+
[
|
|
2085
|
+
"collectionproxy.load:param:callback",
|
|
2086
|
+
"(self: unknown, message_id: Hash, message: Record<string | number, unknown>, sender: Url) => void",
|
|
2087
|
+
],
|
|
1760
2088
|
]);
|
|
1761
2089
|
|
|
1762
2090
|
// resource.set_atlas's `table` param and resource.get_atlas's `data` return are
|
|
@@ -1789,7 +2117,19 @@ export const NESTED_FIELD_CURATIONS: ReadonlyMap<string, readonly TableField[]>
|
|
|
1789
2117
|
// parser recovers every other field faithfully but leaves that `table` token as
|
|
1790
2118
|
// a bare `Record`; this pins only the `value` field's TS type to the documented
|
|
1791
2119
|
// array union while every sibling field stays parser-authoritative. Keyed by
|
|
1792
|
-
// `<element>:<kind>:<slot>:<field>`, mirroring NESTED_FIELD_CURATIONS
|
|
2120
|
+
// `<element>:<kind>:<slot>:<field>`, mirroring NESTED_FIELD_CURATIONS; a field
|
|
2121
|
+
// one level down is `<field>.<member>`, or `<field>[].<member>` inside a list.
|
|
2122
|
+
//
|
|
2123
|
+
// The atlas animation `playback` field is documented as `constant` with the
|
|
2124
|
+
// prose "the default value is go.PLAYBACK_ONCE_FORWARD", which names the enum
|
|
2125
|
+
// the bare token cannot.
|
|
2126
|
+
//
|
|
2127
|
+
// `gui.set`'s `options.key` is documented as `hash`, while upstream's own
|
|
2128
|
+
// example sets a named font with `{ key = "my_font_name" }`.
|
|
2129
|
+
//
|
|
2130
|
+
// `render.set_render_target`'s `options.transient` is documented as `table`
|
|
2131
|
+
// with the prose "a list of buffer types", and upstream's example passes
|
|
2132
|
+
// graphics.BUFFER_TYPE_* constants.
|
|
1793
2133
|
const CONSTANT_VALUE_TS = "Vector4 | Vector3 | Matrix4 | number | (Vector4 | Matrix4)[]";
|
|
1794
2134
|
const ATTRIBUTE_VALUE_TS = "Vector4 | Vector3 | Matrix4 | number | number[]";
|
|
1795
2135
|
export const TABLE_FIELD_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
|
|
@@ -1797,6 +2137,11 @@ export const TABLE_FIELD_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
|
|
|
1797
2137
|
["material.set_constants:param:constants:value", CONSTANT_VALUE_TS],
|
|
1798
2138
|
["material.set_vertex_attributes:param:attributes:value", ATTRIBUTE_VALUE_TS],
|
|
1799
2139
|
["material.get_vertex_attributes:return:table:value", ATTRIBUTE_VALUE_TS],
|
|
2140
|
+
["resource.set_atlas:param:table:animations[].playback", "go.Playback"],
|
|
2141
|
+
["resource.create_atlas:param:table:animations[].playback", "go.Playback"],
|
|
2142
|
+
["resource.get_atlas:return:data:animations[].playback", "go.Playback"],
|
|
2143
|
+
["gui.set:param:options:key", "string | Hash"],
|
|
2144
|
+
["render.set_render_target:param:options:transient", "graphics.BufferType[]"],
|
|
1800
2145
|
]);
|
|
1801
2146
|
|
|
1802
2147
|
// A field the engine accepts on an option bag whose `<dl>` upstream otherwise
|
|
@@ -1949,11 +2294,17 @@ export function applyFieldTypeOverrides(
|
|
|
1949
2294
|
fields: readonly TableField[],
|
|
1950
2295
|
): TableField[] {
|
|
1951
2296
|
if (slotKind === undefined || slotName === undefined) return [...fields];
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
);
|
|
2297
|
+
const prefix = `${tableSlotKey(elementName, slotKind, slotName)}:`;
|
|
2298
|
+
const typed = (field: TableField, path: string): TableField => {
|
|
2299
|
+
const override = TABLE_FIELD_TYPE_OVERRIDES.get(`${prefix}${path}`);
|
|
1956
2300
|
return override === undefined ? field : { ...field, tsType: override };
|
|
2301
|
+
};
|
|
2302
|
+
return fields.map((field) => {
|
|
2303
|
+
const own = typed(field, field.name);
|
|
2304
|
+
if (own.fields === undefined) return own;
|
|
2305
|
+
const parent = `${field.name}${field.isList === true ? "[]" : ""}`;
|
|
2306
|
+
const nested = own.fields.map((child) => typed(child, `${parent}.${child.name}`));
|
|
2307
|
+
return nested.every((child, i) => child === own.fields?.[i]) ? own : { ...own, fields: nested };
|
|
1957
2308
|
});
|
|
1958
2309
|
}
|
|
1959
2310
|
|
|
@@ -2285,6 +2636,15 @@ function groupFlattenedTableFields(fields: readonly TableField[]): TableField[]
|
|
|
2285
2636
|
return grouped;
|
|
2286
2637
|
}
|
|
2287
2638
|
|
|
2639
|
+
// A handle the type map widens (`constant_buffer` takes named constants) keeps
|
|
2640
|
+
// that shape, so annotating with the alias loses nothing the handle-returning
|
|
2641
|
+
// function yields.
|
|
2642
|
+
function typedefAlias(name: string, mapType: (token: string) => string): string {
|
|
2643
|
+
const brand = `Opaque<"${name}">`;
|
|
2644
|
+
const mapped = mapType(name);
|
|
2645
|
+
return `type ${name} = ${mapped.startsWith(brand) ? mapped : brand};`;
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2288
2648
|
export function emitDeclarations(module: ApiModule, options?: EmitOptions): string {
|
|
2289
2649
|
const prefix = `${module.namespace}.`;
|
|
2290
2650
|
|
|
@@ -2367,7 +2727,7 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
|
|
|
2367
2727
|
// A typedef that also has colon methods is emitted as a method-bearing
|
|
2368
2728
|
// interface below, not an opaque brand alias.
|
|
2369
2729
|
if (handleGroups.has(t.name)) continue;
|
|
2370
|
-
lines.push(`${INDENT}${decl}
|
|
2730
|
+
lines.push(`${INDENT}${decl}${typedefAlias(t.name, baseMapType)}`);
|
|
2371
2731
|
}
|
|
2372
2732
|
for (const aliasDecl of constantUnionAliasDeclarations(module.namespace)) {
|
|
2373
2733
|
lines.push(`${INDENT}${decl}${aliasDecl}`);
|
|
@@ -2413,6 +2773,18 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
|
|
|
2413
2773
|
for (const docLine of functionDocLines(fn.original, translations)) lines.push(docLine);
|
|
2414
2774
|
const line = emitFunction(fn, emitName, mapType, resolver, constantTokens, urlParameters);
|
|
2415
2775
|
lines.push(`${INDENT}${reserved ? "" : decl}${line}`);
|
|
2776
|
+
const alternative = firstSlotAlternative(fn, module.namespace);
|
|
2777
|
+
if (alternative !== null) {
|
|
2778
|
+
const overload = emitFunction(
|
|
2779
|
+
alternative,
|
|
2780
|
+
emitName,
|
|
2781
|
+
mapType,
|
|
2782
|
+
resolver,
|
|
2783
|
+
constantTokens,
|
|
2784
|
+
urlParameters,
|
|
2785
|
+
);
|
|
2786
|
+
lines.push(`${INDENT}${reserved ? "" : decl}${overload}`);
|
|
2787
|
+
}
|
|
2416
2788
|
}
|
|
2417
2789
|
|
|
2418
2790
|
for (const alias of [...aliases].sort((a, b) => a.public.localeCompare(b.public))) {
|
|
@@ -2475,10 +2847,12 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
|
|
|
2475
2847
|
const members = [...module.properties].sort((a, b) => a.name.localeCompare(b.name));
|
|
2476
2848
|
lines.push(`${INDENT}${decl}interface properties {`);
|
|
2477
2849
|
for (const p of members) {
|
|
2478
|
-
for (const
|
|
2479
|
-
|
|
2850
|
+
for (const member of emitPropertyMembers(p, mapType, module.namespace)) {
|
|
2851
|
+
for (const docLine of summaryDocLines(p.brief, p.description, `${INDENT}${INDENT}`)) {
|
|
2852
|
+
lines.push(docLine);
|
|
2853
|
+
}
|
|
2854
|
+
lines.push(`${INDENT}${INDENT}${member}`);
|
|
2480
2855
|
}
|
|
2481
|
-
lines.push(`${INDENT}${INDENT}${emitPropertyMember(p, mapType, module.namespace)}`);
|
|
2482
2856
|
}
|
|
2483
2857
|
lines.push(`${INDENT}}`);
|
|
2484
2858
|
}
|
|
@@ -2518,7 +2892,10 @@ export interface SymbolSignature {
|
|
|
2518
2892
|
* {@link collectNestedGroups} tree the declaration emitter walks, so neither
|
|
2519
2893
|
* surface can decide on its own which members are nested or how deep they go.
|
|
2520
2894
|
* The caller applies the same `skipFunctions` filter the `.d.ts` generation
|
|
2521
|
-
* does, so a dropped member never yields a signature.
|
|
2895
|
+
* does, so a dropped member never yields a signature. A top-level function the
|
|
2896
|
+
* declaration emits with a first-slot alternative overload carries one arm per
|
|
2897
|
+
* line, primary first, the same convention as the authored fold; its
|
|
2898
|
+
* `slotTypes` describe the primary arm only.
|
|
2522
2899
|
*/
|
|
2523
2900
|
export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions): SymbolSignature[] {
|
|
2524
2901
|
const prefix = `${module.namespace}.`;
|
|
@@ -2558,19 +2935,28 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
2558
2935
|
const prepared = prepareFunction(fn, prefix);
|
|
2559
2936
|
if (prepared === null) continue;
|
|
2560
2937
|
const slotTypes: Record<string, string> = {};
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
constantTokens,
|
|
2569
|
-
urlParameters,
|
|
2570
|
-
slotTypes,
|
|
2571
|
-
),
|
|
2938
|
+
const primary = emitFunction(
|
|
2939
|
+
prepared,
|
|
2940
|
+
emitName(prepared.name),
|
|
2941
|
+
mapType,
|
|
2942
|
+
resolver,
|
|
2943
|
+
constantTokens,
|
|
2944
|
+
urlParameters,
|
|
2572
2945
|
slotTypes,
|
|
2573
|
-
|
|
2946
|
+
);
|
|
2947
|
+
const alternative = firstSlotAlternative(prepared, module.namespace);
|
|
2948
|
+
const tsSignature =
|
|
2949
|
+
alternative === null
|
|
2950
|
+
? primary
|
|
2951
|
+
: `${primary}\n${emitFunction(
|
|
2952
|
+
alternative,
|
|
2953
|
+
emitName(prepared.name),
|
|
2954
|
+
mapType,
|
|
2955
|
+
resolver,
|
|
2956
|
+
constantTokens,
|
|
2957
|
+
urlParameters,
|
|
2958
|
+
)}`;
|
|
2959
|
+
out.push({ identity: fnIdentity(fn), tsSignature, slotTypes });
|
|
2574
2960
|
}
|
|
2575
2961
|
|
|
2576
2962
|
const nested = flattenNestedGroups(collectNestedGroups(module, prefix));
|
|
@@ -2642,10 +3028,14 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
2642
3028
|
});
|
|
2643
3029
|
}
|
|
2644
3030
|
|
|
3031
|
+
// A key-range property emits several members that share one type; its entry
|
|
3032
|
+
// carries the first, which is verbatim in the `.d.ts` and is what the `/api`
|
|
3033
|
+
// render layer reduces to `<placeholder>: <type>`.
|
|
2645
3034
|
for (const p of module.properties) {
|
|
3035
|
+
const [firstMember = ""] = emitPropertyMembers(p, mapType, module.namespace);
|
|
2646
3036
|
out.push({
|
|
2647
3037
|
identity: { namespace: module.namespace, kind: "PROPERTY", name: p.name, signature: "" },
|
|
2648
|
-
tsSignature:
|
|
3038
|
+
tsSignature: firstMember,
|
|
2649
3039
|
slotTypes: NO_SLOTS,
|
|
2650
3040
|
});
|
|
2651
3041
|
}
|
|
@@ -2654,7 +3044,7 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
2654
3044
|
if (!TS_IDENTIFIER.test(t.name) || handleGroups.has(t.name)) continue;
|
|
2655
3045
|
out.push({
|
|
2656
3046
|
identity: { namespace: module.namespace, kind: "TYPEDEF", name: t.name, signature: "" },
|
|
2657
|
-
tsSignature:
|
|
3047
|
+
tsSignature: typedefAlias(t.name, baseMapType),
|
|
2658
3048
|
slotTypes: NO_SLOTS,
|
|
2659
3049
|
});
|
|
2660
3050
|
}
|
|
@@ -2971,7 +3361,7 @@ function functionDocLines(
|
|
|
2971
3361
|
): string[] {
|
|
2972
3362
|
const params = fn.parameters.map((p, index) => ({
|
|
2973
3363
|
name: emittedParamName(p, index),
|
|
2974
|
-
doc: htmlToDocText(p.doc),
|
|
3364
|
+
doc: rewriteParamDoc(fn.name, p.name, htmlToDocText(p.doc)),
|
|
2975
3365
|
}));
|
|
2976
3366
|
const onlyReturn = fn.returnValues.length === 1 ? fn.returnValues[0] : undefined;
|
|
2977
3367
|
const lua = htmlToCodeText(fn.examples ?? "");
|
|
@@ -3102,19 +3492,22 @@ function parameterType(
|
|
|
3102
3492
|
// `safeParamName` fallback.
|
|
3103
3493
|
const alias = SCENE_ADDRESS_ALIASES[classifyUrlParameter(urlParameters, elementName, p.name)];
|
|
3104
3494
|
const slotMapType = alias === undefined ? mapType : addressMapType(mapType, alias);
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3495
|
+
const mapped =
|
|
3496
|
+
concrete.length > 0
|
|
3497
|
+
? mapSlotUnion(
|
|
3498
|
+
concrete,
|
|
3499
|
+
p.doc,
|
|
3500
|
+
slotMapType,
|
|
3501
|
+
true,
|
|
3502
|
+
resolver,
|
|
3503
|
+
constantTokens,
|
|
3504
|
+
elementName,
|
|
3505
|
+
"param",
|
|
3506
|
+
p.name,
|
|
3507
|
+
)
|
|
3508
|
+
: "unknown";
|
|
3509
|
+
const correction = PARAM_TYPE_CORRECTIONS.get(tableSlotKey(elementName, "param", p.name));
|
|
3510
|
+
return correction === undefined ? mapped : `${mapped} | ${correction.adds}`;
|
|
3118
3511
|
}
|
|
3119
3512
|
|
|
3120
3513
|
const VARARG_PREFIX = "...";
|
|
@@ -3263,12 +3656,19 @@ function emitReturn(
|
|
|
3263
3656
|
return { type: ts, trailing: "", slots: [{ position: 0, name: first.name, ts }] };
|
|
3264
3657
|
}
|
|
3265
3658
|
|
|
3266
|
-
function
|
|
3659
|
+
function emitPropertyMembers(
|
|
3267
3660
|
p: ApiProperty,
|
|
3268
3661
|
mapType: (t: string) => string,
|
|
3269
3662
|
namespace: string,
|
|
3270
|
-
): string {
|
|
3271
|
-
const
|
|
3663
|
+
): readonly string[] {
|
|
3664
|
+
const keyRange = PROPERTY_KEY_RANGES.get(`${namespace}.${p.name}`);
|
|
3665
|
+
const keys: string[] = [];
|
|
3666
|
+
if (keyRange !== undefined) {
|
|
3667
|
+
for (let unit = keyRange.first; unit <= keyRange.last; unit++)
|
|
3668
|
+
keys.push(`${keyRange.prefix}${unit}`);
|
|
3669
|
+
} else {
|
|
3670
|
+
keys.push(TS_IDENTIFIER.test(p.name) ? p.name : JSON.stringify(p.name));
|
|
3671
|
+
}
|
|
3272
3672
|
const correction = PROPERTY_TYPE_CORRECTIONS.get(`${namespace}.${p.name}`);
|
|
3273
3673
|
const ts =
|
|
3274
3674
|
correction !== undefined
|
|
@@ -3276,7 +3676,7 @@ function emitPropertyMember(
|
|
|
3276
3676
|
: p.types.length > 0
|
|
3277
3677
|
? unionFromTokens(p.types, mapType)
|
|
3278
3678
|
: "unknown";
|
|
3279
|
-
return `${key}: ${ts}
|
|
3679
|
+
return keys.map((key) => `${key}: ${ts};`);
|
|
3280
3680
|
}
|
|
3281
3681
|
|
|
3282
3682
|
// Like `unionFromTokens`, but a `table` token whose slot doc carries a parseable
|
|
@@ -3341,11 +3741,21 @@ function mapSlotUnion(
|
|
|
3341
3741
|
// A mapping key may be a `A | B` union token too (render.clear's three
|
|
3342
3742
|
// documented graphics.BUFFER_TYPE_* constants); split it the same way
|
|
3343
3743
|
// the value above is split. A single-token key (no `|`) is unaffected.
|
|
3344
|
-
const
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3744
|
+
const keyTokens = mapping.key.split("|").map((token) => token.trim());
|
|
3745
|
+
if (slotKind === "param" && keyTokens.includes("string")) {
|
|
3746
|
+
// A caller builds a string-keyed parameter table as an object literal,
|
|
3747
|
+
// which `LuaMap` rejects; the `Record` arm admits it and TSTL lowers
|
|
3748
|
+
// both to the same Lua table. The `Record` arm carries the string keys,
|
|
3749
|
+
// so `LuaMap` keeps only the other key tokens: `LuaMap`'s key is
|
|
3750
|
+
// invariant, and a `LuaMap<Hash | string, V>` would reject the
|
|
3751
|
+
// `LuaMap<Hash, V>` a caller builds. A string-only key keeps its
|
|
3752
|
+
// `LuaMap` arm so an existing `LuaMap` caller stays valid.
|
|
3753
|
+
const otherKeys = keyTokens.filter((token) => token !== "string");
|
|
3754
|
+
const key = unionFromTokens(otherKeys.length > 0 ? otherKeys : keyTokens, mapType);
|
|
3755
|
+
ts = `LuaMap<${key}, ${value}> | Record<string, ${value}>`;
|
|
3756
|
+
} else {
|
|
3757
|
+
ts = `LuaMap<${unionFromTokens(keyTokens, mapType)}, ${value}>`;
|
|
3758
|
+
}
|
|
3349
3759
|
} else if (element !== undefined) {
|
|
3350
3760
|
ts = arrayTypeFromTokens(element, mapType);
|
|
3351
3761
|
} else if (curation?.kind === "object" || curation?.kind === "array-object") {
|