@defold-typescript/types 0.35.0 → 0.37.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 +9406 -18
- package/examples/translations.json +11 -11
- package/generated/b2d.d.ts +1 -1
- package/generated/b2d_body.d.ts +6 -4
- package/generated/b2d_fixture.d.ts +6 -4
- package/generated/b2d_shape.d.ts +6 -4
- package/generated/factory.d.ts +11 -10
- package/generated/render.d.ts +5 -5
- package/generated/resource.d.ts +4 -4
- package/generated/sys.d.ts +2 -2
- package/generated/versions/defold-1.12.4/b2d.d.ts +1 -1
- package/generated/versions/defold-1.12.4/factory.d.ts +11 -10
- package/generated/versions/defold-1.12.4/render.d.ts +5 -5
- package/generated/versions/defold-1.12.4/resource.d.ts +4 -4
- package/generated/versions/defold-1.12.4/sys.d.ts +2 -2
- package/generated/versions/defold-1.12.4/window.d.ts +1 -1
- package/generated/versions/defold-1.12.4/zlib.d.ts +2 -1
- package/generated/versions/defold-1.13.0/b2d.d.ts +1 -1
- package/generated/versions/defold-1.13.0/b2d_body.d.ts +6 -4
- package/generated/versions/defold-1.13.0/b2d_fixture.d.ts +6 -4
- package/generated/versions/defold-1.13.0/b2d_shape.d.ts +6 -4
- package/generated/versions/defold-1.13.0/factory.d.ts +11 -10
- package/generated/versions/defold-1.13.0/render.d.ts +5 -5
- package/generated/versions/defold-1.13.0/resource.d.ts +4 -4
- package/generated/versions/defold-1.13.0/sys.d.ts +2 -2
- package/generated/versions/defold-1.13.0/window.d.ts +1 -1
- package/generated/versions/defold-1.13.0/zlib.d.ts +2 -1
- package/generated/window.d.ts +1 -1
- package/generated/zlib.d.ts +2 -1
- package/index.d.ts +1 -0
- package/package.json +5 -2
- package/scripts/generate-api-signatures.ts +33 -8
- package/src/emit-dts.ts +355 -71
- package/src/index.ts +1 -0
- package/src/render-overloads.d.ts +17 -0
- package/src/return-correction-provenance.ts +306 -0
package/src/emit-dts.ts
CHANGED
|
@@ -222,7 +222,9 @@ export const OVERLOAD_COVERED_SKIPS = new Set([
|
|
|
222
222
|
// is the TSTL non-string-key Lua-table idiom and resolves the same way the
|
|
223
223
|
// already-emitted `LuaMultiReturn` does. The audit consults this map too, so the
|
|
224
224
|
// gate and the emitted surface stay coupled; a slot not curated here still
|
|
225
|
-
// 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`.
|
|
226
228
|
export const MAPPING_TABLE_SLOTS: ReadonlyMap<string, { key: string; value: string }> = new Map([
|
|
227
229
|
["gui.clone_tree", { key: "hash", value: "node" }],
|
|
228
230
|
["gui.get_tree", { key: "hash", value: "node" }],
|
|
@@ -243,6 +245,32 @@ export const RETURN_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
|
|
|
243
245
|
["editor.tx.add", 'Opaque<"transaction_step">'],
|
|
244
246
|
]);
|
|
245
247
|
|
|
248
|
+
// A function whose `returnvalues` declare a concrete type while the same slot's
|
|
249
|
+
// prose documents a `nil` alternative. Distinct from RETURN_TYPE_OVERRIDES
|
|
250
|
+
// above, which fills an *empty* `returnvalues`: these contradict a token
|
|
251
|
+
// upstream explicitly declares, so each entry records it and
|
|
252
|
+
// `return-correction-provenance.test.ts` pins it against every vendored
|
|
253
|
+
// ref-doc. Upstream adding `nil` to `types` hands the case to the top-level
|
|
254
|
+
// `nil` projection in `mapSlotUnion` and reds the entry, which is then deleted
|
|
255
|
+
// rather than re-pinned.
|
|
256
|
+
export interface ReturnTypeCorrection {
|
|
257
|
+
readonly ts: string;
|
|
258
|
+
readonly upstream: readonly string[];
|
|
259
|
+
readonly reason: string;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Keyed by FQN, mirroring PROPERTY_TYPE_CORRECTIONS.
|
|
263
|
+
export const RETURN_TYPE_CORRECTIONS: ReadonlyMap<string, ReturnTypeCorrection> = new Map([
|
|
264
|
+
[
|
|
265
|
+
"b2d.get_body",
|
|
266
|
+
{
|
|
267
|
+
ts: 'Opaque<"b2Body"> | undefined',
|
|
268
|
+
upstream: ["b2Body"],
|
|
269
|
+
reason: "the body if successful. Otherwise nil.",
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
]);
|
|
273
|
+
|
|
246
274
|
// A property whose upstream `<span class="type">` states a type the engine does
|
|
247
275
|
// not use. Unlike every other override here, which fills a gap upstream left
|
|
248
276
|
// empty, these contradict a token upstream explicitly declares — so each entry
|
|
@@ -408,6 +436,18 @@ export function documentedConstantTokens(text: string, universe: ReadonlySet<str
|
|
|
408
436
|
return [...out];
|
|
409
437
|
}
|
|
410
438
|
|
|
439
|
+
// The constants a `table` slot's doc names as the table's *keys*, as opposed to
|
|
440
|
+
// the far commoner shape where a field's *value* is a constant
|
|
441
|
+
// (`physics.get_shape`'s `type`, `resource.create_texture`'s `format`). Upstream
|
|
442
|
+
// marks the difference in prose — render.clear reads "table with keys specifying
|
|
443
|
+
// which buffers to clear ... Available keys are:" — so a key mention alongside a
|
|
444
|
+
// named constant is the discriminator. Empty means the slot has no documented
|
|
445
|
+
// constant keys, which is true of every `table` slot but one today.
|
|
446
|
+
export function documentedConstantKeyTokens(doc: string, universe: ReadonlySet<string>): string[] {
|
|
447
|
+
if (!/\bkeys?\b/i.test(doc.replace(/<[^>]*>/g, ""))) return [];
|
|
448
|
+
return documentedConstantTokens(doc, universe);
|
|
449
|
+
}
|
|
450
|
+
|
|
411
451
|
type SlotDocLookup = (slotKey: string) => string | undefined;
|
|
412
452
|
|
|
413
453
|
function constantSlotTokens(
|
|
@@ -805,6 +845,38 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
805
845
|
],
|
|
806
846
|
},
|
|
807
847
|
],
|
|
848
|
+
// resource.get_render_target_info's return `<dl>` writes one key as
|
|
849
|
+
// `<dt>'attachments'</dt>` — quoted rather than wrapped in `<code>` — so
|
|
850
|
+
// TABLE_FIELD drops it and parseTableFields hoists the seven per-attachment
|
|
851
|
+
// fields to the top level, contradicting the `info.attachments[0].handle`
|
|
852
|
+
// access in the doc's own `@example`. Same shape and same remedy as
|
|
853
|
+
// font.get_info:return:info above: replace the mis-parsed field list with the
|
|
854
|
+
// faithful nested isList object. `texture` is documented "only available if
|
|
855
|
+
// the render target passed in is a resource", hence optional.
|
|
856
|
+
[
|
|
857
|
+
"resource.get_render_target_info:return:table",
|
|
858
|
+
{
|
|
859
|
+
kind: "object",
|
|
860
|
+
fields: [
|
|
861
|
+
{ name: "handle", types: ["number"] },
|
|
862
|
+
{
|
|
863
|
+
name: "attachments",
|
|
864
|
+
types: ["table"],
|
|
865
|
+
isList: true,
|
|
866
|
+
fields: [
|
|
867
|
+
{ name: "handle", types: ["number"] },
|
|
868
|
+
{ name: "width", types: ["number"] },
|
|
869
|
+
{ name: "height", types: ["number"] },
|
|
870
|
+
{ name: "depth", types: ["number"] },
|
|
871
|
+
{ name: "mipmaps", types: ["number"] },
|
|
872
|
+
{ name: "type", types: ["number"] },
|
|
873
|
+
{ name: "buffer_type", types: ["number"] },
|
|
874
|
+
{ name: "texture", types: ["hash"], optional: true },
|
|
875
|
+
],
|
|
876
|
+
},
|
|
877
|
+
],
|
|
878
|
+
},
|
|
879
|
+
],
|
|
808
880
|
// profiler.view_recorded_frame's `frame_index` is a typed `<ul>` "specify one
|
|
809
881
|
// of the following": `distance` and `frame`, both numeric frame offsets. The
|
|
810
882
|
// <li> items carry no `<span class="type">`, so parseTableFields recovers
|
|
@@ -830,11 +902,20 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
830
902
|
// render.predicate's `tags` is "table of tags ... can be of either hash or
|
|
831
903
|
// string type", a homogeneous array whose element is the string|hash union.
|
|
832
904
|
["render.predicate:param:tags", { kind: "array", element: ["string", "hash"] }],
|
|
833
|
-
// render.clear's `buffers` maps the
|
|
834
|
-
// their clear value: vector4 for the color buffer, number for
|
|
835
|
-
//
|
|
836
|
-
//
|
|
837
|
-
|
|
905
|
+
// render.clear's `buffers` maps the graphics.BUFFER_TYPE_* constants its doc
|
|
906
|
+
// names as keys to their clear value: vector4 for the color buffer, number for
|
|
907
|
+
// depth/stencil. Both sides are union tokens the mapping string branch splits,
|
|
908
|
+
// so the key emits the three branded constants and the value `number | Vector4`.
|
|
909
|
+
// The key is branded rather than `number`, which is the narrowing users migrate
|
|
910
|
+
// to via the authored `render.ClearBufferKey` alias.
|
|
911
|
+
[
|
|
912
|
+
"render.clear:param:buffers",
|
|
913
|
+
{
|
|
914
|
+
kind: "mapping",
|
|
915
|
+
key: "graphics.BUFFER_TYPE_COLOR0_BIT | graphics.BUFFER_TYPE_DEPTH_BIT | graphics.BUFFER_TYPE_STENCIL_BIT",
|
|
916
|
+
value: "number | vector4",
|
|
917
|
+
},
|
|
918
|
+
],
|
|
838
919
|
// go.on_input and gui.on_input share the same well-known InputAction shape
|
|
839
920
|
// (the on_input function description lists the well-known fields: value,
|
|
840
921
|
// pressed, released, repeated, x/y/screen_x/screen_y/dx/dy/screen_dx/screen_dy,
|
|
@@ -1346,6 +1427,95 @@ export const TABLE_FIELD_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
|
|
|
1346
1427
|
["material.get_vertex_attributes:return:table:value", ATTRIBUTE_VALUE_TS],
|
|
1347
1428
|
]);
|
|
1348
1429
|
|
|
1430
|
+
// A return-slot field upstream documents as platform-gated or nil-valued while
|
|
1431
|
+
// declaring it like any other. Lua removes a key assigned `nil`, so "might be
|
|
1432
|
+
// nil if not available" and "only available on iOS and Android" describe the
|
|
1433
|
+
// same runtime state — the key is absent — and the correction is always `?`,
|
|
1434
|
+
// never a required field widened to `| undefined`.
|
|
1435
|
+
//
|
|
1436
|
+
// Separate from TABLE_FIELD_TYPE_OVERRIDES because presence is the other axis:
|
|
1437
|
+
// the parser reads these fields' types correctly and stays authoritative for
|
|
1438
|
+
// them, so an entry here restates no type and cannot drift when upstream
|
|
1439
|
+
// retypes a field. `upstream` is pinned rather than consumed, and `evidence` is
|
|
1440
|
+
// the decoded prose `return-correction-provenance.test.ts` searches the slot doc
|
|
1441
|
+
// for; both red the entry once upstream marks the field or drops the gate.
|
|
1442
|
+
//
|
|
1443
|
+
// Keyed `<element>:<kind>:<slot>:<field>`, the TABLE_FIELD_TYPE_OVERRIDES shape.
|
|
1444
|
+
export interface ReturnFieldOptionalityCorrection {
|
|
1445
|
+
readonly upstream: readonly string[];
|
|
1446
|
+
readonly evidence: string;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
// The platform-gate markers upstream renders as `<span class="icon-*">` decode
|
|
1450
|
+
// to `[ios]`, `[android]`, `[html5]` — `user_agent` states its gate through the
|
|
1451
|
+
// marker alone, with no availability sentence to pin.
|
|
1452
|
+
export const RETURN_FIELD_OPTIONALITY_CORRECTIONS: ReadonlyMap<
|
|
1453
|
+
string,
|
|
1454
|
+
ReturnFieldOptionalityCorrection
|
|
1455
|
+
> = new Map([
|
|
1456
|
+
[
|
|
1457
|
+
"sys.get_sys_info:return:sys_info:device_model",
|
|
1458
|
+
{ upstream: ["string"], evidence: "[ios][android] Only available on iOS and Android." },
|
|
1459
|
+
],
|
|
1460
|
+
[
|
|
1461
|
+
"sys.get_sys_info:return:sys_info:manufacturer",
|
|
1462
|
+
{ upstream: ["string"], evidence: "[ios][android] Only available on iOS and Android." },
|
|
1463
|
+
],
|
|
1464
|
+
[
|
|
1465
|
+
"sys.get_sys_info:return:sys_info:device_ident",
|
|
1466
|
+
{
|
|
1467
|
+
upstream: ["string"],
|
|
1468
|
+
evidence: '[ios] "identifierForVendor" on iOS. [android] "android_id" on Android.',
|
|
1469
|
+
},
|
|
1470
|
+
],
|
|
1471
|
+
[
|
|
1472
|
+
"sys.get_sys_info:return:sys_info:user_agent",
|
|
1473
|
+
{ upstream: ["string"], evidence: "[html5] The HTTP user agent" },
|
|
1474
|
+
],
|
|
1475
|
+
[
|
|
1476
|
+
"sys.get_ifaddrs:return:ifaddrs:address",
|
|
1477
|
+
{ upstream: ["string"], evidence: "might be nil if not available." },
|
|
1478
|
+
],
|
|
1479
|
+
[
|
|
1480
|
+
"sys.get_ifaddrs:return:ifaddrs:mac",
|
|
1481
|
+
{ upstream: ["string"], evidence: "might be nil if not available." },
|
|
1482
|
+
],
|
|
1483
|
+
]);
|
|
1484
|
+
|
|
1485
|
+
// Mark a parser-recovered field optional from RETURN_FIELD_OPTIONALITY_CORRECTIONS,
|
|
1486
|
+
// leaving every other field — and every field's type — untouched. Sets the
|
|
1487
|
+
// `optional` flag `TableField` already carries and `inlineTableType` already
|
|
1488
|
+
// reads, so no `mapType` is needed and no correction can change a type.
|
|
1489
|
+
//
|
|
1490
|
+
// A correction keyed to this slot that matches no recovered field throws rather
|
|
1491
|
+
// than no-opping: a field upstream renamed would otherwise leave a stale entry
|
|
1492
|
+
// looking applied while the emitted declaration silently re-widened.
|
|
1493
|
+
// Returns a new array; never mutates the parser's result.
|
|
1494
|
+
export function applyFieldOptionalityCorrections(
|
|
1495
|
+
elementName: string,
|
|
1496
|
+
slotKind: "param" | "return" | undefined,
|
|
1497
|
+
slotName: string | undefined,
|
|
1498
|
+
fields: readonly TableField[],
|
|
1499
|
+
): TableField[] {
|
|
1500
|
+
if (slotKind === undefined || slotName === undefined) return [...fields];
|
|
1501
|
+
const prefix = `${tableSlotKey(elementName, slotKind, slotName)}:`;
|
|
1502
|
+
const corrected = fields.map((field) =>
|
|
1503
|
+
RETURN_FIELD_OPTIONALITY_CORRECTIONS.has(`${prefix}${field.name}`)
|
|
1504
|
+
? { ...field, optional: true }
|
|
1505
|
+
: field,
|
|
1506
|
+
);
|
|
1507
|
+
const recovered = new Set(fields.map((field) => field.name));
|
|
1508
|
+
const stale = [...RETURN_FIELD_OPTIONALITY_CORRECTIONS.keys()].filter(
|
|
1509
|
+
(key) => key.startsWith(prefix) && !recovered.has(key.slice(prefix.length)),
|
|
1510
|
+
);
|
|
1511
|
+
if (stale.length > 0) {
|
|
1512
|
+
throw new Error(
|
|
1513
|
+
`field-optionality correction names no recovered field: ${stale.sort().join(", ")}`,
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
return corrected;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1349
1519
|
// Pin a parser-recovered field's TS type from TABLE_FIELD_TYPE_OVERRIDES,
|
|
1350
1520
|
// leaving every other field parser-authoritative. A field the override does not
|
|
1351
1521
|
// name is returned unchanged; a field it names gains a `tsType` (emitted
|
|
@@ -1582,7 +1752,11 @@ export function buildTableDocResolver(sources: readonly TableDocSource[]): Table
|
|
|
1582
1752
|
return (elementName) => docByName.get(elementName);
|
|
1583
1753
|
}
|
|
1584
1754
|
|
|
1585
|
-
export function parseTableFields(
|
|
1755
|
+
export function parseTableFields(
|
|
1756
|
+
doc: string,
|
|
1757
|
+
resolver?: TableDocResolver,
|
|
1758
|
+
slotName?: string,
|
|
1759
|
+
): TableField[] | null {
|
|
1586
1760
|
const fields: TableField[] = [];
|
|
1587
1761
|
for (const match of doc.matchAll(TABLE_FIELD)) {
|
|
1588
1762
|
const name = (match[1] ?? "").trim();
|
|
@@ -1601,14 +1775,20 @@ export function parseTableFields(doc: string, resolver?: TableDocResolver): Tabl
|
|
|
1601
1775
|
}
|
|
1602
1776
|
// Nested recovery, scoped to the one unambiguous shape: a `<dl>` declaring a
|
|
1603
1777
|
// single `table`-typed field whose keys sit in an immediately-following
|
|
1604
|
-
// top-level `<ul>` typed-field list
|
|
1605
|
-
//
|
|
1606
|
-
//
|
|
1778
|
+
// top-level `<ul>` typed-field list. Attach the `<ul>` fields as that field's
|
|
1779
|
+
// nested shape so the slot recovers as a nested object instead of a `Record`.
|
|
1780
|
+
//
|
|
1781
|
+
// Unless the header restates the slot it describes (`window.get_safe_area`'s
|
|
1782
|
+
// `safe_area` return documented under a `safe_area` header): that is a
|
|
1783
|
+
// restatement, not a nesting level, and wrapping it emits a level the engine
|
|
1784
|
+
// never returns. Only a caller that knows the slot name can tell the two
|
|
1785
|
+
// apart, so the `<ul>` keys are returned directly when they match.
|
|
1607
1786
|
if (fields.length === 1) {
|
|
1608
1787
|
const only = fields[0];
|
|
1609
1788
|
if (only && only.types.length === 1 && only.types[0] === "table") {
|
|
1610
1789
|
const nested = parseUlFields(doc);
|
|
1611
1790
|
if (nested.length > 0) {
|
|
1791
|
+
if (slotName !== undefined && slotName === only.name) return nested;
|
|
1612
1792
|
only.fields = nested;
|
|
1613
1793
|
return [only];
|
|
1614
1794
|
}
|
|
@@ -1883,10 +2063,25 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
|
|
|
1883
2063
|
return `${lines.join("\n")}\n`;
|
|
1884
2064
|
}
|
|
1885
2065
|
|
|
2066
|
+
/**
|
|
2067
|
+
* Every documented slot's rendered TS type, keyed `param|return:<position>:<raw
|
|
2068
|
+
* ref-doc name>`. The position is part of the key because a multi-return can
|
|
2069
|
+
* carry several unnamed slots (`push.schedule` renders two), which a name-only
|
|
2070
|
+
* key would collapse into one. Each value appears verbatim inside the symbol's
|
|
2071
|
+
* own `tsSignature`, so a consumer rendering a slot's type beside that signature
|
|
2072
|
+
* cannot contradict it.
|
|
2073
|
+
*/
|
|
2074
|
+
export type SlotTypes = Readonly<Record<string, string>>;
|
|
2075
|
+
|
|
2076
|
+
function slotKey(kind: "param" | "return", position: number, name: string): string {
|
|
2077
|
+
return `${kind}:${position}:${name}`;
|
|
2078
|
+
}
|
|
2079
|
+
|
|
1886
2080
|
/** One emitted symbol's identity paired with its rendered TS signature text. */
|
|
1887
2081
|
export interface SymbolSignature {
|
|
1888
2082
|
readonly identity: ApiSymbolIdentity;
|
|
1889
2083
|
readonly tsSignature: string;
|
|
2084
|
+
readonly slotTypes: SlotTypes;
|
|
1890
2085
|
}
|
|
1891
2086
|
|
|
1892
2087
|
/**
|
|
@@ -1931,9 +2126,14 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1931
2126
|
});
|
|
1932
2127
|
const emitName = (name: string): string => (TS_RESERVED_NAMES.has(name) ? `_${name}` : name);
|
|
1933
2128
|
|
|
2129
|
+
// A member with no call list (constant, variable, property, typedef) documents
|
|
2130
|
+
// no slot, so it reports an empty map rather than being absent from the ledger.
|
|
2131
|
+
const NO_SLOTS: SlotTypes = {};
|
|
2132
|
+
|
|
1934
2133
|
for (const fn of module.functions) {
|
|
1935
2134
|
const prepared = prepareFunction(fn, prefix);
|
|
1936
2135
|
if (prepared === null) continue;
|
|
2136
|
+
const slotTypes: Record<string, string> = {};
|
|
1937
2137
|
out.push({
|
|
1938
2138
|
identity: fnIdentity(fn),
|
|
1939
2139
|
tsSignature: emitFunction(
|
|
@@ -1943,7 +2143,9 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1943
2143
|
resolver,
|
|
1944
2144
|
constantTokens,
|
|
1945
2145
|
urlParameters,
|
|
2146
|
+
slotTypes,
|
|
1946
2147
|
),
|
|
2148
|
+
slotTypes,
|
|
1947
2149
|
});
|
|
1948
2150
|
}
|
|
1949
2151
|
|
|
@@ -1957,9 +2159,11 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1957
2159
|
signature: "",
|
|
1958
2160
|
},
|
|
1959
2161
|
tsSignature: emitVariable(v, emitName(v.name), mapType),
|
|
2162
|
+
slotTypes: NO_SLOTS,
|
|
1960
2163
|
});
|
|
1961
2164
|
}
|
|
1962
2165
|
for (const fn of nested.functions) {
|
|
2166
|
+
const slotTypes: Record<string, string> = {};
|
|
1963
2167
|
out.push({
|
|
1964
2168
|
identity: fnIdentity(fn.original),
|
|
1965
2169
|
tsSignature: emitFunction(
|
|
@@ -1969,16 +2173,27 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1969
2173
|
resolver,
|
|
1970
2174
|
constantTokens,
|
|
1971
2175
|
urlParameters,
|
|
2176
|
+
slotTypes,
|
|
1972
2177
|
),
|
|
2178
|
+
slotTypes,
|
|
1973
2179
|
});
|
|
1974
2180
|
}
|
|
1975
2181
|
|
|
1976
2182
|
const handleGroups = collectHandleMethodGroups(module);
|
|
1977
2183
|
for (const group of handleGroups.values()) {
|
|
1978
2184
|
for (const prepared of group) {
|
|
2185
|
+
const slotTypes: Record<string, string> = {};
|
|
1979
2186
|
out.push({
|
|
1980
2187
|
identity: fnIdentity(prepared.original),
|
|
1981
|
-
tsSignature: emitMethod(
|
|
2188
|
+
tsSignature: emitMethod(
|
|
2189
|
+
prepared,
|
|
2190
|
+
mapType,
|
|
2191
|
+
resolver,
|
|
2192
|
+
constantTokens,
|
|
2193
|
+
urlParameters,
|
|
2194
|
+
slotTypes,
|
|
2195
|
+
),
|
|
2196
|
+
slotTypes,
|
|
1982
2197
|
});
|
|
1983
2198
|
}
|
|
1984
2199
|
}
|
|
@@ -1989,6 +2204,7 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1989
2204
|
out.push({
|
|
1990
2205
|
identity: { namespace: module.namespace, kind: "CONSTANT", name: c.name, signature: "" },
|
|
1991
2206
|
tsSignature: `const ${prepared.name}: ${brandType(prepared.fqn)};`,
|
|
2207
|
+
slotTypes: NO_SLOTS,
|
|
1992
2208
|
});
|
|
1993
2209
|
}
|
|
1994
2210
|
|
|
@@ -1998,6 +2214,7 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
1998
2214
|
out.push({
|
|
1999
2215
|
identity: { namespace: module.namespace, kind: "VARIABLE", name: v.name, signature: "" },
|
|
2000
2216
|
tsSignature: emitVariable(prepared, emitName(prepared.name), mapType),
|
|
2217
|
+
slotTypes: NO_SLOTS,
|
|
2001
2218
|
});
|
|
2002
2219
|
}
|
|
2003
2220
|
|
|
@@ -2005,6 +2222,7 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
2005
2222
|
out.push({
|
|
2006
2223
|
identity: { namespace: module.namespace, kind: "PROPERTY", name: p.name, signature: "" },
|
|
2007
2224
|
tsSignature: emitPropertyMember(p, mapType, module.namespace),
|
|
2225
|
+
slotTypes: NO_SLOTS,
|
|
2008
2226
|
});
|
|
2009
2227
|
}
|
|
2010
2228
|
|
|
@@ -2013,6 +2231,7 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
|
|
|
2013
2231
|
out.push({
|
|
2014
2232
|
identity: { namespace: module.namespace, kind: "TYPEDEF", name: t.name, signature: "" },
|
|
2015
2233
|
tsSignature: `type ${t.name} = Opaque<"${t.name}">;`,
|
|
2234
|
+
slotTypes: NO_SLOTS,
|
|
2016
2235
|
});
|
|
2017
2236
|
}
|
|
2018
2237
|
|
|
@@ -2208,6 +2427,7 @@ function memberSignature(
|
|
|
2208
2427
|
resolver: TableDocResolver,
|
|
2209
2428
|
constantTokens: ConstantSlotTokens,
|
|
2210
2429
|
urlParameters: UrlParameterTable,
|
|
2430
|
+
slotSink?: Record<string, string>,
|
|
2211
2431
|
): string {
|
|
2212
2432
|
const original = prepared.original.parameters;
|
|
2213
2433
|
const elementName = prepared.original.name;
|
|
@@ -2216,22 +2436,19 @@ function memberSignature(
|
|
|
2216
2436
|
const positional = (varargIndex === -1 ? original : original.slice(0, varargIndex)).map((p, i) =>
|
|
2217
2437
|
emitParameter(p, i, i >= cutoff, mapType, resolver, constantTokens, elementName, urlParameters),
|
|
2218
2438
|
);
|
|
2219
|
-
const
|
|
2439
|
+
const rest =
|
|
2220
2440
|
varargIndex === -1
|
|
2221
|
-
?
|
|
2222
|
-
:
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
),
|
|
2233
|
-
]
|
|
2234
|
-
).join(", ");
|
|
2441
|
+
? null
|
|
2442
|
+
: emitRestParameter(
|
|
2443
|
+
original,
|
|
2444
|
+
varargIndex,
|
|
2445
|
+
mapType,
|
|
2446
|
+
resolver,
|
|
2447
|
+
constantTokens,
|
|
2448
|
+
elementName,
|
|
2449
|
+
urlParameters,
|
|
2450
|
+
);
|
|
2451
|
+
const params = [...positional.map((p) => p.text), ...(rest ? [rest.text] : [])].join(", ");
|
|
2235
2452
|
const ret = emitReturn(
|
|
2236
2453
|
prepared.original.returnValues,
|
|
2237
2454
|
mapType,
|
|
@@ -2241,12 +2458,28 @@ function memberSignature(
|
|
|
2241
2458
|
);
|
|
2242
2459
|
const predicateToken = TYPE_PREDICATES.get(elementName);
|
|
2243
2460
|
const soleParam = original[0];
|
|
2244
|
-
|
|
2461
|
+
const isPredicate =
|
|
2245
2462
|
predicateToken !== undefined &&
|
|
2246
2463
|
soleParam !== undefined &&
|
|
2247
2464
|
original.length === 1 &&
|
|
2248
|
-
ret.type === "boolean"
|
|
2249
|
-
) {
|
|
2465
|
+
ret.type === "boolean";
|
|
2466
|
+
if (slotSink !== undefined) {
|
|
2467
|
+
for (const [i, p] of positional.entries()) {
|
|
2468
|
+
const raw = original[i];
|
|
2469
|
+
if (raw) slotSink[slotKey("param", i, raw.name)] = p.ts;
|
|
2470
|
+
}
|
|
2471
|
+
for (const slot of rest?.slots ?? []) {
|
|
2472
|
+
slotSink[slotKey("param", slot.position, slot.name)] = slot.ts;
|
|
2473
|
+
}
|
|
2474
|
+
// A predicate renders `arg is T` in place of the mapped `boolean`, so its
|
|
2475
|
+
// return slot has no text in the signature to stand on and is not reported.
|
|
2476
|
+
if (!isPredicate) {
|
|
2477
|
+
for (const slot of ret.slots) {
|
|
2478
|
+
slotSink[slotKey("return", slot.position, slot.name)] = slot.ts;
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
if (isPredicate && soleParam !== undefined && predicateToken !== undefined) {
|
|
2250
2483
|
return `${name}(${params}): ${safeParamName(soleParam.name, 0)} is ${mapType(predicateToken)};`;
|
|
2251
2484
|
}
|
|
2252
2485
|
return `${name}(${params}): ${ret.type};${ret.trailing}`;
|
|
@@ -2259,8 +2492,9 @@ function emitFunction(
|
|
|
2259
2492
|
resolver: TableDocResolver,
|
|
2260
2493
|
constantTokens: ConstantSlotTokens,
|
|
2261
2494
|
urlParameters: UrlParameterTable,
|
|
2495
|
+
slotSink?: Record<string, string>,
|
|
2262
2496
|
): string {
|
|
2263
|
-
return `function ${memberSignature(prepared, name, mapType, resolver, constantTokens, urlParameters)}`;
|
|
2497
|
+
return `function ${memberSignature(prepared, name, mapType, resolver, constantTokens, urlParameters, slotSink)}`;
|
|
2264
2498
|
}
|
|
2265
2499
|
|
|
2266
2500
|
// A colon-method member of a handle interface: identical signature machinery to a
|
|
@@ -2272,8 +2506,17 @@ function emitMethod(
|
|
|
2272
2506
|
resolver: TableDocResolver,
|
|
2273
2507
|
constantTokens: ConstantSlotTokens,
|
|
2274
2508
|
urlParameters: UrlParameterTable,
|
|
2509
|
+
slotSink?: Record<string, string>,
|
|
2275
2510
|
): string {
|
|
2276
|
-
return memberSignature(
|
|
2511
|
+
return memberSignature(
|
|
2512
|
+
prepared,
|
|
2513
|
+
prepared.name,
|
|
2514
|
+
mapType,
|
|
2515
|
+
resolver,
|
|
2516
|
+
constantTokens,
|
|
2517
|
+
urlParameters,
|
|
2518
|
+
slotSink,
|
|
2519
|
+
);
|
|
2277
2520
|
}
|
|
2278
2521
|
|
|
2279
2522
|
// Build the indented JSDoc lines for a function from its ref-doc prose. The
|
|
@@ -2444,21 +2687,29 @@ function emitRestParameter(
|
|
|
2444
2687
|
constantTokens: ConstantSlotTokens,
|
|
2445
2688
|
elementName: string,
|
|
2446
2689
|
urlParameters: UrlParameterTable,
|
|
2447
|
-
): string {
|
|
2690
|
+
): { text: string; slots: EmittedSlot[] } {
|
|
2448
2691
|
const vararg = params[varargIndex];
|
|
2449
|
-
if (vararg === undefined) return "";
|
|
2450
|
-
const
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
),
|
|
2457
|
-
),
|
|
2458
|
-
];
|
|
2692
|
+
if (vararg === undefined) return { text: "", slots: [] };
|
|
2693
|
+
const folded = params.slice(varargIndex).map((p, offset) => ({
|
|
2694
|
+
position: varargIndex + offset,
|
|
2695
|
+
name: p.name,
|
|
2696
|
+
ts: parameterType(p, mapType, resolver, constantTokens, elementName, urlParameters),
|
|
2697
|
+
}));
|
|
2698
|
+
const members = [...new Set(folded.map((slot) => slot.ts))];
|
|
2459
2699
|
const first = members[0] ?? "unknown";
|
|
2460
2700
|
const element = members.length > 1 ? `(${members.join(" | ")})` : first;
|
|
2461
|
-
return
|
|
2701
|
+
return {
|
|
2702
|
+
text: `...${varargParamName(vararg.name, varargIndex)}: ${element}[]`,
|
|
2703
|
+
slots: folded,
|
|
2704
|
+
};
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
// A slot's rendered type paired with the position and raw ref-doc name that
|
|
2708
|
+
// identify it, so the signature text and the per-slot map come out of one call.
|
|
2709
|
+
interface EmittedSlot {
|
|
2710
|
+
readonly position: number;
|
|
2711
|
+
readonly name: string;
|
|
2712
|
+
readonly ts: string;
|
|
2462
2713
|
}
|
|
2463
2714
|
|
|
2464
2715
|
function emitParameter(
|
|
@@ -2470,7 +2721,7 @@ function emitParameter(
|
|
|
2470
2721
|
constantTokens: ConstantSlotTokens,
|
|
2471
2722
|
elementName: string,
|
|
2472
2723
|
urlParameters: UrlParameterTable,
|
|
2473
|
-
): string {
|
|
2724
|
+
): { text: string; ts: string } {
|
|
2474
2725
|
const name = safeParamName(p.name, index);
|
|
2475
2726
|
const ts = parameterType(p, mapType, resolver, constantTokens, elementName, urlParameters);
|
|
2476
2727
|
// An interior doc-optional param (a required param follows, so the trailing-`?`
|
|
@@ -2478,7 +2729,7 @@ function emitParameter(
|
|
|
2478
2729
|
// lowers `undefined` to `nil`, the faithful call. Trailing optionals keep the
|
|
2479
2730
|
// `?` form; required params are untouched.
|
|
2480
2731
|
const interiorOptional = !optional && isDocOptional(p, elementName) ? " | undefined" : "";
|
|
2481
|
-
return `${name}${optional ? "?" : ""}: ${ts}${interiorOptional}
|
|
2732
|
+
return { text: `${name}${optional ? "?" : ""}: ${ts}${interiorOptional}`, ts };
|
|
2482
2733
|
}
|
|
2483
2734
|
|
|
2484
2735
|
function emitReturn(
|
|
@@ -2487,33 +2738,53 @@ function emitReturn(
|
|
|
2487
2738
|
resolver: TableDocResolver,
|
|
2488
2739
|
constantTokens: ConstantSlotTokens,
|
|
2489
2740
|
elementName: string,
|
|
2490
|
-
): { type: string; trailing: string } {
|
|
2741
|
+
): { type: string; trailing: string; slots: EmittedSlot[] } {
|
|
2742
|
+
// An authored whole-return override is not a per-slot render, so it reports no
|
|
2743
|
+
// slots and leaves the consumer on its token-derived fallback.
|
|
2491
2744
|
const override = RETURN_TYPE_OVERRIDES.get(elementName);
|
|
2492
|
-
if (override !== undefined) return { type: override, trailing: "" };
|
|
2493
|
-
|
|
2745
|
+
if (override !== undefined) return { type: override, trailing: "", slots: [] };
|
|
2746
|
+
// Kept beside the override lookup so the two stay visibly distinct: this one
|
|
2747
|
+
// contradicts a declared token rather than filling an empty `returnvalues`,
|
|
2748
|
+
// and it is a per-slot render, so it reports its slot.
|
|
2749
|
+
const correction = RETURN_TYPE_CORRECTIONS.get(elementName);
|
|
2750
|
+
if (returnValues.length === 0) return { type: "void", trailing: "", slots: [] };
|
|
2494
2751
|
if (returnValues.length > 1) {
|
|
2495
2752
|
// Defold multi-returns are positional and always present; each slot maps
|
|
2496
2753
|
// straight through (unknown when the doc lists no type) into a tuple that
|
|
2497
2754
|
// typescript-to-lua erases to `local a, b = fn()`.
|
|
2498
|
-
const
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2755
|
+
const emitted = returnValues.map((rv, index) => ({
|
|
2756
|
+
position: index,
|
|
2757
|
+
name: rv.name,
|
|
2758
|
+
ts:
|
|
2759
|
+
rv.types.length > 0
|
|
2760
|
+
? mapSlotUnion(
|
|
2761
|
+
rv.types,
|
|
2762
|
+
rv.doc,
|
|
2763
|
+
mapType,
|
|
2764
|
+
false,
|
|
2765
|
+
resolver,
|
|
2766
|
+
constantTokens,
|
|
2767
|
+
elementName,
|
|
2768
|
+
"return",
|
|
2769
|
+
rv.name,
|
|
2770
|
+
)
|
|
2771
|
+
: "unknown",
|
|
2772
|
+
}));
|
|
2773
|
+
return {
|
|
2774
|
+
type: `LuaMultiReturn<[${emitted.map((slot) => slot.ts).join(", ")}]>`,
|
|
2775
|
+
trailing: "",
|
|
2776
|
+
slots: emitted,
|
|
2777
|
+
};
|
|
2514
2778
|
}
|
|
2515
2779
|
const first = returnValues[0];
|
|
2516
|
-
if (!first) return { type: "void", trailing: "" };
|
|
2780
|
+
if (!first) return { type: "void", trailing: "", slots: [] };
|
|
2781
|
+
if (correction !== undefined) {
|
|
2782
|
+
return {
|
|
2783
|
+
type: correction.ts,
|
|
2784
|
+
trailing: "",
|
|
2785
|
+
slots: [{ position: 0, name: first.name, ts: correction.ts }],
|
|
2786
|
+
};
|
|
2787
|
+
}
|
|
2517
2788
|
const ts =
|
|
2518
2789
|
first.types.length > 0
|
|
2519
2790
|
? mapSlotUnion(
|
|
@@ -2528,7 +2799,7 @@ function emitReturn(
|
|
|
2528
2799
|
first.name,
|
|
2529
2800
|
)
|
|
2530
2801
|
: "unknown";
|
|
2531
|
-
return { type: ts, trailing: "" };
|
|
2802
|
+
return { type: ts, trailing: "", slots: [{ position: 0, name: first.name, ts }] };
|
|
2532
2803
|
}
|
|
2533
2804
|
|
|
2534
2805
|
function emitPropertyMember(
|
|
@@ -2606,7 +2877,14 @@ function mapSlotUnion(
|
|
|
2606
2877
|
const nested = mapping.value as NestedMapping;
|
|
2607
2878
|
value = `LuaMap<${mapType(nested.key)}, ${mapType(nested.value)}>`;
|
|
2608
2879
|
}
|
|
2609
|
-
|
|
2880
|
+
// A mapping key may be a `A | B` union token too (render.clear's three
|
|
2881
|
+
// documented graphics.BUFFER_TYPE_* constants); split it the same way
|
|
2882
|
+
// the value above is split. A single-token key (no `|`) is unaffected.
|
|
2883
|
+
const key = unionFromTokens(
|
|
2884
|
+
mapping.key.split("|").map((token) => token.trim()),
|
|
2885
|
+
mapType,
|
|
2886
|
+
);
|
|
2887
|
+
ts = `LuaMap<${key}, ${value}>`;
|
|
2610
2888
|
} else if (element !== undefined) {
|
|
2611
2889
|
ts = arrayTypeFromTokens(element, mapType);
|
|
2612
2890
|
} else if (curation?.kind === "object" || curation?.kind === "array-object") {
|
|
@@ -2617,19 +2895,25 @@ function mapSlotUnion(
|
|
|
2617
2895
|
// arbitrary name. Reusing the parser keeps the inner fields in lockstep
|
|
2618
2896
|
// with the doc instead of a hand-listed drift-prone copy; a documented
|
|
2619
2897
|
// array-form field is pinned via TABLE_FIELD_TYPE_OVERRIDES.
|
|
2620
|
-
const parsed =
|
|
2898
|
+
const parsed = applyFieldOptionalityCorrections(
|
|
2621
2899
|
elementName,
|
|
2622
2900
|
slotKind,
|
|
2623
2901
|
slotName,
|
|
2624
|
-
|
|
2902
|
+
applyFieldTypeOverrides(
|
|
2903
|
+
elementName,
|
|
2904
|
+
slotKind,
|
|
2905
|
+
slotName,
|
|
2906
|
+
parseTableFields(doc, resolver, slotName) ?? [],
|
|
2907
|
+
),
|
|
2625
2908
|
);
|
|
2626
2909
|
const object = inlineTableType(parsed, mapType, optionalFields);
|
|
2627
2910
|
ts = `Record<string, ${object}>`;
|
|
2628
2911
|
} else {
|
|
2629
|
-
const parsed = parseTableFields(doc, resolver);
|
|
2912
|
+
const parsed = parseTableFields(doc, resolver, slotName);
|
|
2630
2913
|
if (parsed !== null) {
|
|
2631
2914
|
const nested = applyNestedFieldCurations(elementName, slotKind, slotName, parsed);
|
|
2632
|
-
const
|
|
2915
|
+
const typed = applyFieldTypeOverrides(elementName, slotKind, slotName, nested);
|
|
2916
|
+
const fields = applyFieldOptionalityCorrections(elementName, slotKind, slotName, typed);
|
|
2633
2917
|
const object = inlineTableType(fields, mapType, optionalFields);
|
|
2634
2918
|
ts = isSlotLevelList(doc) ? `${object}[]` : object;
|
|
2635
2919
|
} else {
|