@defold-typescript/types 0.34.0 → 0.36.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-signatures.json +9547 -186
- package/api-targets.json +9 -66
- package/examples/translations.json +6 -6
- package/generated/buffer.d.ts +2 -2
- package/generated/collectionfactory.d.ts +2 -2
- package/generated/factory.d.ts +13 -12
- package/generated/gui.d.ts +23 -23
- package/generated/kinds/gui-script.d.ts +0 -4
- package/generated/kinds/render-script.d.ts +1 -4
- package/generated/kinds/script.d.ts +0 -4
- package/generated/model.d.ts +2 -2
- package/generated/profiler.d.ts +2 -4
- package/generated/render.d.ts +7 -155
- package/generated/sys.d.ts +1 -1
- package/generated/versions/defold-1.12.4/buffer.d.ts +2 -2
- package/generated/versions/defold-1.12.4/collectionfactory.d.ts +2 -2
- package/generated/versions/defold-1.12.4/factory.d.ts +13 -12
- package/generated/versions/defold-1.12.4/gui.d.ts +23 -23
- package/generated/versions/defold-1.12.4/index.d.ts +9 -4
- package/generated/versions/defold-1.12.4/model.d.ts +2 -2
- package/generated/versions/defold-1.12.4/profiler.d.ts +2 -4
- package/generated/versions/defold-1.12.4/render.d.ts +7 -155
- package/generated/versions/defold-1.12.4/sys.d.ts +1 -1
- package/generated/versions/defold-1.12.4/vmath.d.ts +0 -19
- package/generated/versions/defold-1.12.4/window.d.ts +2 -4
- package/generated/versions/defold-1.12.4/zlib.d.ts +2 -1
- package/generated/versions/defold-1.13.0/buffer.d.ts +2 -2
- package/generated/versions/defold-1.13.0/collectionfactory.d.ts +2 -2
- package/generated/versions/defold-1.13.0/factory.d.ts +13 -12
- package/generated/versions/defold-1.13.0/gui.d.ts +23 -23
- package/generated/versions/defold-1.13.0/index.d.ts +9 -4
- package/generated/versions/defold-1.13.0/model.d.ts +2 -2
- package/generated/versions/defold-1.13.0/profiler.d.ts +2 -4
- package/generated/versions/defold-1.13.0/render.d.ts +7 -155
- package/generated/versions/defold-1.13.0/sys.d.ts +1 -1
- package/generated/versions/defold-1.13.0/vmath.d.ts +0 -19
- package/generated/versions/defold-1.13.0/window.d.ts +2 -4
- package/generated/versions/defold-1.13.0/zlib.d.ts +2 -1
- package/generated/vmath.d.ts +0 -19
- package/generated/window.d.ts +2 -4
- package/generated/zlib.d.ts +2 -1
- package/index.d.ts +2 -4
- package/package.json +7 -2
- package/scripts/extension-goldens.ts +23 -0
- package/scripts/generate-api-signatures.ts +72 -8
- package/scripts/materialize-version.ts +15 -1
- package/scripts/regen.ts +109 -27
- package/scripts/signature-store-fs.ts +1 -0
- package/scripts/sync-api-docs.ts +5 -61
- package/src/emit-dts.ts +359 -58
- package/src/index.ts +1 -0
- package/src/optional-correction-provenance.ts +13 -2
- package/src/render-overloads.d.ts +46 -0
- package/src/vmath-overloads.d.ts +19 -0
- package/generated/iac.d.ts +0 -18
- package/generated/iap.d.ts +0 -60
- package/generated/push.d.ts +0 -66
- package/generated/versions/defold-1.12.4/iac.d.ts +0 -18
- package/generated/versions/defold-1.12.4/iap.d.ts +0 -60
- package/generated/versions/defold-1.12.4/push.d.ts +0 -66
- package/generated/versions/defold-1.12.4/webview.d.ts +0 -71
- package/generated/versions/defold-1.13.0/iac.d.ts +0 -18
- package/generated/versions/defold-1.13.0/iap.d.ts +0 -60
- package/generated/versions/defold-1.13.0/push.d.ts +0 -66
- package/generated/versions/defold-1.13.0/webview.d.ts +0 -71
- package/generated/webview.d.ts +0 -71
- package/scripts/fidelity-audit.ts +0 -684
package/src/emit-dts.ts
CHANGED
|
@@ -151,11 +151,10 @@ export const ARBITRARY_TABLE_SLOTS = new Set([
|
|
|
151
151
|
"socket.dns.toip",
|
|
152
152
|
"socket.dns.getaddrinfo",
|
|
153
153
|
"socket.dns.getnameinfo",
|
|
154
|
-
// engine-built render-target tables: the "see the description" cross-
|
|
155
|
-
//
|
|
154
|
+
// engine-built render-target tables: the "see the description" cross-ref is
|
|
155
|
+
// dead (the function's own description is empty) or carries a typed
|
|
156
156
|
// `transient: table` field whose sub-doc is prose-only, so the nested table
|
|
157
157
|
// is opaque to the field-list parser and the whole-slot `Record` is faithful.
|
|
158
|
-
"render.render_target",
|
|
159
158
|
"render.set_render_target",
|
|
160
159
|
// reserved-but-undocumented options bag: `collectionproxy.load`'s `options`
|
|
161
160
|
// slot is documented verbatim as "options table, currently unused" and accepts
|
|
@@ -204,6 +203,10 @@ export const OVERLOAD_COVERED_SKIPS = new Set([
|
|
|
204
203
|
// msg-overloads.d.ts supplies the typed msg.url shape (0 / 1-string / 3-arg
|
|
205
204
|
// arities; the runtime-invalid two-arg form is intentionally omitted).
|
|
206
205
|
"msg.url",
|
|
206
|
+
// render-overloads.d.ts supplies the name-less and named render_target arities.
|
|
207
|
+
"render.render_target",
|
|
208
|
+
// vmath-overloads.d.ts supplies the vector3 and three-number euler_to_quat arities.
|
|
209
|
+
"vmath.euler_to_quat",
|
|
207
210
|
// vmath-overloads.d.ts supplies the generic covariant signatures.
|
|
208
211
|
"vmath.clamp",
|
|
209
212
|
"vmath.lerp",
|
|
@@ -219,7 +222,9 @@ export const OVERLOAD_COVERED_SKIPS = new Set([
|
|
|
219
222
|
// is the TSTL non-string-key Lua-table idiom and resolves the same way the
|
|
220
223
|
// already-emitted `LuaMultiReturn` does. The audit consults this map too, so the
|
|
221
224
|
// gate and the emitted surface stay coupled; a slot not curated here still
|
|
222
|
-
// surfaces under `recordTables` until a human adds it.
|
|
225
|
+
// surfaces under `recordTables` until a human adds it. Key and value are both
|
|
226
|
+
// `A | B` union tokens split per token, so a key whose doc names constants
|
|
227
|
+
// brands each of them rather than widening to `number`.
|
|
223
228
|
export const MAPPING_TABLE_SLOTS: ReadonlyMap<string, { key: string; value: string }> = new Map([
|
|
224
229
|
["gui.clone_tree", { key: "hash", value: "node" }],
|
|
225
230
|
["gui.get_tree", { key: "hash", value: "node" }],
|
|
@@ -351,6 +356,131 @@ export const OPTIONAL_SLOT_CORRECTIONS: ReadonlyMap<string, string> = new Map([
|
|
|
351
356
|
),
|
|
352
357
|
]);
|
|
353
358
|
|
|
359
|
+
export type ConstantSlotResolution =
|
|
360
|
+
| { readonly borrow: string }
|
|
361
|
+
| { readonly family: string; readonly evidence: string }
|
|
362
|
+
| { readonly residual: string };
|
|
363
|
+
|
|
364
|
+
// A top-level `constant` slot takes the constants its own doc names. This table
|
|
365
|
+
// fills only a slot whose own doc names none: a `borrow` reads another slot's doc
|
|
366
|
+
// (same key shape), a `family` expands a `<prefix>*` wildcard evidenced by the
|
|
367
|
+
// named element's example, and a `residual` records why nothing can brand it.
|
|
368
|
+
// Constants always come from the brand universe, never from a hand-typed list.
|
|
369
|
+
export const CONSTANT_SLOT_RESOLUTIONS: ReadonlyMap<string, ConstantSlotResolution> = new Map<
|
|
370
|
+
string,
|
|
371
|
+
ConstantSlotResolution
|
|
372
|
+
>([
|
|
373
|
+
["gui.get:param:property", { borrow: "gui.animate:param:property" }],
|
|
374
|
+
["gui.set:param:property", { borrow: "gui.animate:param:property" }],
|
|
375
|
+
["gui.cancel_animations:param:property", { borrow: "gui.animate:param:property" }],
|
|
376
|
+
[
|
|
377
|
+
"buffer.set_metadata:param:value_type",
|
|
378
|
+
{ family: "buffer.VALUE_TYPE_*", evidence: "buffer.set_metadata" },
|
|
379
|
+
],
|
|
380
|
+
[
|
|
381
|
+
"buffer.get_metadata:return:value_type",
|
|
382
|
+
{ family: "buffer.VALUE_TYPE_*", evidence: "buffer.set_metadata" },
|
|
383
|
+
],
|
|
384
|
+
["gui.new_texture:param:type", { residual: "the doc lists texture type strings only" }],
|
|
385
|
+
["gui.set_texture_data:param:type", { residual: "the doc lists texture type strings only" }],
|
|
386
|
+
[
|
|
387
|
+
"iap.get_provider_id:return:provider_id",
|
|
388
|
+
{ residual: "the doc names iap.PROVIDER_ID_*, which no ref-doc defines as a constant" },
|
|
389
|
+
],
|
|
390
|
+
]);
|
|
391
|
+
|
|
392
|
+
const DOC_CONSTANT_TOKEN =
|
|
393
|
+
/(?<![A-Za-z0-9_.])([a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*\.[A-Z][A-Z0-9_]*)(\*)?/g;
|
|
394
|
+
|
|
395
|
+
// The constant FQNs a piece of ref-doc text names, in doc order: exact tokens kept
|
|
396
|
+
// only when they are in `universe`, and each `<prefix>*` wildcard expanded to every
|
|
397
|
+
// universe FQN with that prefix, sorted.
|
|
398
|
+
export function documentedConstantTokens(text: string, universe: ReadonlySet<string>): string[] {
|
|
399
|
+
const out = new Set<string>();
|
|
400
|
+
for (const match of text.replace(/<[^>]*>/g, "").matchAll(DOC_CONSTANT_TOKEN)) {
|
|
401
|
+
const token = match[1] as string;
|
|
402
|
+
if (match[2] === undefined) {
|
|
403
|
+
if (universe.has(token)) out.add(token);
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
for (const fqn of [...universe].filter((candidate) => candidate.startsWith(token)).sort()) {
|
|
407
|
+
out.add(fqn);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return [...out];
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// The constants a `table` slot's doc names as the table's *keys*, as opposed to
|
|
414
|
+
// the far commoner shape where a field's *value* is a constant
|
|
415
|
+
// (`physics.get_shape`'s `type`, `resource.create_texture`'s `format`). Upstream
|
|
416
|
+
// marks the difference in prose — render.clear reads "table with keys specifying
|
|
417
|
+
// which buffers to clear ... Available keys are:" — so a key mention alongside a
|
|
418
|
+
// named constant is the discriminator. Empty means the slot has no documented
|
|
419
|
+
// constant keys, which is true of every `table` slot but one today.
|
|
420
|
+
export function documentedConstantKeyTokens(doc: string, universe: ReadonlySet<string>): string[] {
|
|
421
|
+
if (!/\bkeys?\b/i.test(doc.replace(/<[^>]*>/g, ""))) return [];
|
|
422
|
+
return documentedConstantTokens(doc, universe);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
type SlotDocLookup = (slotKey: string) => string | undefined;
|
|
426
|
+
|
|
427
|
+
function constantSlotTokens(
|
|
428
|
+
ownDoc: string,
|
|
429
|
+
slotKey: string,
|
|
430
|
+
lookupDoc: SlotDocLookup,
|
|
431
|
+
universe: ReadonlySet<string>,
|
|
432
|
+
): string[] {
|
|
433
|
+
const own = documentedConstantTokens(ownDoc, universe);
|
|
434
|
+
if (own.length > 0) return own;
|
|
435
|
+
const entry = CONSTANT_SLOT_RESOLUTIONS.get(slotKey);
|
|
436
|
+
if (entry === undefined || "residual" in entry) return [];
|
|
437
|
+
if ("borrow" in entry) return documentedConstantTokens(lookupDoc(entry.borrow) ?? "", universe);
|
|
438
|
+
return documentedConstantTokens(entry.family, universe);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function slotDocLookup(module: ApiModule): SlotDocLookup {
|
|
442
|
+
const docs = new Map<string, string>();
|
|
443
|
+
for (const fn of module.functions) {
|
|
444
|
+
for (const p of fn.parameters) {
|
|
445
|
+
const key = tableSlotKey(fn.name, "param", p.name);
|
|
446
|
+
if (!docs.has(key)) docs.set(key, p.doc);
|
|
447
|
+
}
|
|
448
|
+
for (const rv of fn.returnValues) {
|
|
449
|
+
const key = tableSlotKey(fn.name, "return", rv.name);
|
|
450
|
+
if (!docs.has(key)) docs.set(key, rv.doc);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return (slotKey) => docs.get(slotKey);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// The constants a module's top-level `constant` slot expands into: its own doc
|
|
457
|
+
// first, then its CONSTANT_SLOT_RESOLUTIONS entry. Empty means the slot keeps
|
|
458
|
+
// `Opaque<"constant">`.
|
|
459
|
+
export function resolveConstantSlotTokens(
|
|
460
|
+
module: ApiModule,
|
|
461
|
+
slotKey: string,
|
|
462
|
+
universe: ReadonlySet<string>,
|
|
463
|
+
): string[] {
|
|
464
|
+
const lookupDoc = slotDocLookup(module);
|
|
465
|
+
return constantSlotTokens(lookupDoc(slotKey) ?? "", slotKey, lookupDoc, universe);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
type ConstantSlotTokens = (
|
|
469
|
+
elementName: string,
|
|
470
|
+
slotKind: "param" | "return",
|
|
471
|
+
slotName: string,
|
|
472
|
+
doc: string,
|
|
473
|
+
) => readonly string[];
|
|
474
|
+
|
|
475
|
+
function constantSlotTokenResolver(
|
|
476
|
+
module: ApiModule,
|
|
477
|
+
universe: ReadonlySet<string>,
|
|
478
|
+
): ConstantSlotTokens {
|
|
479
|
+
const lookupDoc = slotDocLookup(module);
|
|
480
|
+
return (elementName, slotKind, slotName, doc) =>
|
|
481
|
+
constantSlotTokens(doc, tableSlotKey(elementName, slotKind, slotName), lookupDoc, universe);
|
|
482
|
+
}
|
|
483
|
+
|
|
354
484
|
// FQN-keyed allowlist of the `types.is_*` checks that genuinely narrow their
|
|
355
485
|
// argument, mapped to the `DEFOLD_TYPE_MAP` token whose interface they prove.
|
|
356
486
|
// Emitting these as user-defined type guards (`var_ is Vector3`) is the only way
|
|
@@ -714,11 +844,20 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
714
844
|
// render.predicate's `tags` is "table of tags ... can be of either hash or
|
|
715
845
|
// string type", a homogeneous array whose element is the string|hash union.
|
|
716
846
|
["render.predicate:param:tags", { kind: "array", element: ["string", "hash"] }],
|
|
717
|
-
// render.clear's `buffers` maps the
|
|
718
|
-
// their clear value: vector4 for the color buffer, number for
|
|
719
|
-
//
|
|
720
|
-
//
|
|
721
|
-
|
|
847
|
+
// render.clear's `buffers` maps the graphics.BUFFER_TYPE_* constants its doc
|
|
848
|
+
// names as keys to their clear value: vector4 for the color buffer, number for
|
|
849
|
+
// depth/stencil. Both sides are union tokens the mapping string branch splits,
|
|
850
|
+
// so the key emits the three branded constants and the value `number | Vector4`.
|
|
851
|
+
// The key is branded rather than `number`, which is the narrowing users migrate
|
|
852
|
+
// to via the authored `render.ClearBufferKey` alias.
|
|
853
|
+
[
|
|
854
|
+
"render.clear:param:buffers",
|
|
855
|
+
{
|
|
856
|
+
kind: "mapping",
|
|
857
|
+
key: "graphics.BUFFER_TYPE_COLOR0_BIT | graphics.BUFFER_TYPE_DEPTH_BIT | graphics.BUFFER_TYPE_STENCIL_BIT",
|
|
858
|
+
value: "number | vector4",
|
|
859
|
+
},
|
|
860
|
+
],
|
|
722
861
|
// go.on_input and gui.on_input share the same well-known InputAction shape
|
|
723
862
|
// (the on_input function description lists the well-known fields: value,
|
|
724
863
|
// pressed, released, repeated, x/y/screen_x/screen_y/dx/dy/screen_dx/screen_dy,
|
|
@@ -1580,6 +1719,10 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
|
|
|
1580
1719
|
constantFqns.has(token) || knownConstantFqns?.has(token)
|
|
1581
1720
|
? brandType(token)
|
|
1582
1721
|
: baseMapType(token);
|
|
1722
|
+
const constantTokens = constantSlotTokenResolver(
|
|
1723
|
+
module,
|
|
1724
|
+
new Set([...constantFqns, ...(knownConstantFqns ?? [])]),
|
|
1725
|
+
);
|
|
1583
1726
|
|
|
1584
1727
|
const constants = module.constants
|
|
1585
1728
|
.map((c) => prepareConstant(c, prefix))
|
|
@@ -1656,7 +1799,9 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
|
|
|
1656
1799
|
for (const docLine of functionDocLines(fn.original, translations, handleIndent)) {
|
|
1657
1800
|
lines.push(docLine);
|
|
1658
1801
|
}
|
|
1659
|
-
lines.push(
|
|
1802
|
+
lines.push(
|
|
1803
|
+
`${handleIndent}${emitMethod(fn, mapType, resolver, constantTokens, urlParameters)}`,
|
|
1804
|
+
);
|
|
1660
1805
|
}
|
|
1661
1806
|
lines.push(`${INDENT}}`);
|
|
1662
1807
|
}
|
|
@@ -1685,7 +1830,7 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
|
|
|
1685
1830
|
const reserved = TS_RESERVED_NAMES.has(fn.name);
|
|
1686
1831
|
const emitName = aliasName(fn.name, aliases);
|
|
1687
1832
|
for (const docLine of functionDocLines(fn.original, translations)) lines.push(docLine);
|
|
1688
|
-
const line = emitFunction(fn, emitName, mapType, resolver, urlParameters);
|
|
1833
|
+
const line = emitFunction(fn, emitName, mapType, resolver, constantTokens, urlParameters);
|
|
1689
1834
|
lines.push(`${INDENT}${reserved ? "" : decl}${line}`);
|
|
1690
1835
|
}
|
|
1691
1836
|
|
|
@@ -1733,7 +1878,7 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
|
|
|
1733
1878
|
lines.push(docLine);
|
|
1734
1879
|
}
|
|
1735
1880
|
lines.push(
|
|
1736
|
-
`${bodyIndent}${reserved ? "" : segmentDecl}${emitFunction(fn, emitName, mapType, resolver, urlParameters)}`,
|
|
1881
|
+
`${bodyIndent}${reserved ? "" : segmentDecl}${emitFunction(fn, emitName, mapType, resolver, constantTokens, urlParameters)}`,
|
|
1737
1882
|
);
|
|
1738
1883
|
}
|
|
1739
1884
|
for (const alias of [...segmentAliases].sort((a, b) => a.public.localeCompare(b.public))) {
|
|
@@ -1761,10 +1906,25 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
|
|
|
1761
1906
|
return `${lines.join("\n")}\n`;
|
|
1762
1907
|
}
|
|
1763
1908
|
|
|
1909
|
+
/**
|
|
1910
|
+
* Every documented slot's rendered TS type, keyed `param|return:<position>:<raw
|
|
1911
|
+
* ref-doc name>`. The position is part of the key because a multi-return can
|
|
1912
|
+
* carry several unnamed slots (`push.schedule` renders two), which a name-only
|
|
1913
|
+
* key would collapse into one. Each value appears verbatim inside the symbol's
|
|
1914
|
+
* own `tsSignature`, so a consumer rendering a slot's type beside that signature
|
|
1915
|
+
* cannot contradict it.
|
|
1916
|
+
*/
|
|
1917
|
+
export type SlotTypes = Readonly<Record<string, string>>;
|
|
1918
|
+
|
|
1919
|
+
function slotKey(kind: "param" | "return", position: number, name: string): string {
|
|
1920
|
+
return `${kind}:${position}:${name}`;
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1764
1923
|
/** One emitted symbol's identity paired with its rendered TS signature text. */
|
|
1765
1924
|
export interface SymbolSignature {
|
|
1766
1925
|
readonly identity: ApiSymbolIdentity;
|
|
1767
1926
|
readonly tsSignature: string;
|
|
1927
|
+
readonly slotTypes: SlotTypes;
|
|
1768
1928
|
}
|
|
1769
1929
|
|
|
1770
1930
|
/**
|
|
@@ -1789,6 +1949,10 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1789
1949
|
constantFqns.has(token) || knownConstantFqns?.has(token)
|
|
1790
1950
|
? brandType(token)
|
|
1791
1951
|
: baseMapType(token);
|
|
1952
|
+
const constantTokens = constantSlotTokenResolver(
|
|
1953
|
+
module,
|
|
1954
|
+
new Set([...constantFqns, ...(knownConstantFqns ?? [])]),
|
|
1955
|
+
);
|
|
1792
1956
|
const resolver = buildTableDocResolver(
|
|
1793
1957
|
module.functions.map((fn) => ({
|
|
1794
1958
|
name: fn.name,
|
|
@@ -1805,9 +1969,14 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1805
1969
|
});
|
|
1806
1970
|
const emitName = (name: string): string => (TS_RESERVED_NAMES.has(name) ? `_${name}` : name);
|
|
1807
1971
|
|
|
1972
|
+
// A member with no call list (constant, variable, property, typedef) documents
|
|
1973
|
+
// no slot, so it reports an empty map rather than being absent from the ledger.
|
|
1974
|
+
const NO_SLOTS: SlotTypes = {};
|
|
1975
|
+
|
|
1808
1976
|
for (const fn of module.functions) {
|
|
1809
1977
|
const prepared = prepareFunction(fn, prefix);
|
|
1810
1978
|
if (prepared === null) continue;
|
|
1979
|
+
const slotTypes: Record<string, string> = {};
|
|
1811
1980
|
out.push({
|
|
1812
1981
|
identity: fnIdentity(fn),
|
|
1813
1982
|
tsSignature: emitFunction(
|
|
@@ -1815,8 +1984,11 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1815
1984
|
emitName(prepared.name),
|
|
1816
1985
|
mapType,
|
|
1817
1986
|
resolver,
|
|
1987
|
+
constantTokens,
|
|
1818
1988
|
urlParameters,
|
|
1989
|
+
slotTypes,
|
|
1819
1990
|
),
|
|
1991
|
+
slotTypes,
|
|
1820
1992
|
});
|
|
1821
1993
|
}
|
|
1822
1994
|
|
|
@@ -1830,21 +2002,41 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1830
2002
|
signature: "",
|
|
1831
2003
|
},
|
|
1832
2004
|
tsSignature: emitVariable(v, emitName(v.name), mapType),
|
|
2005
|
+
slotTypes: NO_SLOTS,
|
|
1833
2006
|
});
|
|
1834
2007
|
}
|
|
1835
2008
|
for (const fn of nested.functions) {
|
|
2009
|
+
const slotTypes: Record<string, string> = {};
|
|
1836
2010
|
out.push({
|
|
1837
2011
|
identity: fnIdentity(fn.original),
|
|
1838
|
-
tsSignature: emitFunction(
|
|
2012
|
+
tsSignature: emitFunction(
|
|
2013
|
+
fn,
|
|
2014
|
+
emitName(fn.name),
|
|
2015
|
+
mapType,
|
|
2016
|
+
resolver,
|
|
2017
|
+
constantTokens,
|
|
2018
|
+
urlParameters,
|
|
2019
|
+
slotTypes,
|
|
2020
|
+
),
|
|
2021
|
+
slotTypes,
|
|
1839
2022
|
});
|
|
1840
2023
|
}
|
|
1841
2024
|
|
|
1842
2025
|
const handleGroups = collectHandleMethodGroups(module);
|
|
1843
2026
|
for (const group of handleGroups.values()) {
|
|
1844
2027
|
for (const prepared of group) {
|
|
2028
|
+
const slotTypes: Record<string, string> = {};
|
|
1845
2029
|
out.push({
|
|
1846
2030
|
identity: fnIdentity(prepared.original),
|
|
1847
|
-
tsSignature: emitMethod(
|
|
2031
|
+
tsSignature: emitMethod(
|
|
2032
|
+
prepared,
|
|
2033
|
+
mapType,
|
|
2034
|
+
resolver,
|
|
2035
|
+
constantTokens,
|
|
2036
|
+
urlParameters,
|
|
2037
|
+
slotTypes,
|
|
2038
|
+
),
|
|
2039
|
+
slotTypes,
|
|
1848
2040
|
});
|
|
1849
2041
|
}
|
|
1850
2042
|
}
|
|
@@ -1855,6 +2047,7 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1855
2047
|
out.push({
|
|
1856
2048
|
identity: { namespace: module.namespace, kind: "CONSTANT", name: c.name, signature: "" },
|
|
1857
2049
|
tsSignature: `const ${prepared.name}: ${brandType(prepared.fqn)};`,
|
|
2050
|
+
slotTypes: NO_SLOTS,
|
|
1858
2051
|
});
|
|
1859
2052
|
}
|
|
1860
2053
|
|
|
@@ -1864,6 +2057,7 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1864
2057
|
out.push({
|
|
1865
2058
|
identity: { namespace: module.namespace, kind: "VARIABLE", name: v.name, signature: "" },
|
|
1866
2059
|
tsSignature: emitVariable(prepared, emitName(prepared.name), mapType),
|
|
2060
|
+
slotTypes: NO_SLOTS,
|
|
1867
2061
|
});
|
|
1868
2062
|
}
|
|
1869
2063
|
|
|
@@ -1871,6 +2065,7 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1871
2065
|
out.push({
|
|
1872
2066
|
identity: { namespace: module.namespace, kind: "PROPERTY", name: p.name, signature: "" },
|
|
1873
2067
|
tsSignature: emitPropertyMember(p, mapType, module.namespace),
|
|
2068
|
+
slotTypes: NO_SLOTS,
|
|
1874
2069
|
});
|
|
1875
2070
|
}
|
|
1876
2071
|
|
|
@@ -1879,6 +2074,7 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1879
2074
|
out.push({
|
|
1880
2075
|
identity: { namespace: module.namespace, kind: "TYPEDEF", name: t.name, signature: "" },
|
|
1881
2076
|
tsSignature: `type ${t.name} = Opaque<"${t.name}">;`,
|
|
2077
|
+
slotTypes: NO_SLOTS,
|
|
1882
2078
|
});
|
|
1883
2079
|
}
|
|
1884
2080
|
|
|
@@ -2072,32 +2268,61 @@ function memberSignature(
|
|
|
2072
2268
|
name: string,
|
|
2073
2269
|
mapType: (t: string) => string,
|
|
2074
2270
|
resolver: TableDocResolver,
|
|
2271
|
+
constantTokens: ConstantSlotTokens,
|
|
2075
2272
|
urlParameters: UrlParameterTable,
|
|
2273
|
+
slotSink?: Record<string, string>,
|
|
2076
2274
|
): string {
|
|
2077
2275
|
const original = prepared.original.parameters;
|
|
2078
2276
|
const elementName = prepared.original.name;
|
|
2079
2277
|
const cutoff = trailingOptionalCutoff(original, elementName);
|
|
2080
2278
|
const varargIndex = original.findIndex(isVarargParameter);
|
|
2081
2279
|
const positional = (varargIndex === -1 ? original : original.slice(0, varargIndex)).map((p, i) =>
|
|
2082
|
-
emitParameter(p, i, i >= cutoff, mapType, resolver, elementName, urlParameters),
|
|
2280
|
+
emitParameter(p, i, i >= cutoff, mapType, resolver, constantTokens, elementName, urlParameters),
|
|
2083
2281
|
);
|
|
2084
|
-
const
|
|
2282
|
+
const rest =
|
|
2085
2283
|
varargIndex === -1
|
|
2086
|
-
?
|
|
2087
|
-
:
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2284
|
+
? null
|
|
2285
|
+
: emitRestParameter(
|
|
2286
|
+
original,
|
|
2287
|
+
varargIndex,
|
|
2288
|
+
mapType,
|
|
2289
|
+
resolver,
|
|
2290
|
+
constantTokens,
|
|
2291
|
+
elementName,
|
|
2292
|
+
urlParameters,
|
|
2293
|
+
);
|
|
2294
|
+
const params = [...positional.map((p) => p.text), ...(rest ? [rest.text] : [])].join(", ");
|
|
2295
|
+
const ret = emitReturn(
|
|
2296
|
+
prepared.original.returnValues,
|
|
2297
|
+
mapType,
|
|
2298
|
+
resolver,
|
|
2299
|
+
constantTokens,
|
|
2300
|
+
elementName,
|
|
2301
|
+
);
|
|
2093
2302
|
const predicateToken = TYPE_PREDICATES.get(elementName);
|
|
2094
2303
|
const soleParam = original[0];
|
|
2095
|
-
|
|
2304
|
+
const isPredicate =
|
|
2096
2305
|
predicateToken !== undefined &&
|
|
2097
2306
|
soleParam !== undefined &&
|
|
2098
2307
|
original.length === 1 &&
|
|
2099
|
-
ret.type === "boolean"
|
|
2100
|
-
) {
|
|
2308
|
+
ret.type === "boolean";
|
|
2309
|
+
if (slotSink !== undefined) {
|
|
2310
|
+
for (const [i, p] of positional.entries()) {
|
|
2311
|
+
const raw = original[i];
|
|
2312
|
+
if (raw) slotSink[slotKey("param", i, raw.name)] = p.ts;
|
|
2313
|
+
}
|
|
2314
|
+
for (const slot of rest?.slots ?? []) {
|
|
2315
|
+
slotSink[slotKey("param", slot.position, slot.name)] = slot.ts;
|
|
2316
|
+
}
|
|
2317
|
+
// A predicate renders `arg is T` in place of the mapped `boolean`, so its
|
|
2318
|
+
// return slot has no text in the signature to stand on and is not reported.
|
|
2319
|
+
if (!isPredicate) {
|
|
2320
|
+
for (const slot of ret.slots) {
|
|
2321
|
+
slotSink[slotKey("return", slot.position, slot.name)] = slot.ts;
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
if (isPredicate && soleParam !== undefined && predicateToken !== undefined) {
|
|
2101
2326
|
return `${name}(${params}): ${safeParamName(soleParam.name, 0)} is ${mapType(predicateToken)};`;
|
|
2102
2327
|
}
|
|
2103
2328
|
return `${name}(${params}): ${ret.type};${ret.trailing}`;
|
|
@@ -2108,9 +2333,11 @@ function emitFunction(
|
|
|
2108
2333
|
name: string,
|
|
2109
2334
|
mapType: (t: string) => string,
|
|
2110
2335
|
resolver: TableDocResolver,
|
|
2336
|
+
constantTokens: ConstantSlotTokens,
|
|
2111
2337
|
urlParameters: UrlParameterTable,
|
|
2338
|
+
slotSink?: Record<string, string>,
|
|
2112
2339
|
): string {
|
|
2113
|
-
return `function ${memberSignature(prepared, name, mapType, resolver, urlParameters)}`;
|
|
2340
|
+
return `function ${memberSignature(prepared, name, mapType, resolver, constantTokens, urlParameters, slotSink)}`;
|
|
2114
2341
|
}
|
|
2115
2342
|
|
|
2116
2343
|
// A colon-method member of a handle interface: identical signature machinery to a
|
|
@@ -2120,9 +2347,19 @@ function emitMethod(
|
|
|
2120
2347
|
prepared: PreparedFunction,
|
|
2121
2348
|
mapType: (t: string) => string,
|
|
2122
2349
|
resolver: TableDocResolver,
|
|
2350
|
+
constantTokens: ConstantSlotTokens,
|
|
2123
2351
|
urlParameters: UrlParameterTable,
|
|
2352
|
+
slotSink?: Record<string, string>,
|
|
2124
2353
|
): string {
|
|
2125
|
-
return memberSignature(
|
|
2354
|
+
return memberSignature(
|
|
2355
|
+
prepared,
|
|
2356
|
+
prepared.name,
|
|
2357
|
+
mapType,
|
|
2358
|
+
resolver,
|
|
2359
|
+
constantTokens,
|
|
2360
|
+
urlParameters,
|
|
2361
|
+
slotSink,
|
|
2362
|
+
);
|
|
2126
2363
|
}
|
|
2127
2364
|
|
|
2128
2365
|
// Build the indented JSDoc lines for a function from its ref-doc prose. The
|
|
@@ -2238,6 +2475,7 @@ function parameterType(
|
|
|
2238
2475
|
p: ApiParameter,
|
|
2239
2476
|
mapType: (t: string) => string,
|
|
2240
2477
|
resolver: TableDocResolver,
|
|
2478
|
+
constantTokens: ConstantSlotTokens,
|
|
2241
2479
|
elementName: string,
|
|
2242
2480
|
urlParameters: UrlParameterTable,
|
|
2243
2481
|
): string {
|
|
@@ -2247,13 +2485,25 @@ function parameterType(
|
|
|
2247
2485
|
const alias = SCENE_ADDRESS_ALIASES[classifyUrlParameter(urlParameters, elementName, p.name)];
|
|
2248
2486
|
const slotMapType = alias === undefined ? mapType : addressMapType(mapType, alias);
|
|
2249
2487
|
return concrete.length > 0
|
|
2250
|
-
? mapSlotUnion(
|
|
2488
|
+
? mapSlotUnion(
|
|
2489
|
+
concrete,
|
|
2490
|
+
p.doc,
|
|
2491
|
+
slotMapType,
|
|
2492
|
+
true,
|
|
2493
|
+
resolver,
|
|
2494
|
+
constantTokens,
|
|
2495
|
+
elementName,
|
|
2496
|
+
"param",
|
|
2497
|
+
p.name,
|
|
2498
|
+
)
|
|
2251
2499
|
: "unknown";
|
|
2252
2500
|
}
|
|
2253
2501
|
|
|
2254
2502
|
const VARARG_PREFIX = "...";
|
|
2255
2503
|
|
|
2256
|
-
|
|
2504
|
+
// Exported because the fidelity audit must ask the same question about the
|
|
2505
|
+
// same input: what the emitted declaration accepts is the emitter's to decide.
|
|
2506
|
+
export function isVarargParameter(p: ApiParameter): boolean {
|
|
2257
2507
|
return p.name.startsWith(VARARG_PREFIX);
|
|
2258
2508
|
}
|
|
2259
2509
|
|
|
@@ -2277,21 +2527,32 @@ function emitRestParameter(
|
|
|
2277
2527
|
varargIndex: number,
|
|
2278
2528
|
mapType: (t: string) => string,
|
|
2279
2529
|
resolver: TableDocResolver,
|
|
2530
|
+
constantTokens: ConstantSlotTokens,
|
|
2280
2531
|
elementName: string,
|
|
2281
2532
|
urlParameters: UrlParameterTable,
|
|
2282
|
-
): string {
|
|
2533
|
+
): { text: string; slots: EmittedSlot[] } {
|
|
2283
2534
|
const vararg = params[varargIndex];
|
|
2284
|
-
if (vararg === undefined) return "";
|
|
2285
|
-
const
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
];
|
|
2535
|
+
if (vararg === undefined) return { text: "", slots: [] };
|
|
2536
|
+
const folded = params.slice(varargIndex).map((p, offset) => ({
|
|
2537
|
+
position: varargIndex + offset,
|
|
2538
|
+
name: p.name,
|
|
2539
|
+
ts: parameterType(p, mapType, resolver, constantTokens, elementName, urlParameters),
|
|
2540
|
+
}));
|
|
2541
|
+
const members = [...new Set(folded.map((slot) => slot.ts))];
|
|
2292
2542
|
const first = members[0] ?? "unknown";
|
|
2293
2543
|
const element = members.length > 1 ? `(${members.join(" | ")})` : first;
|
|
2294
|
-
return
|
|
2544
|
+
return {
|
|
2545
|
+
text: `...${varargParamName(vararg.name, varargIndex)}: ${element}[]`,
|
|
2546
|
+
slots: folded,
|
|
2547
|
+
};
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
// A slot's rendered type paired with the position and raw ref-doc name that
|
|
2551
|
+
// identify it, so the signature text and the per-slot map come out of one call.
|
|
2552
|
+
interface EmittedSlot {
|
|
2553
|
+
readonly position: number;
|
|
2554
|
+
readonly name: string;
|
|
2555
|
+
readonly ts: string;
|
|
2295
2556
|
}
|
|
2296
2557
|
|
|
2297
2558
|
function emitParameter(
|
|
@@ -2300,41 +2561,62 @@ function emitParameter(
|
|
|
2300
2561
|
optional: boolean,
|
|
2301
2562
|
mapType: (t: string) => string,
|
|
2302
2563
|
resolver: TableDocResolver,
|
|
2564
|
+
constantTokens: ConstantSlotTokens,
|
|
2303
2565
|
elementName: string,
|
|
2304
2566
|
urlParameters: UrlParameterTable,
|
|
2305
|
-
): string {
|
|
2567
|
+
): { text: string; ts: string } {
|
|
2306
2568
|
const name = safeParamName(p.name, index);
|
|
2307
|
-
const ts = parameterType(p, mapType, resolver, elementName, urlParameters);
|
|
2569
|
+
const ts = parameterType(p, mapType, resolver, constantTokens, elementName, urlParameters);
|
|
2308
2570
|
// An interior doc-optional param (a required param follows, so the trailing-`?`
|
|
2309
2571
|
// projection cannot mark it) keeps its optionality as `| undefined` — TSTL
|
|
2310
2572
|
// lowers `undefined` to `nil`, the faithful call. Trailing optionals keep the
|
|
2311
2573
|
// `?` form; required params are untouched.
|
|
2312
2574
|
const interiorOptional = !optional && isDocOptional(p, elementName) ? " | undefined" : "";
|
|
2313
|
-
return `${name}${optional ? "?" : ""}: ${ts}${interiorOptional}
|
|
2575
|
+
return { text: `${name}${optional ? "?" : ""}: ${ts}${interiorOptional}`, ts };
|
|
2314
2576
|
}
|
|
2315
2577
|
|
|
2316
2578
|
function emitReturn(
|
|
2317
2579
|
returnValues: ApiParameter[],
|
|
2318
2580
|
mapType: (t: string) => string,
|
|
2319
2581
|
resolver: TableDocResolver,
|
|
2582
|
+
constantTokens: ConstantSlotTokens,
|
|
2320
2583
|
elementName: string,
|
|
2321
|
-
): { type: string; trailing: string } {
|
|
2584
|
+
): { type: string; trailing: string; slots: EmittedSlot[] } {
|
|
2585
|
+
// An authored whole-return override is not a per-slot render, so it reports no
|
|
2586
|
+
// slots and leaves the consumer on its token-derived fallback.
|
|
2322
2587
|
const override = RETURN_TYPE_OVERRIDES.get(elementName);
|
|
2323
|
-
if (override !== undefined) return { type: override, trailing: "" };
|
|
2324
|
-
if (returnValues.length === 0) return { type: "void", trailing: "" };
|
|
2588
|
+
if (override !== undefined) return { type: override, trailing: "", slots: [] };
|
|
2589
|
+
if (returnValues.length === 0) return { type: "void", trailing: "", slots: [] };
|
|
2325
2590
|
if (returnValues.length > 1) {
|
|
2326
2591
|
// Defold multi-returns are positional and always present; each slot maps
|
|
2327
2592
|
// straight through (unknown when the doc lists no type) into a tuple that
|
|
2328
2593
|
// typescript-to-lua erases to `local a, b = fn()`.
|
|
2329
|
-
const
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2594
|
+
const emitted = returnValues.map((rv, index) => ({
|
|
2595
|
+
position: index,
|
|
2596
|
+
name: rv.name,
|
|
2597
|
+
ts:
|
|
2598
|
+
rv.types.length > 0
|
|
2599
|
+
? mapSlotUnion(
|
|
2600
|
+
rv.types,
|
|
2601
|
+
rv.doc,
|
|
2602
|
+
mapType,
|
|
2603
|
+
false,
|
|
2604
|
+
resolver,
|
|
2605
|
+
constantTokens,
|
|
2606
|
+
elementName,
|
|
2607
|
+
"return",
|
|
2608
|
+
rv.name,
|
|
2609
|
+
)
|
|
2610
|
+
: "unknown",
|
|
2611
|
+
}));
|
|
2612
|
+
return {
|
|
2613
|
+
type: `LuaMultiReturn<[${emitted.map((slot) => slot.ts).join(", ")}]>`,
|
|
2614
|
+
trailing: "",
|
|
2615
|
+
slots: emitted,
|
|
2616
|
+
};
|
|
2335
2617
|
}
|
|
2336
2618
|
const first = returnValues[0];
|
|
2337
|
-
if (!first) return { type: "void", trailing: "" };
|
|
2619
|
+
if (!first) return { type: "void", trailing: "", slots: [] };
|
|
2338
2620
|
const ts =
|
|
2339
2621
|
first.types.length > 0
|
|
2340
2622
|
? mapSlotUnion(
|
|
@@ -2343,12 +2625,13 @@ function emitReturn(
|
|
|
2343
2625
|
mapType,
|
|
2344
2626
|
false,
|
|
2345
2627
|
resolver,
|
|
2628
|
+
constantTokens,
|
|
2346
2629
|
elementName,
|
|
2347
2630
|
"return",
|
|
2348
2631
|
first.name,
|
|
2349
2632
|
)
|
|
2350
2633
|
: "unknown";
|
|
2351
|
-
return { type: ts, trailing: "" };
|
|
2634
|
+
return { type: ts, trailing: "", slots: [{ position: 0, name: first.name, ts }] };
|
|
2352
2635
|
}
|
|
2353
2636
|
|
|
2354
2637
|
function emitPropertyMember(
|
|
@@ -2380,13 +2663,26 @@ function mapSlotUnion(
|
|
|
2380
2663
|
mapType: (t: string) => string,
|
|
2381
2664
|
optionalFields: boolean,
|
|
2382
2665
|
resolver: TableDocResolver,
|
|
2666
|
+
constantTokens: ConstantSlotTokens,
|
|
2383
2667
|
elementName: string,
|
|
2384
2668
|
slotKind?: "param" | "return",
|
|
2385
2669
|
slotName?: string,
|
|
2386
2670
|
): string {
|
|
2387
2671
|
const mapped: string[] = [];
|
|
2388
2672
|
const seen = new Set<string>();
|
|
2673
|
+
const push = (ts: string): void => {
|
|
2674
|
+
if (seen.has(ts)) return;
|
|
2675
|
+
seen.add(ts);
|
|
2676
|
+
mapped.push(ts);
|
|
2677
|
+
};
|
|
2389
2678
|
for (const token of types) {
|
|
2679
|
+
if (token === "constant" && slotKind !== undefined && slotName !== undefined) {
|
|
2680
|
+
const expanded = constantTokens(elementName, slotKind, slotName, doc);
|
|
2681
|
+
if (expanded.length > 0) {
|
|
2682
|
+
for (const fqn of expanded) push(mapType(fqn));
|
|
2683
|
+
continue;
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2390
2686
|
let ts: string;
|
|
2391
2687
|
if (token === "table") {
|
|
2392
2688
|
const curation =
|
|
@@ -2413,7 +2709,14 @@ function mapSlotUnion(
|
|
|
2413
2709
|
const nested = mapping.value as NestedMapping;
|
|
2414
2710
|
value = `LuaMap<${mapType(nested.key)}, ${mapType(nested.value)}>`;
|
|
2415
2711
|
}
|
|
2416
|
-
|
|
2712
|
+
// A mapping key may be a `A | B` union token too (render.clear's three
|
|
2713
|
+
// documented graphics.BUFFER_TYPE_* constants); split it the same way
|
|
2714
|
+
// the value above is split. A single-token key (no `|`) is unaffected.
|
|
2715
|
+
const key = unionFromTokens(
|
|
2716
|
+
mapping.key.split("|").map((token) => token.trim()),
|
|
2717
|
+
mapType,
|
|
2718
|
+
);
|
|
2719
|
+
ts = `LuaMap<${key}, ${value}>`;
|
|
2417
2720
|
} else if (element !== undefined) {
|
|
2418
2721
|
ts = arrayTypeFromTokens(element, mapType);
|
|
2419
2722
|
} else if (curation?.kind === "object" || curation?.kind === "array-object") {
|
|
@@ -2457,9 +2760,7 @@ function mapSlotUnion(
|
|
|
2457
2760
|
: undefined;
|
|
2458
2761
|
ts = curated ?? mapType(token);
|
|
2459
2762
|
}
|
|
2460
|
-
|
|
2461
|
-
seen.add(ts);
|
|
2462
|
-
mapped.push(ts);
|
|
2763
|
+
push(ts);
|
|
2463
2764
|
}
|
|
2464
2765
|
// A genuine `unknown` member (an `any` slot, including `any | nil`) absorbs
|
|
2465
2766
|
// every other union member, so collapse to exactly `unknown` rather than emit
|